vue2-excel-grid 1.1.13 → 1.1.16
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.
- package/dist/vue2-excel-grid.esm.js +1633 -644
- package/dist/vue2-excel-grid.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/EgCheckbox.vue +96 -0
- package/src/components/EgSelect.vue +377 -0
- package/src/components/EgTreeSelect.vue +357 -0
- package/src/components/ExcelGrid.vue +88 -437
- package/src/index.js +7 -1
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* (c) 2026
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("xlsx")):"function"==typeof define&&define.amd?define(["exports","xlsx"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Vue2ExcelGrid={},e.XLSX)}(this,function(e,t){"use strict";function n(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var i=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,i.get?i:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var i=n(t);function o(e){return{startRow:Math.min(e.startRow,e.endRow),endRow:Math.max(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endCol:Math.max(e.startCol,e.endCol)}}function l(e,t,n){const i=o(n);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol}function s(e,t){return{startRow:e,endRow:e,startCol:t,endCol:t}}function r(e){const t=[];return function e(n){for(const i of n)i.children&&i.children.length?e(i.children):t.push({...i,_flatIndex:t.length})}(e),t}function a(e){return function e(t){let n=0;for(const i of t)n=i.children&&i.children.length?Math.max(n,1+e(i.children)):Math.max(n,1);return n}(e)}function c(e,t){const n=Array.from({length:t},()=>[]);let i=0;return function e(o,l){for(const s of o)if(s.children&&s.children.length){const t=d(s),o=i;n[l].push({column:s,colspan:t,rowspan:1,isGroup:!0,_firstLeafFlatIndex:o}),e(s.children,l+1)}else{const e=t-l;n[l].push({column:{...s,_flatIndex:i},colspan:1,rowspan:e,isGroup:!1,_firstLeafFlatIndex:i}),i++}}(e,0),n}function d(e){return e.children&&0!==e.children.length?e.children.reduce((e,t)=>e+d(t),0):1}function h(e,t,n=[]){let i=0;for(let o=0;o<e;o++)i+=void 0!==n[o]?n[o]:t;return i}const u=["Dushanba","Seshanba","Chorshanba","Payshanba","Juma","Shanba","Yakshanba"],p=["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],g=["January","February","March","April","May","June","July","August","September","October","November","December"],f=/^\d{4}[-/]\d{2}[-/]\d{2}$/;function A(e){return"string"==typeof e&&f.test(e.trim())}function C(e){if("boolean"==typeof e)return!1;const t=String(e).trim();return""!==t&&(!A(t)&&!isNaN(Number(t)))}function w(e,t){const n=function(e){if(0===e.length)return{type:"copy",step:0,base:null};if(1===e.length)return{type:"copy",step:0,base:e[0]};const t=e.map(e=>u.indexOf(e));if(t.every(e=>e>=0))return{type:"weekday_uz",step:e.length>=2&&(t[1]-t[0]+7)%7||1,base:t[0]};const n=e.map(e=>p.indexOf(e));if(n.every(e=>e>=0))return{type:"weekday_en",step:e.length>=2&&(n[1]-n[0]+7)%7||1,base:n[0]};const i=e.map(e=>g.indexOf(e));if(i.every(e=>e>=0))return{type:"month",step:e.length>=2&&(i[1]-i[0]+12)%12||1,base:i[0]};if(e.every(A)){const t=e.map(e=>new Date(e).getTime());if(t.every(e=>!isNaN(e))){const e=t.slice(1).map((e,n)=>e-t[n]);if(e.every(t=>Math.abs(t-e[0])<864e5))return{type:"date",step:e[0],base:t[0]}}}if(e.every(C)){const t=e.map(e=>Number(e)),n=t.slice(1).map((e,n)=>e-t[n]);if(n.every(e=>Math.abs(e-n[0])<1e-10))return{type:"number",step:n[0],base:t[0]}}return{type:"copy",step:0,base:e[0]}}(e),i=e.length,o=[];for(let l=0;l<t;l++){const t=i+l;switch(n.type){case"number":o.push(n.base+n.step*t);break;case"weekday_uz":o.push(u[(n.base+n.step*t)%7]);break;case"weekday_en":o.push(p[(n.base+n.step*t)%7]);break;case"month":o.push(g[(n.base+n.step*t)%12]);break;case"date":{const e=new Date(n.base+n.step*t);o.push(e.toISOString().split("T")[0]);break}default:o.push(e[t%i])}}return o}function m(e,t,n){const i=[];for(let o=n.startRow;o<=n.endRow;o++){const l=[];for(let i=n.startCol;i<=n.endCol;i++){const n=t[i],s=n&&e[o]?e[o][n.key]:"";l.push(void 0!==s?s:"")}i.push(l)}return i}function x(e,t,n,i,o,l="all",s=null,r=null){const a=e.slice(),c=Math.min(n+o.length-1,e.length-1),d=Math.min(i+(o[0]?o[0].length-1:0),t.length-1),h=[];for(let u=n;u<=c;u++){const c=o[u-n];if(!c)continue;let p=null;for(let n=i;n<=d;n++){const o=t[n];if(o&&((s?s(u,n):!1!==o.editable)&&("all"===l||"values"===l))){const t=c[n-i],l=void 0!==t?t:"";if(r&&!r(u,n,l)){h.push({row:u,col:n,value:l});continue}p||(p={...e[u]||{}},a[u]=p),p[o.key]=l}}}return{newData:a,affectedRange:{startRow:n,endRow:c,startCol:i,endCol:d},skipped:h}}function v(e,t){const n=o(e);return t.some(e=>function(e,t){const n=o(e),i=o(t);return!(n.endRow<i.startRow||n.startRow>i.endRow||n.endCol<i.startCol||n.startCol>i.endCol)}(n,o(e)))}function y(e,t,n){return n.find(n=>{const i=o(n);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol})||null}function b(e,t,n,i){if(!function(e,t){return Number.isInteger(e)&&Number.isInteger(t)&&e<=t&&e>=0}(t,n))return i;return i.find(i=>i.type===e&&i.startIndex===t&&i.endIndex===n)?i:[...i,{type:e,startIndex:t,endIndex:n}]}function R(e,t,n,i){return i.filter(i=>!(i.type===e&&i.startIndex===t&&i.endIndex===n))}function k(e,t){return t.some(t=>t.type===e.type&&t.startIndex===e.startIndex&&t.endIndex===e.endIndex)}function S(e){const t=[];for(const n of e||[])n.children&&n.children.length?t.push(...S(n.children)):n.key&&t.push(n);return t}function E(e,t,n,i,o){for(let l=t;l<=n;l++)for(let t=i;t<=o;t++)if(""!==((e[l]||[])[t]||"").toString().trim())return!0;return!1}function _(e,t,n){return String((e[t]||[])[n]||"").trim().replace(/\n/g," ")}function D(){return new Promise(e=>setTimeout(e,0))}async function M(e,t,n={},o){const l=(e,t)=>o&&o(e,t);l(5,"Fayl o'qilmoqda..."),await D();const s=await e.arrayBuffer();let r;l(20,"Excel tahlil qilinmoqda..."),await D();try{r=i.read(s,{type:"array",cellDates:!0})}catch(e){throw new Error("Excel fayl o'qib bo'lmadi: "+e.message)}const{sheetName:a}=n;let c;if(a){if(c=r.Sheets[a],!c)throw new Error(`Sheet "${a}" topilmadi`)}else{const e=r.SheetNames[0];if(!e)throw new Error("Workbook bo'sh");c=r.Sheets[e]}l(40,"Ma'lumotlar tayyorlanmoqda..."),await D();const d=i.utils.sheet_to_json(c,{header:1,raw:!1,defval:""});if(!d||0===d.length)throw new Error("Sheet bo'sh");const h=i.utils.decode_range(c["!ref"]||"A1:A1"),u=h.s.r,p=h.s.c,g=(c["!merges"]||[]).map(e=>({s:{r:e.s.r-u,c:e.s.c-p},e:{r:e.e.r-u,c:e.e.c-p}})),f=function(e,t){if(!e||0===e.length)return{headerRow:0,headerEndRow:0,dataStartRow:1};const n=e.map(e=>e.filter(e=>""!==e).length);t=t||[];let i,o,l,s=-1;for(let t=0;t<Math.min(e.length,20);t++){const n=e[t].filter(e=>""!==e);if(n.length<2)continue;const i=n.map(e=>{const t=parseInt(String(e).trim(),10);return isNaN(t)?null:t});if(!i.some(e=>null===e)&&i.every((e,t)=>e===t+1)){s=t;break}}if(s>=0){for(l=s+1,o=s-1;o>=0&&0===n[o];)o--;for(o<0&&(o=0),i=o;i>0&&n[i-1]>1;)i--}else{i=0;for(let t=0;t<Math.min(e.length,20);t++)if(n[t]>1){i=t;break}let s=1;for(const e of t)e.s.r===i&&e.e.r>e.s.r&&(s=Math.max(s,e.e.r-e.s.r+1));o=i+s-1,l=o+1}for(;l<e.length&&0===n[l];)l++;return{headerRow:i,headerEndRow:o,dataStartRow:l}}(d,g),A=null!=n.headerRow?n.headerRow:f.headerRow,C=null!=n.headerEndRow?n.headerEndRow:f.headerEndRow,w=null!=n.dataStartRow?n.dataStartRow:f.dataStartRow,m=S(t),x=new Set,{columns:v,colMap:y}=function(e,t,n,i,o,l){let s=-1;for(let i=t;i<=n;i++)for(let t=(e[i]||[]).length-1;t>=0;t--)if(""!==((e[i]||[])[t]||"")){s=Math.max(s,t);break}const r=(i||[]).filter(e=>e.s.r>=t&&e.s.r<=n);for(const e of r)s=Math.max(s,e.e.c);if(s<0)return{columns:[],colMap:[]};const a=[];function c(e,t){const n=o.find(t=>t.title&&t.title.toLowerCase()===e.toLowerCase());if(n)return{key:n.key,title:n.title,width:n.width||100};let i=String(e||"").toLowerCase().replace(/\s+/g,"_").replace(/[^a-z0-9_]/g,"").replace(/^_+|_+$/g,"").replace(/_+/g,"_").replace(/^(\d)/,"col_$1")||`col_${t}`,s=i,r=1;for(;l.has(s)||o.some(e=>e.key===s);)s=`${i}_${r++}`;return l.add(s),{key:s,title:e||s,width:100}}const d=function t(n,i,o,l){if(n>i||o>l)return[];const s=[];let d=o;for(;d<=l;){if(r.some(e=>e.s.r<=n&&e.e.r>=n&&e.s.c<d&&e.e.c>=d)){d++;continue}const o=r.find(e=>e.s.r===n&&e.s.c===d),h=o?_(e,o.s.r,o.s.c):_(e,n,d);if(o&&o.e.c>d){const n=Math.min(o.e.c,l),r=o.e.r+1;if(r<=i&&E(e,r,i,d,n)){const e=t(r,i,d,n);s.push({title:h,children:e})}else for(let e=d;e<=n;e++){const t=c(h,e);s.push(t),a.push({key:t.key,colIndex:e})}d=n+1}else if(o&&o.e.r>=i){const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d}),d++}else if(o){const n=o.e.r+1;if(n<=i&&E(e,n,i,d,d)){const e=t(n,i,d,d);s.push({title:h,children:e})}else{const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d})}d++}else if(n===i){const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d}),d++}else{if(E(e,n+1,i,d,d)){const e=t(n+1,i,d,d);s.push({title:h,children:e})}else{const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d})}d++}}return s}(t,n,0,s);return{columns:d,colMap:a}}(d,A,C,g,m,x),b=Math.max(d.length-w,0);l(55,`Qatorlar qayta ishlanmoqda (0 / ${b})...`),await D();const R=[];for(let e=w;e<d.length;e+=2e3){const t=Math.min(e+2e3,d.length);for(let n=e;n<t;n++){const e=d[n],t={};for(const{key:n,colIndex:i}of y)t[n]=void 0!==e[i]?e[i]:"";R.push(t)}const n=Math.min(t-w,b);l(55+Math.round(40*n/Math.max(b,1)),`Qatorlar: ${n} / ${b}`),await D()}const k=g.filter(e=>e.s.r>=w).map(e=>({startRow:e.s.r-w,startCol:e.s.c,endRow:e.e.r-w,endCol:e.e.c})).filter(e=>e.startRow>=0&&e.startCol>=0);return l(100,"Tayyor!"),{data:R,columns:v,merges:k}}const z={en:{copy:"Copy",cut:"Cut",paste:"Paste",pasteSpecial:"Paste Special",pasteAll:"All (Value + Format)",pasteValues:"Values Only",pasteFormats:"Formats Only",transpose:"Transpose",mergeCells:"Merge Cells",unmergeCells:"Unmerge Cells",hideRows:"Hide Row(s)",hideColumns:"Hide Column(s)",unhideRows:"Unhide All Rows",unhideColumns:"Unhide All Columns",pinLeft:"Pin Left",pinRight:"Pin Right",unpin:"Unpin",invalidValue:"Invalid value",total:"Total"},ru:{copy:"Копировать",cut:"Вырезать",paste:"Вставить",pasteSpecial:"Специальная вставка",pasteAll:"Всё (значение + формат)",pasteValues:"Только значения",pasteFormats:"Только форматы",transpose:"Транспонировать",mergeCells:"Объединить ячейки",unmergeCells:"Разъединить ячейки",hideRows:"Скрыть строку(и)",hideColumns:"Скрыть столбец(цы)",unhideRows:"Показать все строки",unhideColumns:"Показать все столбцы",pinLeft:"Закрепить слева",pinRight:"Закрепить справа",unpin:"Открепить",invalidValue:"Недопустимое значение",total:"Итого"},uz:{copy:"Nusxalash",cut:"Kesib olish",paste:"Joylashtirish",pasteSpecial:"Maxsus joylashtirish",pasteAll:"Hammasi (qiymat + format)",pasteValues:"Faqat qiymatlar",pasteFormats:"Faqat formatlar",transpose:"Transpozitsiya",mergeCells:"Katakchalarni birlashtirish",unmergeCells:"Katakchalarni ajratish",hideRows:"Qatorlarni yashirish",hideColumns:"Ustunlarni yashirish",unhideRows:"Barcha qatorlarni ko'rsatish",unhideColumns:"Barcha ustunlarni ko'rsatish",pinLeft:"Chapga pin qilish",pinRight:"O'ngga pin qilish",unpin:"Pindan olish",invalidValue:"Noto'g'ri qiymat",total:"Jami"},"uz-Cyrl":{copy:"Нусхалаш",cut:"Кесиб олиш",paste:"Жойлаштириш",pasteSpecial:"Махсус жойлаштириш",pasteAll:"Ҳаммаси (қиймат + формат)",pasteValues:"Фақат қийматлар",pasteFormats:"Фақат форматлар",transpose:"Транспозиция",mergeCells:"Катакчаларни бирлаштириш",unmergeCells:"Катакчаларни ажратиш",hideRows:"Қаторларни яшириш",hideColumns:"Устунларни яшириш",unhideRows:"Барча қаторларни кўрсатиш",unhideColumns:"Барча устунларни кўрсатиш",pinLeft:"Чапга пин қилиш",pinRight:"Ўнгга пин қилиш",unpin:"Пиндан олиш",invalidValue:"Нотўғри қиймат",total:"Жами"}};const H={small:{row:22,header:24,footer:24},middle:{row:28,header:28,footer:28},large:{row:36,header:36,footer:36}},I=50,V=20;var B={name:"ExcelGrid",model:{prop:"data",event:"input"},props:{data:{type:Array,default:()=>[]},columns:{type:Array,default:()=>[]},hiddenColumns:{type:Array,default:()=>[]},hiddenRows:{type:Array,default:()=>[]},merges:{type:Array,default:()=>[]},collapsedGroups:{type:Array,default:()=>[]},height:{type:Number,default:400},width:{type:[Number,String],default:"100%"},beforeDrop:{type:Function,default:null},locale:{type:String,default:"en"},pinnedColumns:{type:Object,default:()=>({})},cellEditable:{type:Function,default:null},cellValidator:{type:Function,default:null},cellValidationMessage:{type:[Function,String],default:null},strictBulkValidation:{type:Boolean,default:!1},size:{type:String,default:"middle"},transitionEnabled:{type:Boolean,default:!1},autoWidth:{type:Boolean,default:!1}},data:()=>({activeCell:{row:0,col:0},selectionRanges:[],isMouseDown:!1,mouseDownRange:null,isCtrlDrag:!1,editingCell:null,editValue:"",editOriginalValue:"",scrollTop:0,scrollLeft:0,headerViewportWidth:null,virtualStartIndex:0,virtualEndIndex:0,virtualOffsetTop:0,totalVirtualHeight:0,colWidths:{},rowHeights:{},importState:{active:!1,percent:0,message:""},exportState:{active:!1},_isScrolling:!1,resizing:{active:!1,type:null,index:-1,startX:0,startY:0,startSize:0,currentPos:0},fillDrag:{active:!1,sourceRange:null,currentRow:-1,currentCol:-1},dragMove:{active:!1,sourceRange:null,startX:0,startY:0,targetRow:-1,targetCol:-1,copyMode:!1},clipboardMatrix:null,clipboardRange:null,clipboardMode:null,contextMenu:{visible:!1,x:0,y:0,row:-1,col:-1},selectDropdown:null,invalidCell:null,internalMerges:[],internalGroups:[],internalCollapsedGroups:[],headerDepth:1,headerRows:[],leafCols:[],columnTotals:{}}),computed:{t(){return e=this.locale,z[e]||z.en;var e},pinnedLeftSet(){const e=new Set;return this.leafCols.forEach(t=>{"left"===this.pinnedColumns[t.key]&&e.add(t._flatIndex)}),e},pinnedRightSet(){const e=new Set;return this.leafCols.forEach(t=>{"right"===this.pinnedColumns[t.key]&&e.add(t._flatIndex)}),e},pinnedLeftCols(){return this.leafCols.filter(e=>this.pinnedLeftSet.has(e._flatIndex))},pinnedRightCols(){return this.leafCols.filter(e=>this.pinnedRightSet.has(e._flatIndex))},hasPinnedLeft(){return this.pinnedLeftSet.size>0},hasPinnedRight(){return this.pinnedRightSet.size>0},hasFooter(){return this.leafCols.some(e=>e.showTotal)},pinnedLeftWidth(){let e=0;for(const t of this.pinnedLeftCols)this.isColHidden(t._flatIndex)||(e+=this.getColW(t._flatIndex));return e},pinnedRightWidth(){let e=0;for(const t of this.pinnedRightCols)this.isColHidden(t._flatIndex)||(e+=this.getColW(t._flatIndex));return e},totalRows(){return this.data.length},_rowTransitionName(){return this.transitionEnabled&&!this._isScrolling?"eg-row":""},_sizeConf(){return H[this.size]||H.middle},_rowH(){return this._sizeConf.row},_headerRowH(){return this._sizeConf.header},_footerRowH(){return this._sizeConf.footer},headerRowHeight(){return this._headerRowH},footerRowHeight(){return this._footerRowH},cumulativeHeights(){return function(e,t,n={},i=new Set){const o=new Array(e+1);o[0]=0;for(let l=0;l<e;l++){const e=i.has(l)?0:void 0!==n[l]?n[l]:t;o[l+1]=o[l]+e}return o}(this.totalRows,this._rowH,this.rowHeights,this.hiddenRowSet)},hiddenColSet(){return new Set(this.hiddenColumns)},hiddenRowSet(){const e=new Set(this.hiddenRows);return function(e,t){const n=new Set;for(const i of e)if("row"===i.type&&k(i,t))for(let e=i.startIndex;e<=i.endIndex;e++)n.add(e);return n}(this.internalGroups,this.internalCollapsedGroups).forEach(t=>e.add(t)),e},hiddenColFromGroupSet(){return function(e,t){const n=new Set;for(const i of e)if("column"===i.type&&k(i,t))for(let e=i.startIndex;e<=i.endIndex;e++)n.add(e);return n}(this.internalGroups,this.internalCollapsedGroups)},effectiveHiddenColSet(){const e=new Set(this.hiddenColSet);return this.hiddenColFromGroupSet.forEach(t=>e.add(t)),e},rowGroups(){return this.internalGroups.filter(e=>"row"===e.type)},colGroups(){return this.internalGroups.filter(e=>"column"===e.type)},visibleRowRange(){const e=[];for(let t=this.virtualStartIndex;t<=this.virtualEndIndex;t++)e.push(t);return e},primaryRange(){return function(e,t){if(!e||0===e.length)return null;return o(e.find(e=>l(t.row,t.col,e))||e[0])}(this.selectionRanges,this.activeCell)},fillHandleVisible(){return this.primaryRange&&!this.editingCell},fillHandleStyle(){if(!this.primaryRange)return{};const e=o(this.primaryRange),t=this.cumulativeHeights[e.endRow+1]||0,n=this.getColLeft(e.endCol+1);return{position:"absolute",width:"6px",height:"6px",background:"#1a73e8",border:"1px solid white",bottom:this.totalVirtualHeight-t+1+"px",right:this.scrollContentWidth-n+1+"px",cursor:"crosshair",zIndex:10}},filteredSelectOptions(){if(!this.selectDropdown)return[];const{options:e,searchQuery:t,enableSearch:n,tree:i,allFlatOptions:o}=this.selectDropdown;if(!n||!t||!t.trim())return e;const l=t.trim().toLowerCase();return(i&&o?o:e).filter(e=>String(e.label||"").toLowerCase().includes(l))},dropdownStyle(){if(!this.selectDropdown)return{};const{rect:e}=this.selectDropdown,t=this._rowH+4,n=this.selectDropdown.enableSearch?42:0,i=Math.min(this.filteredSelectOptions.length*t+8+n,320);return{position:"fixed",top:(window.innerHeight-e.bottom>=i?e.bottom:e.top-i)+"px",left:e.left+"px",minWidth:Math.max(e.width,120)+"px",maxWidth:Math.min(360,window.innerWidth-e.left-16)+"px",zIndex:9999}},invalidTooltipStyle(){if(!this.invalidCell||!this.invalidCell.rect)return{};const{rect:e}=this.invalidCell;return{position:"fixed",top:e.bottom+4+"px",left:e.left+"px",zIndex:9999}},dragGhostStyle(){if(!this.dragMove.active||this.dragMove.targetRow<0)return{};const e=o(this.dragMove.sourceRange),t=e.endRow-e.startRow,n=e.endCol-e.startCol;return{position:"absolute",top:(this.cumulativeHeights[this.dragMove.targetRow]||0)+"px",left:this.getColLeft(this.dragMove.targetCol)+"px",width:this.getRangeWidth(this.dragMove.targetCol,this.dragMove.targetCol+n)+"px",height:this.getRangeHeight(this.dragMove.targetRow,this.dragMove.targetRow+t)+"px",border:"2px dashed #1a73e8",pointerEvents:"none",zIndex:20}},wrapperStyle(){return{width:"number"==typeof this.width?this.width+"px":this.width,height:this.height+"px",position:"relative",outline:"none",userSelect:"none"}},headerAreaStyle:()=>({display:"flex",flexDirection:"row",position:"sticky",top:0,zIndex:30,background:"#f8f9fa"}),cornerStyle(){const e=this.rowGroups.length?V:0;return{width:I+e+"px",minWidth:I+e+"px",height:this._headerRowH*this.headerDepth+"px",background:"#f0f0f0",borderRight:"1px solid #ccc",borderBottom:"1px solid #ccc",boxSizing:"border-box",flexShrink:0}},rowOutlineGutterStyle:()=>({width:"20px",flexShrink:0}),frozenColHeaderStyle(){const e=this.getFrozenWidth();return{width:e+"px",minWidth:e+"px",overflow:"hidden",flexShrink:0}},scrollColHeaderStyle(){return null!=this.headerViewportWidth?{flex:"0 0 "+this.headerViewportWidth+"px",overflow:"hidden"}:{flex:1,overflow:"hidden"}},bodyAreaStyle:()=>({display:"flex",flexDirection:"row",flex:1,position:"relative"}),rowHeaderColStyle:()=>({width:"50px",minWidth:"50px",flexShrink:0,display:"flex",flexDirection:"column",position:"sticky",left:0,zIndex:20,background:"#f0f0f0"}),rowOutlineColStyle:()=>({width:"20px",minWidth:"20px",flexShrink:0,position:"relative",background:"#fafafa"}),scrollBodyStyle:()=>({flex:1,overflow:"auto",position:"relative"}),frozenColBodyStyle(){return{position:"sticky",left:0,width:this.getFrozenWidth()+"px",zIndex:10,background:"white",overflow:"hidden"}},mainCellsStyle(){return{position:"absolute",left:this.getFrozenWidth()+"px",top:0,bottom:0}},scrollContentWidth(){let e=0;for(let t=0;t<this.leafCols.length;t++)this.isColHidden(t)||this.pinnedRightSet.has(t)||(e+=this.getColW(t));return this.hasPinnedRight&&(e+=this.pinnedRightWidth),e},scrollableContentWidth(){let e=0;for(let t=0;t<this.leafCols.length;t++)!this.isColHidden(t)&&this.isColumnScrollable(t)&&(e+=this.getColW(t));return e},pinnedRightBodyStyle(){return{position:"sticky",right:0,width:this.pinnedRightWidth+"px",zIndex:10,background:"white",flexShrink:0}},pinnedRightHeaderStyle(){return{width:this.pinnedRightWidth+"px",minWidth:this.pinnedRightWidth+"px",overflow:"hidden",flexShrink:0,borderLeft:"1px solid #ccc"}},scrollRowHeadersStyle:()=>({flex:1,overflow:"hidden",position:"relative"}),footerAreaStyle:()=>({display:"flex",flexDirection:"row",flexShrink:0,position:"sticky",bottom:0,zIndex:30,background:"#f8f9fa",boxShadow:"0 -2px 0 0 #d0d0d0"}),footerLabelStyle(){const e=this.rowGroups.length?V:0;return{width:I+e+"px",minWidth:I+e+"px",height:this._footerRowH+"px",display:"flex",alignItems:"center",justifyContent:"center",background:"#f0f0f0",borderRight:"1px solid #ccc",borderTop:"1px solid #ccc",boxSizing:"border-box",flexShrink:0,fontSize:"12px",fontWeight:600}},frozenColFooterStyle(){const e=this.getFrozenWidth();return{width:e+"px",minWidth:e+"px",height:this._footerRowH+"px",overflow:"hidden",flexShrink:0}},pinnedRightFooterStyle(){return{width:this.pinnedRightWidth+"px",minWidth:this.pinnedRightWidth+"px",height:this._footerRowH+"px",overflow:"hidden",flexShrink:0,borderLeft:"1px solid #ccc"}},colOutlineRowStyle:()=>({position:"absolute",top:"-20px",left:0,right:0,height:"20px"}),resizeLineStyle(){return this.resizing.active?"col"===this.resizing.type?{position:"fixed",top:0,left:this.resizing.currentPos+"px",width:"2px",height:"100%",background:"#1a73e8",pointerEvents:"none",zIndex:9999}:{position:"fixed",left:0,top:this.resizing.currentPos+"px",height:"2px",width:"100%",background:"#1a73e8",pointerEvents:"none",zIndex:9999}:{}}},watch:{size(){this.rowHeights={},this.$nextTick(()=>this.updateVirtualScroll())},autoWidth(e){e&&this._applyAutoWidth()},columns:{immediate:!0,handler(e){this.leafCols=r(e),this.headerDepth=a(e),this.headerRows=c(e,this.headerDepth),this.leafCols.forEach(e=>{void 0===this.colWidths[e._flatIndex]&&(this.colWidths[e._flatIndex]=e.width||100)}),this.computeColumnTotals(),this.updateVirtualScroll(),this._applyAutoWidth()}},merges:{immediate:!0,handler(e){this.internalMerges=e?[...e]:[]}},collapsedGroups:{immediate:!0,handler(e){this.internalCollapsedGroups=e?[...e]:[]}},data:{immediate:!0,handler(e){this._dataRaw=e||[],this.updateVirtualScroll(),this.computeColumnTotals(),this.$forceUpdate(),this._applyAutoWidth()}},totalRows(){this.updateVirtualScroll()},hiddenRowSet(){this.updateVirtualScroll()},totalVirtualHeight(){this.$nextTick(this.syncHeaderViewportWidth)}},created(){this._dataRaw=this.data||[],this._cellStyleCache=Object.create(null),this._rowStyleCache=Object.create(null),this._rowHeaderStyleCache=Object.create(null),this._scrollRAF=null,this._resizeRAF=null,this._history=[],this._historyIndex=-1,this._historySkip=!1},mounted(){this.updateVirtualScroll(),this.$nextTick(this.syncHeaderViewportWidth),document.addEventListener("mousemove",this.onGlobalMouseMove),document.addEventListener("mouseup",this.onGlobalMouseUp),document.addEventListener("keydown",this.onGlobalKeyDown),document.addEventListener("mousedown",this._onDocMouseDownForDropdown,!0),window.addEventListener("resize",this.syncHeaderViewportWidth),window.addEventListener("scroll",this._onWindowScrollForDropdown,!0)},beforeDestroy(){document.removeEventListener("mousemove",this.onGlobalMouseMove),document.removeEventListener("mouseup",this.onGlobalMouseUp),document.removeEventListener("keydown",this.onGlobalKeyDown),document.removeEventListener("mousedown",this._onDocMouseDownForDropdown,!0),window.removeEventListener("resize",this.syncHeaderViewportWidth),window.removeEventListener("scroll",this._onWindowScrollForDropdown,!0),clearTimeout(this._scrollEndTimer)},methods:{updateVirtualScroll(){const e=this.hasFooter?this._footerRowH:0,t=this.height-this._headerRowH*this.headerDepth-e,n=function(e,t,n,i,o=5){if(0===n)return{startIndex:0,endIndex:-1,offsetTop:0,totalHeight:0};const l=i[n],s=e+t;let r=0,a=n-1;for(;r<a;){const t=r+a>>1;i[t+1]<=e?r=t+1:a=t}let c=r,d=c;for(;d<n-1&&i[d+1]<s;)d++;return c=Math.max(0,c-o),d=Math.min(n-1,d+o),{startIndex:c,endIndex:d,offsetTop:i[c],totalHeight:l}}(this.scrollTop,Math.max(t,100),this.totalRows,this.cumulativeHeights);this.virtualStartIndex=n.startIndex,this.virtualEndIndex=n.endIndex,this.virtualOffsetTop=n.offsetTop,this.totalVirtualHeight=n.totalHeight},onBodyScroll(e){const t=e.target;this.$refs.scrollColHeader&&(this.$refs.scrollColHeader.scrollLeft=t.scrollLeft),this.$refs.scrollFooter&&(this.$refs.scrollFooter.scrollLeft=t.scrollLeft),this.$refs.scrollRowHeaders&&(this.$refs.scrollRowHeaders.scrollTop=t.scrollTop),this.selectDropdown&&this.closeSelectDropdown(),this.transitionEnabled&&(this._isScrolling=!0,clearTimeout(this._scrollEndTimer),this._scrollEndTimer=setTimeout(()=>{this._isScrolling=!1},150)),this._scrollRAF||(this._scrollRAF=requestAnimationFrame(()=>{this._scrollRAF=null,this.scrollTop=t.scrollTop,this.scrollLeft=t.scrollLeft,this.updateVirtualScroll()}))},onScroll(e){},syncHeaderViewportWidth(){const e=this.$refs.scrollBody;e&&this.headerViewportWidth!==e.clientWidth&&(this.headerViewportWidth=e.clientWidth)},isRowHidden(e){return this.hiddenRowSet.has(e)},isColHidden(e){return this.effectiveHiddenColSet.has(e)},getColW(e){return void 0!==this.colWidths[e]?this.colWidths[e]:this.leafCols[e]&&this.leafCols[e].width||100},getRowH(e){return this.isRowHidden(e)?0:this.rowHeights[e]||this._rowH},getColLeft(e){let t=0;for(let n=0;n<e&&n<this.leafCols.length;n++)this.isColHidden(n)||(t+=this.getColW(n));return t},getFrozenWidth(){return this.hasPinnedLeft?this.pinnedLeftWidth:0},isColumnPinnedLeft(e){return this.pinnedLeftSet.has(e)},isColumnPinnedRight(e){return this.pinnedRightSet.has(e)},isColumnScrollable(e){return!this.isColumnPinnedLeft(e)&&!this.isColumnPinnedRight(e)},getZoneColLeft(e,t){let n=0;if("left"===t)for(const t of this.pinnedLeftCols){if(t._flatIndex===e)break;this.isColHidden(t._flatIndex)||(n+=this.getColW(t._flatIndex))}else if("right"===t)for(const t of this.pinnedRightCols){if(t._flatIndex===e)break;this.isColHidden(t._flatIndex)||(n+=this.getColW(t._flatIndex))}else for(let t=0;t<this.leafCols.length;t++){const i=this.leafCols[t];if(i._flatIndex===e)break;!this.isColHidden(i._flatIndex)&&this.isColumnScrollable(i._flatIndex)&&(n+=this.getColW(i._flatIndex))}return n},isCellInZone(e,t,n){if(e.isGroup){const t=e._firstLeafFlatIndex,i=t+e.colspan-1;for(let e=t;e<=i;e++){if(this.isColHidden(e))continue;if((this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll")===n)return!0}return!1}const i=e.column._flatIndex;if(this.isColHidden(i))return!1;return(this.isColumnPinnedLeft(i)?"left":this.isColumnPinnedRight(i)?"right":"scroll")===n},getHeaderCellStyleForZone(e,t,n){let i=e._firstLeafFlatIndex;if(e.isGroup){const t=i+e.colspan-1;for(let e=i;e<=t;e++){if(this.isColHidden(e))continue;if((this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll")===n){i=e;break}}}const o=this.getZoneColLeft(i,n);let l=0;if(e.isGroup){const t=e._firstLeafFlatIndex,i=t+e.colspan-1;for(let e=t;e<=i;e++){if(this.isColHidden(e))continue;(this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll")===n&&(l+=this.getColW(e))}}else l=this.getColW(e.column._flatIndex);return{position:"absolute",top:this._headerRowH*t+"px",left:o+"px",width:l+"px",height:this._headerRowH*e.rowspan+"px",display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",borderRight:"1px solid #ccc",borderBottom:"1px solid #ccc",background:"#f0f0f0",fontSize:"12px",fontWeight:600,overflow:"hidden",whiteSpace:"nowrap",userSelect:"none",cursor:"pointer"}},getRangeWidth(e,t){let n=0;for(let i=e;i<=t&&i<this.leafCols.length;i++)this.isColHidden(i)||(n+=this.getColW(i));return n},getRangeHeight(e,t){let n=0;for(let i=e;i<=t&&i<this.totalRows;i++)this.isRowHidden(i)||(n+=this.getRowH(i));return n},getCellValue(e,t){const n=this.leafCols[t];if(!n)return"";const i=this._dataRaw[e];if(!i)return"";const o=i[n.key];return void 0!==o?o:""},isEditing(e,t){return this.editingCell&&this.editingCell.row===e&&this.editingCell.col===t},isMergedAway(e,t){const n=y(e,t,this.internalMerges);if(!n)return!1;const i=o(n);return!(i.startRow===e&&i.startCol===t)},getMergeSpan(e,t){const n=y(e,t,this.internalMerges);if(!n)return{rowspan:1,colspan:1};const i=o(n);return{rowspan:i.endRow-i.startRow+1,colspan:i.endCol-i.startCol+1}},isSelected(e,t){return n=e,i=t,this.selectionRanges.some(e=>l(n,i,e));var n,i},isActiveCell(e,t){return this.activeCell.row===e&&this.activeCell.col===t},isCopyHighlighted(e,t){return!(!this.clipboardRange||"copy"!==this.clipboardMode)&&l(e,t,this.clipboardRange)},getRowStyle(e){const t=this.getRowH(e),n=t;if(this._rowStyleCache[n])return this._rowStyleCache[n];const i={height:t+"px",minHeight:t+"px"};return this._rowStyleCache[n]=i,i},getRowHeaderStyle(e){const t=this.getRowH(e),n=t;if(this._rowHeaderStyleCache[n])return this._rowHeaderStyleCache[n];const i={height:t+"px"};return this._rowHeaderStyleCache[n]=i,i},getCellStyle(e,t){const n=y(e,t,this.internalMerges),i=n?o(n):null;let l=this.getColW(t),s=this.getRowH(e);i&&i.startRow===e&&i.startCol===t&&(l=this.getRangeWidth(i.startCol,i.endCol),s=this.getRangeHeight(i.startRow,i.endRow));const r=this.isColumnPinnedLeft(t)?"left":this.isColumnPinnedRight(t)?"right":"scroll",a=this.getZoneColLeft(t,r),c=l+"_"+s+"_"+a;if(this._cellStyleCache[c])return this._cellStyleCache[c];const d={position:"absolute",left:a+"px",top:"0px",width:l+"px",minWidth:l+"px",height:s+"px"};return this._cellStyleCache[c]=d,d},getCellClasses(e,t){const n=this.leafCols[t];return{"eg-cell-active":this.isActiveCell(e,t),"eg-cell-selected":this.isSelected(e,t)&&!this.isActiveCell(e,t),"eg-cell-fill-preview":this.isInFillPreview(e,t),"eg-cell-copy":this.isCopyHighlighted(e,t),"eg-cell-merged-hidden":this.isMergedAway(e,t),"eg-cell-select":!(!n||!this.isSelectLikeCol(n)),"eg-cell-checkbox":!(!n||"checkbox"!==n.type),"eg-cell-readonly":!this.isCellEditable(e,t),"eg-cell-invalid":!(!this.invalidCell||this.invalidCell.row!==e||this.invalidCell.col!==t)}},isInFillPreview(e,t){if(!this.fillDrag.active||!this.fillDrag.sourceRange)return!1;const n=this.fillDrag.sourceRange,i=this.fillDrag.currentRow,o=this.fillDrag.currentCol;return i>n.endRow?e>n.endRow&&e<=i&&t>=n.startCol&&t<=n.endCol:o>n.endCol&&(t>n.endCol&&t<=o&&e>=n.startRow&&e<=n.endRow)},getHeaderCellStyle(e,t,n){const i=n?"left":"scroll";let o=e._firstLeafFlatIndex;if(e.isGroup){const t=o+e.colspan-1;for(let e=o;e<=t;e++)if(!this.isColHidden(e)&&(n?this.isColumnPinnedLeft(e):this.isColumnScrollable(e))){o=e;break}}const l=this.getZoneColLeft(o,i);let s=0;if(e.isGroup){const t=e._firstLeafFlatIndex,i=t+e.colspan-1;for(let e=t;e<=i;e++){if(this.isColHidden(e))continue;(n?this.isColumnPinnedLeft(e):this.isColumnScrollable(e))&&(s+=this.getColW(e))}}else s=this.getColW(e.column._flatIndex);return{position:"absolute",top:this._headerRowH*t+"px",left:l+"px",width:s+"px",height:this._headerRowH*e.rowspan+"px",display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",borderRight:"1px solid #ccc",borderBottom:"1px solid #ccc",background:"#f0f0f0",fontSize:"12px",fontWeight:600,overflow:"hidden",whiteSpace:"nowrap",userSelect:"none",cursor:"pointer"}},computeColumnTotals(){const e={},t=this._dataRaw||[];this.leafCols.forEach(n=>{if(!n.showTotal)return;let i=0,o=!1;for(let e=0;e<t.length;e++){const l=t[e],s=l?l[n.key]:void 0;"number"==typeof s&&isFinite(s)&&(i+=s,o=!0)}e[n._flatIndex]=o?Math.round(1e6*i)/1e6:null}),this.columnTotals=e},getColumnTotalDisplay(e){const t=this.leafCols[e];if(!t||!t.showTotal)return"";const n=this.columnTotals[e];return null==n?"-":n},getFooterCellStyle(e){const t=this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll",n=this.getZoneColLeft(e,t),i=this.getColW(e);return{position:"absolute",left:n+"px",top:"0px",width:i+"px",minWidth:i+"px",height:this._footerRowH+"px"}},isCellVisible(e,t,n){if(e.isGroup){const t=e._firstLeafFlatIndex,i=t+e.colspan-1;for(let e=t;e<=i;e++){if(this.isColHidden(e))continue;if(n?this.isColumnPinnedLeft(e):this.isColumnScrollable(e))return!0}return!1}const i=e.column._flatIndex;return!this.isColHidden(i)&&(n?this.isColumnPinnedLeft(i):this.isColumnScrollable(i))},getRowGroupBtnStyle(e){return{position:"absolute",top:h(e.startIndex,this._rowH,this.rowHeights)-this.scrollTop+"px",left:"2px",width:"16px",height:"16px",lineHeight:"16px",textAlign:"center",fontSize:"10px",cursor:"pointer",background:"#e8eaf6",border:"1px solid #9fa8da",borderRadius:"2px",zIndex:5}},getColGroupBtnStyle(e){return{position:"absolute",left:this.getColLeft(e.startIndex)-this.scrollLeft+"px",top:"2px",width:"16px",height:"16px",lineHeight:"16px",textAlign:"center",fontSize:"10px",cursor:"pointer",background:"#e8eaf6",border:"1px solid #9fa8da",borderRadius:"2px"}},selectCell(e,t,n=!1){e<0||e>=this.totalRows||t<0||t>=this.leafCols.length||(this.activeCell={row:e,col:t},n?this.selectionRanges.push(s(e,t)):this.selectionRanges=[s(e,t)],this.emitSelectionChange())},selectRange(e,t,n,i,o=!0){const l={startRow:e,startCol:t,endRow:n,endCol:i};o?this.selectionRanges=[l]:this.selectionRanges.push(l),this.emitSelectionChange()},selectRow(e){this.activeCell={row:e,col:0},this.selectionRanges=[{startRow:e,endRow:e,startCol:0,endCol:this.leafCols.length-1}],this.emitSelectionChange()},selectColumn(e){this.activeCell={row:0,col:e},this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:e,endCol:e}],this.emitSelectionChange()},selectAll(){this.activeCell={row:0,col:0},this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:0,endCol:this.leafCols.length-1}],this.emitSelectionChange()},emitSelectionChange(){this.$emit("selection-change",{activeCell:{...this.activeCell},ranges:this.selectionRanges.map(e=>o(e))})},onCellMouseDown(e,t,n){if(this.invalidCell=null,this.editingCell){const e=this.leafCols[this.editingCell.col];e&&this.hasEditSlot(e)?this.cancelEdit():this.commitEdit()}if(this.hideContextMenu(),this.$refs.wrapper&&this.$refs.wrapper.focus(),e.ctrlKey||e.metaKey)this.selectDropdown&&this.closeSelectDropdown(),this.activeCell={row:t,col:n},this.mouseDownRange={startRow:t,startCol:n,endRow:t,endCol:n},this.selectionRanges.push(this.mouseDownRange),this.isCtrlDrag=!0;else if(e.shiftKey&&this.selectionRanges.length>0)this.selectDropdown&&this.closeSelectDropdown(),this.selectionRanges=[(i=this.activeCell,o={row:t,col:n},{startRow:i.row,startCol:i.col,endRow:o.row,endCol:o.col})];else{const i=this.activeCell.row===t&&this.activeCell.col===n;this.activeCell={row:t,col:n},this.mouseDownRange={startRow:t,startCol:n,endRow:t,endCol:n},this.selectionRanges=[this.mouseDownRange],this.isCtrlDrag=!1;const o=this.leafCols[n];if(i&&o&&this.isSelectLikeCol(o)&&!this.hasEditSlot(o)&&this.isCellEditable(t,n))return this.openSelectDropdown(t,n,e),e.preventDefault(),this.isMouseDown=!0,void this.emitSelectionChange();this.selectDropdown&&this.closeSelectDropdown()}var i,o;this.isMouseDown=!0,this.emitSelectionChange(),e.preventDefault()},onCellMouseOver(e,t,n){if(this.isMouseDown&&!this.fillDrag.active)return this.dragMove.active?(this.dragMove.targetRow=t,void(this.dragMove.targetCol=n)):void(this.mouseDownRange&&(this.mouseDownRange.endRow=t,this.mouseDownRange.endCol=n,this.emitSelectionChange()))},onCellMouseUp(){this.isMouseDown=!1},onCellDblClick(e,t){const n=this.leafCols[t];this.isCellEditable(e,t)&&(n&&this.isSelectLikeCol(n)&&!this.hasEditSlot(n)?this.openSelectDropdownForActive():n&&"checkbox"===n.type||this.startEditing(e,t,null,!0))},onRowHeaderMouseDown(e,t){const n=this.leafCols.length-1,i={startRow:t,endRow:t,startCol:0,endCol:n};if(e.ctrlKey||e.metaKey)if(e.shiftKey&&this.selectionRanges.length>0){const e=Math.min(this.activeCell.row,t),i=Math.max(this.activeCell.row,t);this.selectionRanges=[...this.selectionRanges,{startRow:e,endRow:i,startCol:0,endCol:n}]}else{const e=this.selectionRanges.findIndex(e=>e.startRow===t&&e.endRow===t&&0===e.startCol&&e.endCol===n);if(e>=0){const t=this.selectionRanges.filter((t,n)=>n!==e);this.selectionRanges=t.length?t:[i]}else this.activeCell={row:t,col:0},this.selectionRanges=[...this.selectionRanges,i]}else if(e.shiftKey&&this.selectionRanges.length>0){const e=Math.min(this.activeCell.row,t),i=Math.max(this.activeCell.row,t);this.selectionRanges=[{startRow:e,endRow:i,startCol:0,endCol:n}]}else this.activeCell={row:t,col:0},this.selectionRanges=[i];this.mouseDownRange=this.selectionRanges[this.selectionRanges.length-1],this.isMouseDown=!0,this.emitSelectionChange(),e.preventDefault()},onColHeaderMouseDown(e,t){if(t.isGroup)return;const n=t.column._flatIndex;if(e.shiftKey&&this.selectionRanges.length>0){const e=Math.min(this.activeCell.col,n),t=Math.max(this.activeCell.col,n);this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:e,endCol:t}]}else this.activeCell={row:0,col:n},this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:n,endCol:n}];this.isMouseDown=!0,this.mouseDownRange=this.selectionRanges[0],this.emitSelectionChange(),e.preventDefault()},onColHeaderDblClick(e,t){t.isGroup||this.autoFitColumn(t.column._flatIndex)},onColHeaderContextMenu(e,t){t.isGroup||(this.contextMenu={visible:!0,x:e.clientX,y:e.clientY,row:-1,col:t.column._flatIndex})},onRowContextMenu(e,t){this.contextMenu={visible:!0,x:e.clientX,y:e.clientY,row:t,col:-1}},onCellContextMenu(e,t,n){this.isSelected(t,n)||this.selectCell(t,n),this.contextMenu={visible:!0,x:e.clientX,y:e.clientY,row:t,col:n}},hideContextMenu(){this.contextMenu.visible=!1},onKeyDown(e){if(this.selectDropdown){const t=e.key;if("Escape"===t)return e.preventDefault(),void this.closeSelectDropdown();if("Enter"===t||" "===t){e.preventDefault();const t=this.selectDropdown.highlight;return void(t>=0&&this.selectDropdown.options[t]?this.commitSelectValue(this.selectDropdown.options[t].value):this.closeSelectDropdown())}if("ArrowDown"===t)return e.preventDefault(),void(this.selectDropdown.highlight=Math.min(this.selectDropdown.highlight+1,this.selectDropdown.options.length-1));if("ArrowUp"===t)return e.preventDefault(),void(this.selectDropdown.highlight=Math.max(this.selectDropdown.highlight-1,0));if(this.selectDropdown.tree&&!this.selectDropdown.searchQuery&&("ArrowRight"===t||"ArrowLeft"===t)){e.preventDefault();const n=this.selectDropdown.options[this.selectDropdown.highlight];return void(n&&n.hasChildren&&("ArrowRight"!==t||n.expanded||this.toggleTreeNode(this.selectDropdown.highlight),"ArrowLeft"===t&&n.expanded&&this.toggleTreeNode(this.selectDropdown.highlight)))}return}if(this.editingCell)return;const t=e.key;if((e.ctrlKey||e.metaKey)&&"z"===t&&!e.shiftKey)return e.preventDefault(),void this.undo();if((e.ctrlKey||e.metaKey)&&("y"===t||"z"===t&&e.shiftKey))return e.preventDefault(),void this.redo();if("a"===t&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.selectAll();if("c"===t&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.copySelection();if("x"===t&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.cutSelection();if("v"===t&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.pasteAt(this.activeCell.row,this.activeCell.col);if("Delete"===t||"Backspace"===t)return e.preventDefault(),void this.clearSelection();if("F2"===t){e.preventDefault();const t=this.leafCols[this.activeCell.col];return void(t&&this.isSelectLikeCol(t)&&!this.hasEditSlot(t)?this.openSelectDropdownForActive():t&&"checkbox"===t.type?this.toggleCheckbox(this.activeCell.row,this.activeCell.col):this.startEditing(this.activeCell.row,this.activeCell.col,null,!0))}if(" "===t){const t=this.leafCols[this.activeCell.col];if(t&&"checkbox"===t.type)return e.preventDefault(),void this.toggleCheckbox(this.activeCell.row,this.activeCell.col)}if("Escape"===t)return this.clipboardRange=null,this.clipboardMatrix=null,void(this.clipboardMode=null);if("Tab"===t)return e.preventDefault(),void this.moveActive(0,e.shiftKey?-1:1,e);if("Enter"===t)return e.preventDefault(),void this.moveActive(e.shiftKey?-1:1,0,e);const n={ArrowUp:[-1,0],ArrowDown:[1,0],ArrowLeft:[0,-1],ArrowRight:[0,1]};if(n[t]){e.preventDefault();const[i,l]=n[t];if(e.shiftKey){const e=this.primaryRange||s(this.activeCell.row,this.activeCell.col),t=o(e);-1===i&&(e.startRow=Math.max(0,t.startRow-1)),1===i&&(e.endRow=Math.min(this.totalRows-1,t.endRow+1)),-1===l&&(e.startCol=Math.max(0,t.startCol-1)),1===l&&(e.endCol=Math.min(this.leafCols.length-1,t.endCol+1)),this.selectionRanges=[e],this.emitSelectionChange()}else this.moveActive(i,l,e);return}if("Home"===t)return e.preventDefault(),void(e.ctrlKey?this.selectCell(0,0):this.selectCell(this.activeCell.row,0));if("End"===t)return e.preventDefault(),void(e.ctrlKey?this.selectCell(this.totalRows-1,this.leafCols.length-1):this.selectCell(this.activeCell.row,this.leafCols.length-1));if("PageUp"===t){e.preventDefault();const t=Math.floor((this.height-this._headerRowH*this.headerDepth)/this._rowH);return void this.moveActive(-t,0,e)}if("PageDown"===t){e.preventDefault();const t=Math.floor((this.height-this._headerRowH*this.headerDepth)/this._rowH);return void this.moveActive(t,0,e)}},onKeyPress(e){if(!this.editingCell&&!this.selectDropdown&&!(e.ctrlKey||e.metaKey||e.altKey)&&e.key&&1===e.key.length){const t=this.leafCols[this.activeCell.col];if(t&&this.isSelectLikeCol(t)&&!this.hasEditSlot(t))return e.preventDefault(),void this.openSelectDropdownForActive();if(t&&"checkbox"===t.type)return void e.preventDefault();this.startEditing(this.activeCell.row,this.activeCell.col,e.key,!1)}},onGlobalKeyDown(e){},moveActive(e,t,n){let i=this.activeCell.row+e,l=this.activeCell.col+t;for(n&&"Tab"===n.key&&(l>=this.leafCols.length&&(l=0,i++),l<0&&(l=this.leafCols.length-1,i--)),i=Math.max(0,Math.min(this.totalRows-1,i)),l=Math.max(0,Math.min(this.leafCols.length-1,l));this.isColHidden(l)&&l>0&&l<this.leafCols.length-1;)l+=t>=0?1:-1;for(;this.isRowHidden(i)&&i>0&&i<this.totalRows-1;)i+=e>=0?1:-1;const r=function(e,t,n){const i=y(e,t,n);if(!i)return{row:e,col:t};const l=o(i);return{row:l.startRow,col:l.startCol}}(i,l,this.internalMerges);i=r.row,l=r.col,this.activeCell={row:i,col:l},this.selectionRanges=[s(i,l)],this.emitSelectionChange(),this.scrollToCell(i,l)},scrollToCell(e,t){const n=this.$refs.scrollBody;if(!n)return;const i=h(e,this._rowH,this.rowHeights),o=i+this.getRowH(e),l=this.getColLeft(t)-this.getFrozenWidth(),s=l+this.getColW(t);i<n.scrollTop&&(n.scrollTop=i),o>n.scrollTop+n.clientHeight&&(n.scrollTop=o-n.clientHeight),l<n.scrollLeft&&(n.scrollLeft=l),s>n.scrollLeft+n.clientWidth&&(n.scrollLeft=s-n.clientWidth)},startEditing(e,t,n=null,i=!1){const o=this.leafCols[t];if(o&&this.isCellEditable(e,t)&&!(e<0||e>=this.totalRows)){if(!this.hasEditSlot(o)){if(this.isSelectLikeCol(o))return void this.openSelectDropdownForActive();if("checkbox"===o.type)return}this.invalidCell=null,this.editingCell={row:e,col:t},this.editValue=null!==n?n:i?String(this.getCellValue(e,t)||""):"",this.editOriginalValue=String(this.getCellValue(e,t)||""),this.$nextTick(()=>{const e=this.$refs.editInput;if(e){const t=Array.isArray(e)?e[0]:e;t&&(t.focus(),i&&t.setSelectionRange(t.value.length,t.value.length))}})}},commitEdit(){if(!this.editingCell)return;const{row:e,col:t}=this.editingCell,n=this.leafCols[t];if(!n)return void(this.editingCell=null);if(!this.isCellValueValid(e,t,this.editValue))return this._showInvalidCell(e,t,this.editValue),this.editingCell=null,void(this.$refs.wrapper&&this.$refs.wrapper.focus());this.invalidCell=null;const i=this.data,o=i.slice();o[e]={...i[e],[n.key]:this.editValue},this._emitDataChange(i,o),this.editingCell=null,this.$refs.wrapper&&this.$refs.wrapper.focus()},cancelEdit(){this.editingCell=null,this.$refs.wrapper&&this.$refs.wrapper.focus()},hasEditSlot(e){return!!(e&&e.key&&this.$scopedSlots&&this.$scopedSlots["edit-"+e.key])},commitEditSlot(e){if(!this.editingCell)return;const{row:t,col:n}=this.editingCell,i=this.leafCols[n];if(!i)return void(this.editingCell=null);if(!this.isCellValueValid(t,n,e))return this._showInvalidCell(t,n,e),this.editingCell=null,void(this.$refs.wrapper&&this.$refs.wrapper.focus());this.invalidCell=null;const o=this.data,l=o.slice();l[t]={...o[t],[i.key]:e},this._emitDataChange(o,l),this.editingCell=null,this.$refs.wrapper&&this.$refs.wrapper.focus()},isCheckboxChecked(e,t){const n=this.getCellValue(e,t);return!0===n||1===n||"string"==typeof n&&["true","1"].includes(n.trim().toLowerCase())},setCheckboxValue(e,t,n){const i=this.leafCols[t];if(!i||!this.isCellEditable(e,t))return;if(!this.isCellValueValid(e,t,n))return void this._showInvalidCell(e,t,n);this.invalidCell=null;const o=this.data,l=o.slice();l[e]={...o[e],[i.key]:n},this._emitDataChange(o,l)},toggleCheckbox(e,t){this.setCheckboxValue(e,t,!this.isCheckboxChecked(e,t))},onCheckboxChange(e,t,n){this.setCheckboxValue(t,n,e.target.checked)},isCellEditable(e,t){const n=this.leafCols[t];if(!n)return!1;const i=this._dataRaw[e]||{};return"function"==typeof this.cellEditable?!!this.cellEditable(i,e,n,t):"function"==typeof n.editable?!!n.editable(i,e):!1!==n.editable},isCellValueValid(e,t,n){if("function"!=typeof this.cellValidator)return!0;const i=this.leafCols[t];if(!i)return!0;const o=this._dataRaw[e]||{};return!!this.cellValidator(n,o,e,i,t)},_resolveValidationMessage(e,t,n){const i=this.leafCols[t],o=this._dataRaw[e]||{};return"function"==typeof this.cellValidationMessage?this.cellValidationMessage(n,o,e,i,t):"string"==typeof this.cellValidationMessage&&this.cellValidationMessage?this.cellValidationMessage:this.t.invalidValue},_invalidCellRect(){const e=this.$refs.editInput,t=Array.isArray(e)?e[0]:e;if(t)return t.getBoundingClientRect();const n=this.$el?this.$el.querySelector(".eg-cell-active"):null;return n?n.getBoundingClientRect():null},_showInvalidCell(e,t,n){const i=this._resolveValidationMessage(e,t,n);this.invalidCell={row:e,col:t,message:i,rect:this._invalidCellRect()},this.$emit("cell-validation-error",{row:e,col:t,value:n,message:i})},_emitBulkValidationError(e,t){const n=e.map(e=>({row:e.row,col:e.col,value:e.value,message:this._resolveValidationMessage(e.row,e.col,e.value)}));this.$emit("bulk-validation-error",{blocked:t,cells:n})},isSelectLikeCol:e=>!(!e||"select"!==e.type&&"treeSelect"!==e.type),treeOptionKeys:e=>({valueKey:e.optionValueKey||"value",labelKey:e.optionLabelKey||"label",childrenKey:e.optionChildrenKey||"children"}),_treeFlatten(e,t,n,i,o){let l=[];return(e||[]).forEach((e,s)=>{const r=o.concat(s),a=r.join("-"),c=Array.isArray(e[t.childrenKey])?e[t.childrenKey]:[],d=c.length>0,h=d&&!!n[a];l.push({value:e[t.valueKey],label:null!=e[t.labelKey]?e[t.labelKey]:String(e[t.valueKey]),color:e.color,id:a,depth:i,hasChildren:d,expanded:h}),h&&(l=l.concat(this._treeFlatten(c,t,n,i+1,r)))}),l},_treeFlattenAll(e,t,n,i){return(e||[]).forEach(e=>{const o=Array.isArray(e[t.childrenKey])?e[t.childrenKey]:[];i.push({value:e[t.valueKey],label:null!=e[t.labelKey]?e[t.labelKey]:String(e[t.valueKey]),color:e.color,id:String(e[t.valueKey]),depth:0,hasChildren:!1,expanded:!1}),o.length&&this._treeFlattenAll(o,t,n+1,i)}),i},_treeFindLabel(e,t,n){for(const i of e||[]){if(String(i[t.valueKey])===n)return null!=i[t.labelKey]?i[t.labelKey]:String(i[t.valueKey]);const e=this._treeFindLabel(i[t.childrenKey],t,n);if(null!=e)return e}return null},_treeFindPath(e,t,n,i){for(let o=0;o<(e||[]).length;o++){const l=e[o],s=i.concat(o);if(String(l[t.valueKey])===n)return[s.join("-")];const r=l[t.childrenKey];if(Array.isArray(r)){const e=this._treeFindPath(r,t,n,s);if(e)return[s.join("-")].concat(e)}}return null},colOptions(e){const t=this.leafCols[e];return t&&Array.isArray(t.options)?"treeSelect"===t.type?this._treeFlatten(t.options,this.treeOptionKeys(t),{},0,[]):t.options.map(e=>"string"==typeof e?{value:e,label:e}:{value:e.value,label:null!=e.label?e.label:String(e.value),color:e.color}):[]},getSelectLabel(e,t){const n=this.leafCols[e],i=null==t?"":String(t);if(n&&"treeSelect"===n.type){if(!Array.isArray(n.options))return i;const e=this._treeFindLabel(n.options,this.treeOptionKeys(n),i);return null!=e?e:i}const o=this.colOptions(e);if(!o.length)return i;const l=o.find(e=>String(e.value)===i);return l?l.label:i},_buildSelectDropdownState(e,t,n,i,o){const l={top:o.top,bottom:o.bottom,left:o.left,width:o.width},s=!!n.enableSearch;if("treeSelect"===n.type){const o=this.treeOptionKeys(n),r=Array.isArray(n.options)&&this._treeFindPath(n.options,o,i,[])||[],a={};r.forEach(e=>{a[e]=!0});const c=this._treeFlatten(n.options,o,a,0,[]);return{row:e,col:t,tree:!0,keys:o,expandedMap:a,options:c,allFlatOptions:s?this._treeFlattenAll(n.options||[],o,0,[]):null,currentValue:i,highlight:c.findIndex(e=>String(e.value)===i),rect:l,enableSearch:s,searchQuery:""}}const r=this.colOptions(t);return{row:e,col:t,tree:!1,options:r,allFlatOptions:null,currentValue:i,highlight:r.findIndex(e=>String(e.value)===i),rect:l,enableSearch:s,searchQuery:""}},toggleTreeNode(e){if(!this.selectDropdown||!this.selectDropdown.tree)return;const t=this.selectDropdown.options[e];if(!t||!t.hasChildren)return;const{row:n,col:i,expandedMap:o,keys:l}=this.selectDropdown,s=this.leafCols[i];if(!s)return;const r={...o};r[t.id]?delete r[t.id]:r[t.id]=!0;const a=this._treeFlatten(s.options,l,r,0,[]),c=a.findIndex(e=>e.id===t.id);this.selectDropdown={...this.selectDropdown,row:n,col:i,expandedMap:r,options:a,highlight:c>=0?c:this.selectDropdown.highlight}},openSelectDropdown(e,t,n){const i=this.leafCols[t];if(!i||!Array.isArray(i.options))return;const o=n.currentTarget.getBoundingClientRect(),l=String(null!=this.getCellValue(e,t)?this.getCellValue(e,t):"");this.selectDropdown=this._buildSelectDropdownState(e,t,i,l,o),i.enableSearch&&this.$nextTick(()=>{this.$refs.dropdownSearch&&this.$refs.dropdownSearch.focus()})},openSelectDropdownForActive(){const{row:e,col:t}=this.activeCell,n=this.leafCols[t];n&&Array.isArray(n.options)&&this.$nextTick(()=>{const i=this.$el?this.$el.querySelector(".eg-cell-active"):null;if(!i)return;const o=i.getBoundingClientRect(),l=String(null!=this.getCellValue(e,t)?this.getCellValue(e,t):"");this.selectDropdown=this._buildSelectDropdownState(e,t,n,l,o),n.enableSearch&&this.$nextTick(()=>{this.$refs.dropdownSearch&&this.$refs.dropdownSearch.focus()})})},commitSelectValue(e){if(!this.selectDropdown)return;const{row:t,col:n}=this.selectDropdown,i=this.leafCols[n];if(i){if(!this.isCellValueValid(t,n,e))return this._showInvalidCell(t,n,e),this.closeSelectDropdown(),void(this.$refs.wrapper&&this.$refs.wrapper.focus());this.invalidCell=null;const o=this.data,l=o.slice();if(l[t]={...o[t],[i.key]:e},this._emitDataChange(o,l),"function"==typeof i.onSelect){const n=this.filteredSelectOptions.find(t=>t.value===e);i.onSelect({value:e,label:n?n.label:String(e)},l[t],t,i)}}this.closeSelectDropdown(),this.$refs.wrapper&&this.$refs.wrapper.focus()},onDropdownSearch(e){this.selectDropdown&&(this.selectDropdown.searchQuery=e.target.value,this.selectDropdown.highlight=0)},onDropdownSearchKeyDown(e){if(!this.selectDropdown)return;const{key:t}=e;if("Escape"===t)this.closeSelectDropdown(),this.$refs.wrapper&&this.$refs.wrapper.focus();else if("ArrowDown"===t)e.preventDefault(),this.selectDropdown.highlight=Math.min((this.selectDropdown.highlight||0)+1,this.filteredSelectOptions.length-1);else if("ArrowUp"===t)e.preventDefault(),this.selectDropdown.highlight=Math.max((this.selectDropdown.highlight||0)-1,0);else if("Enter"===t){e.preventDefault();const t=this.selectDropdown.highlight,n=this.filteredSelectOptions;t>=0&&n[t]&&this.commitSelectValue(n[t].value)}},closeSelectDropdown(){this.selectDropdown=null},_onWindowScrollForDropdown(e){if(!this.selectDropdown)return;const t=this.$el?this.$el.querySelector(".eg-select-dropdown"):null;t&&t.contains(e.target)||this.closeSelectDropdown()},_onDocMouseDownForDropdown(e){if(!this.selectDropdown)return;const t=this.$el?this.$el.querySelector(".eg-select-dropdown"):null;t&&t.contains(e.target)||this.closeSelectDropdown()},onEditKeyDown(e){"Escape"===e.key?(e.preventDefault(),this.cancelEdit()):"Enter"!==e.key||e.shiftKey?"Tab"===e.key&&(e.preventDefault(),this.commitEdit(),this.moveActive(0,e.shiftKey?-1:1,e)):(e.preventDefault(),this.commitEdit(),this.moveActive(1,0,e))},clearSelection(){if(!this.selectionRanges.length)return;const e=this.data,t=e.slice();let n=!1;const i=[];for(const e of this.selectionRanges){const o={startRow:Math.min(e.startRow,e.endRow),endRow:Math.max(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endCol:Math.max(e.startCol,e.endCol)};for(let e=o.startRow;e<=o.endRow&&e<t.length;e++){let l=null;for(let s=o.startCol;s<=o.endCol;s++){const o=this.leafCols[s];if(o&&this.isCellEditable(e,s)){if(!this.isCellValueValid(e,s,"")){i.push({row:e,col:s,value:""});continue}l||(l={...t[e]}),l[o.key]="",n=!0}}l&&(t[e]=l)}}this.strictBulkValidation&&i.length>0?this._emitBulkValidationError(i,!0):(n&&this._emitDataChange(e,t),i.length>0&&this._emitBulkValidationError(i,!1))},startFillDrag(e){const t=this.primaryRange;t&&(this.fillDrag.active=!0,this.fillDrag.sourceRange=o(t),this.fillDrag.currentRow=t.endRow,this.fillDrag.currentCol=t.endCol,e.preventDefault())},onGlobalMouseMove(e){this.resizing.active?this.handleResizeMove(e):this.fillDrag.active?this.handleFillDragMove(e):this.dragMove.active&&this.handleDragMoveMove(e)},onGlobalMouseUp(e){this.resizing.active?this.commitResize():this.fillDrag.active?this.commitFillDrag():this.dragMove.active?this.commitDragMove(e):this.isMouseDown=!1},handleFillDragMove(e){const t=this.$refs.scrollBody;if(!t)return;const n=t.getBoundingClientRect(),i=e.clientY-n.top+t.scrollTop,o=e.clientX-n.left+t.scrollLeft,l=this.cumulativeHeights,s=this.totalRows;let r=0,a=s-1;for(;r<a;){const e=r+a>>1;l[e+1]<=i?r=e+1:a=e}const c=Math.min(r,s-1);let d=this.getFrozenWidth(),h=0;for(let e=0;e<this.leafCols.length;e++)if(this.isColumnScrollable(e)){if(d+=this.getColW(e),o<d){h=e;break}h=e}this.fillDrag.currentRow=c,this.fillDrag.currentCol=h},commitFillDrag(){const e=this.fillDrag.sourceRange;if(!e)return void(this.fillDrag.active=!1);const t=this.fillDrag.currentRow,n=this.fillDrag.currentCol,i=this.data,o=i.slice();let l;const s=[];if(t>e.endRow){const n=t-e.endRow;for(let t=e.startCol;t<=e.endCol;t++){const l=this.leafCols[t];if(!l)continue;const r=[];for(let t=e.startRow;t<=e.endRow;t++)r.push(i[t]?i[t][l.key]:"");const a=w(r,n);for(let r=0;r<n;r++){const n=e.endRow+1+r;if(n<this.totalRows&&this.isCellEditable(n,t)){if(!this.isCellValueValid(n,t,a[r])){s.push({row:n,col:t,value:a[r]});continue}o[n]===i[n]&&(o[n]={...i[n]}),o[n][l.key]=a[r]}}}l={startRow:e.endRow+1,endRow:t,startCol:e.startCol,endCol:e.endCol}}else if(n>e.endCol){const t=n-e.endCol;for(let n=e.startRow;n<=e.endRow;n++){const l=[];for(let t=e.startCol;t<=e.endCol;t++){const e=this.leafCols[t];l.push(e&&i[n]?i[n][e.key]:"")}const r=w(l,t);for(let l=0;l<t;l++){const t=e.endCol+1+l;if(t<this.leafCols.length){const e=this.leafCols[t];if(e&&this.isCellEditable(n,t)){if(!this.isCellValueValid(n,t,r[l])){s.push({row:n,col:t,value:r[l]});continue}o[n]===i[n]&&(o[n]={...i[n]}),o[n][e.key]=r[l]}}}}l={startRow:e.startRow,endRow:e.endRow,startCol:e.endCol+1,endCol:n}}l&&(this.strictBulkValidation&&s.length>0?this._emitBulkValidationError(s,!0):(this._emitDataChange(i,o),this.$emit("fill",{sourceRange:e,targetRange:l,values:o}),s.length>0&&this._emitBulkValidationError(s,!1))),this.fillDrag.active=!1,this.fillDrag.sourceRange=null},doubleFillDown(){const e=this.primaryRange;if(!e)return;const t=o(e),n=t.startCol>0?t.startCol-1:t.endCol+1;if(n<0||n>=this.leafCols.length)return;const i=this.leafCols[n];if(!i)return;let l=t.endRow;for(let e=t.endRow+1;e<this.totalRows&&(this.data[e]&&""!==this.data[e][i.key]&&void 0!==this.data[e][i.key]);e++)l=e;if(l<=t.endRow)return;const s=l-t.endRow,r=this.data,a=r.slice(),c=[];for(let e=t.startCol;e<=t.endCol;e++){const n=this.leafCols[e];if(!n)continue;const i=[];for(let e=t.startRow;e<=t.endRow;e++)i.push(r[e]?r[e][n.key]:"");const o=w(i,s);for(let i=0;i<s;i++){const l=t.endRow+1+i;if(l<this.totalRows&&this.isCellEditable(l,e)){if(!this.isCellValueValid(l,e,o[i])){c.push({row:l,col:e,value:o[i]});continue}a[l]===r[l]&&(a[l]={...r[l]}),a[l][n.key]=o[i]}}}this.strictBulkValidation&&c.length>0?this._emitBulkValidationError(c,!0):(this._emitDataChange(r,a),c.length>0&&this._emitBulkValidationError(c,!1))},copySelection(){const e=this.primaryRange;if(!e)return;const t=o(e);this.clipboardMatrix=m(this.data,this.leafCols,t),this.clipboardRange=t,this.clipboardMode="copy";const n=this.clipboardMatrix.map(e=>e.map(e=>(null==e?"":String(e)).replace(/\t/g," ").replace(/\r?\n/g," ")).join("\t")).join("\n");navigator.clipboard&&navigator.clipboard.writeText(n).catch(()=>{})},cutSelection(){this.copySelection(),this.clipboardMode="cut"},async pasteAt(e,t,n={}){const{mode:i="all",transpose:l=!1}=n;let s=null;if(navigator.clipboard)try{const e=await navigator.clipboard.readText();e&&(s=(r=e)?r.split("\n").map(e=>e.split("\t")):[[""]])}catch(e){}var r;if(!s&&this.clipboardMatrix&&(s=this.clipboardMatrix),!s)return;l&&(s=function(e){if(!e||0===e.length)return[];const t=e.length,n=e[0].length;return Array.from({length:n},(n,i)=>Array.from({length:t},(t,n)=>e[n][i]))}(s));if(!this.clipboardMode||"copy"===this.clipboardMode){const n=this.primaryRange;if(n){const i=o(n),l=i.endRow-i.startRow+1,r=i.endCol-i.startCol+1,a=s.length,c=s[0]?s[0].length:0;(l>a||r>c)&&l%a===0&&r%c===0&&(s=function(e,t,n){const i=e.length,o=e[0]?e[0].length:0;if(!i||!o)return e;if(t%i!==0||n%o!==0)return e;const l=[];for(let s=0;s<t;s++){const t=[];for(let l=0;l<n;l++)t.push(e[s%i][l%o]);l.push(t)}return l}(s,l,r),e=i.startRow,t=i.startCol)}}const a=this.data,{newData:c,affectedRange:d,skipped:h}=x(a,this.leafCols,e,t,s,i,(e,t)=>this.isCellEditable(e,t),(e,t,n)=>this.isCellValueValid(e,t,n));if(this.strictBulkValidation&&h.length>0)this._emitBulkValidationError(h,!0);else{if("cut"===this.clipboardMode&&this.clipboardRange){const e=this.clipboardRange;for(let t=e.startRow;t<=e.endRow;t++){c[t]===a[t]&&(c[t]={...a[t]});for(let n=e.startCol;n<=e.endCol;n++){const e=this.leafCols[n];e&&this.isCellEditable(t,n)&&this.isCellValueValid(t,n,"")&&(c[t][e.key]="")}}this.clipboardRange=null,this.clipboardMode=null}this._emitDataChange(a,c),h.length>0&&this._emitBulkValidationError(h,!1)}},ctxCopy(){this.hideContextMenu(),this.copySelection()},ctxCut(){this.hideContextMenu(),this.cutSelection()},ctxPaste(){this.hideContextMenu(),this.pasteAt(this.activeCell.row,this.activeCell.col)},ctxPasteSpecial(e,t=!1){this.hideContextMenu(),this.pasteAt(this.activeCell.row,this.activeCell.col,{mode:e,transpose:t})},startColResize(e,t){this.$refs.wrapper.getBoundingClientRect(),this.resizing={active:!0,type:"col",index:t,startX:e.clientX,startY:e.clientY,startSize:this.getColW(t),currentPos:e.clientX},e.preventDefault()},startRowResize(e,t){this.resizing={active:!0,type:"row",index:t,startX:e.clientX,startY:e.clientY,startSize:this.getRowH(t),currentPos:e.clientY},e.preventDefault()},handleResizeMove(e){if("col"===this.resizing.type){this.resizing.currentPos=e.clientX;const t=Math.max(20,this.resizing.startSize+(e.clientX-this.resizing.startX));this.$set(this.colWidths,this.resizing.index,t),this._cellStyleCache=Object.create(null)}else this.resizing.currentPos=e.clientY,this._resizePreviewHeight=Math.max(10,this.resizing.startSize+(e.clientY-this.resizing.startY)),this._resizeRAF||(this._resizeRAF=requestAnimationFrame(()=>{this._resizeRAF=null,this._rowStyleCache=Object.create(null),this._rowHeaderStyleCache=Object.create(null),this._cellStyleCache=Object.create(null),this.$set(this.rowHeights,this.resizing.index,this._resizePreviewHeight),this.updateVirtualScroll()}))},commitResize(){if(this._resizeRAF&&(cancelAnimationFrame(this._resizeRAF),this._resizeRAF=null,"row"===this.resizing.type&&void 0!==this._resizePreviewHeight&&this.$set(this.rowHeights,this.resizing.index,this._resizePreviewHeight)),"col"===this.resizing.type){this._cellStyleCache=Object.create(null);const e=this.colWidths[this.resizing.index];this.$emit("column-resize",{index:this.resizing.index,newSize:e})}else{this._rowStyleCache=Object.create(null),this._rowHeaderStyleCache=Object.create(null),this._cellStyleCache=Object.create(null);const e=this.rowHeights[this.resizing.index];this.$emit("row-resize",{index:this.resizing.index,newSize:e}),this.updateVirtualScroll()}this.resizing.active=!1},autoFitColumn(e){const t=this.leafCols[e];if(!t)return;let n=(t.title||"").length;for(let e=0;e<Math.min(this.totalRows,500);e++){const i=this.data[e]?String(this.data[e][t.key]||""):"";i.length>n&&(n=i.length)}const i=Math.max(50,8*n+16);this.$set(this.colWidths,e,i),this._cellStyleCache=Object.create(null),this.$emit("column-resize",{index:e,newSize:i})},_applyAutoWidth(){this.autoWidth&&this.leafCols.length&&(this.leafCols.forEach((e,t)=>{let n=(e.title||"").length;for(let t=0;t<Math.min(this.totalRows,500);t++){const i=this.data[t]?String(this.data[t][e.key]||""):"";i.length>n&&(n=i.length)}this.$set(this.colWidths,t,Math.max(50,8*n+16))}),this._cellStyleCache=Object.create(null))},ctxHideRow(){this.hideContextMenu();const e=this.primaryRange;if(!e)return;const t=o(e),n=[...this.hiddenRows];for(let e=t.startRow;e<=t.endRow;e++)n.includes(e)||n.push(e);this.$emit("update:hiddenRows",n)},ctxHideColumn(){this.hideContextMenu();const e=this.primaryRange;if(!e)return;const t=o(e),n=[...this.hiddenColumns];for(let e=t.startCol;e<=t.endCol;e++)n.includes(e)||n.push(e);this.$emit("update:hiddenColumns",n)},ctxUnhideRows(){this.hideContextMenu(),this.$emit("update:hiddenRows",[])},ctxUnhideColumns(){this.hideContextMenu(),this.$emit("update:hiddenColumns",[])},ctxPinLeft(){const e=this.leafCols[this.contextMenu.col];e?(this.$emit("update:pinnedColumns",{...this.pinnedColumns,[e.key]:"left"}),this.hideContextMenu()):this.hideContextMenu()},ctxPinRight(){const e=this.leafCols[this.contextMenu.col];e?(this.$emit("update:pinnedColumns",{...this.pinnedColumns,[e.key]:"right"}),this.hideContextMenu()):this.hideContextMenu()},ctxUnpin(){const e=this.leafCols[this.contextMenu.col];if(!e)return void this.hideContextMenu();const t={...this.pinnedColumns};delete t[e.key],this.$emit("update:pinnedColumns",t),this.hideContextMenu()},isColPinned(e){const t=this.leafCols[e];return!(!t||!this.pinnedColumns[t.key])},mergeCells(e){const t=this.internalMerges.slice(),{merges:n,conflict:i}=function(e,t){const n=o(e);return n.startRow===n.endRow&&n.startCol===n.endCol?{merges:t,conflict:!1}:v(n,t)?{merges:t,conflict:!0}:{merges:[...t,n],conflict:!1}}(e,this.internalMerges);return i?(this.$emit("merge-error",{range:e,reason:"Overlaps existing merge"}),!1):(this.internalMerges=n,this._emitMergeChange(t,[...n]),!0)},unmergeCells(e,t){const n=this.internalMerges.slice(),i=function(e,t,n){return n.filter(n=>{const i=o(n);return!(e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol)})}(e,t,this.internalMerges);this.internalMerges=i,this._emitMergeChange(n,[...i])},ctxMerge(){this.hideContextMenu();const e=this.primaryRange;e&&this.mergeCells(o(e))},ctxUnmerge(){this.hideContextMenu(),this.unmergeCells(this.activeCell.row,this.activeCell.col)},groupRows(e,t){this.internalGroups=b("row",e,t,this.internalGroups)},groupColumns(e,t){this.internalGroups=b("column",e,t,this.internalGroups)},ungroupRows(e,t){this.internalGroups=R("row",e,t,this.internalGroups)},ungroupColumns(e,t){this.internalGroups=R("column",e,t,this.internalGroups)},toggleGroup(e){this.internalCollapsedGroups=function(e,t){return t.find(t=>t.type===e.type&&t.startIndex===e.startIndex&&t.endIndex===e.endIndex)?t.filter(t=>!(t.type===e.type&&t.startIndex===e.startIndex&&t.endIndex===e.endIndex)):[...t,{type:e.type,startIndex:e.startIndex,endIndex:e.endIndex}]}(e,this.internalCollapsedGroups),this.$emit("update:collapsedGroups",[...this.internalCollapsedGroups]),this.$emit("group-toggle",{group:e,collapsed:k(e,this.internalCollapsedGroups)}),this.updateVirtualScroll()},isGroupCollapsed(e){return k(e,this.internalCollapsedGroups)},expandAll(){this.internalCollapsedGroups=[],this.$emit("update:collapsedGroups",[]),this.updateVirtualScroll()},collapseAll(){this.internalCollapsedGroups=[...this.internalGroups],this.$emit("update:collapsedGroups",[...this.internalGroups]),this.updateVirtualScroll()},startDragMove(e,t,n){const i=this.primaryRange;i&&(this.dragMove={active:!0,sourceRange:o(i),startX:e.clientX,startY:e.clientY,targetRow:t,targetCol:n,copyMode:e.ctrlKey||e.metaKey},this.$emit("cell-drag-start",{sourceRange:this.dragMove.sourceRange}),e.preventDefault())},handleDragMoveMove(e){this.dragMove.copyMode=e.ctrlKey||e.metaKey,this.$emit("cell-drag-move",{sourceRange:this.dragMove.sourceRange,targetRow:this.dragMove.targetRow,targetCol:this.dragMove.targetCol})},async commitDragMove(e){if(!this.dragMove.active)return;const{sourceRange:t,targetRow:n,targetCol:i,copyMode:o}=this.dragMove,l={startRow:n,startCol:i,endRow:n+(t.endRow-t.startRow),endCol:i+(t.endCol-t.startCol)};let s=!0;if(this.beforeDrop&&(s=await Promise.resolve(this.beforeDrop(t,l))),s){const e=this.data,s=m(e,this.leafCols,t),{newData:r}=x(e,this.leafCols,n,i,s,"all",(e,t)=>this.isCellEditable(e,t));if(!o)for(let n=t.startRow;n<=t.endRow;n++){r[n]===e[n]&&(r[n]={...e[n]});for(let e=t.startCol;e<=t.endCol;e++){const t=this.leafCols[e];t&&this.isCellEditable(n,e)&&(r[n][t.key]="")}}this._emitDataChange(e,r),this.$emit("cell-drop",{sourceRange:t,targetRange:l})}this.dragMove={active:!1,sourceRange:null,startX:0,startY:0,targetRow:-1,targetCol:-1,copyMode:!1},this.isMouseDown=!1},async exportToExcel(e={}){const{filename:t="export.xlsx",sheetName:n="Sheet1",scope:l="all",includeHidden:s=!1}=e;this.$emit("export-start"),this.exportState={active:!0};const d={data:this.data,columns:this.columns,leafCols:this.leafCols,merges:this.internalMerges,hiddenCols:[...this.effectiveHiddenColSet],hiddenRows:[...this.hiddenRowSet],range:"selection"===l?this.primaryRange:null,filename:t,sheetName:n,scope:l,includeHidden:s};try{null||function(e){const{data:t,columns:n,leafCols:l,merges:s=[],hiddenColSet:d=new Set,hiddenRowSet:h=new Set,range:u=null,filename:p="export.xlsx",sheetName:g="Sheet1",scope:f="all",includeHidden:A=!1}=e,C=l.filter((e,t)=>{if(!A&&d.has(t))return!1;if("selection"===f&&u){const e=o(u);if(t<e.startCol||t>e.endCol)return!1}return!0});let w=0,m=t.length-1;if("selection"===f&&u){const e=o(u);w=e.startRow,m=e.endRow}const x=[];for(let e=w;e<=m;e++)!A&&h.has(e)||x.push({rowIndex:e,data:t[e]});const v=a(n),y=c(n,v),b=i.utils.book_new(),R=[];for(let e=0;e<y.length;e++){const t=[];for(const n of y[e])if(n.isGroup){const e=r(n.column.children||[]).filter(e=>C.some(t=>t.key===e.key));if(0===e.length)continue;t.push(n.column.title||"");for(let n=1;n<e.length;n++)t.push(null)}else{if(!C.some(e=>e.key===n.column.key))continue;n.rowspan,t.push(n.column.title||"")}R.push(t)}for(const{data:e}of x){const t=C.map(t=>{const n=e?e[t.key]:"";return void 0!==n?n:""});R.push(t)}const k=i.utils.aoa_to_sheet(R);k["!cols"]=C.map(e=>({wch:Math.round((e.width||100)/7)}));const S=[];for(const e of s){const t=o(e),n="selection"===f&&u?o(u):{startRow:0,startCol:0};if("selection"===f){if(t.startRow<n.startRow||t.endRow>n.endRow)continue;if(t.startCol<n.startCol||t.endCol>n.endCol)continue}const i=C.findIndex(e=>l.indexOf(e)===t.startCol),s=C.findIndex(e=>l.indexOf(e)===t.endCol);if(i<0||s<0)continue;const r=x.filter(e=>e.rowIndex<t.startRow).length,a=x.filter(e=>e.rowIndex<=t.endRow).length;S.push({s:{r:v+r,c:i},e:{r:v+a-1,c:s}})}S.length&&(k["!merges"]=S),i.utils.book_append_sheet(b,k,g),i.writeFile(b,p)}({...d,hiddenColSet:this.effectiveHiddenColSet,hiddenRowSet:this.hiddenRowSet}),this.$emit("export-success")}catch(e){this.$emit("export-error",e)}finally{this.exportState={active:!1}}},_exportViaWorker:(e,t)=>new Promise((n,i)=>{let o=!1;const l=(t,n)=>{o||(o=!0,e.terminate(),t(n))};e.onmessage=e=>{const{type:t,buffer:o,filename:s,message:r}=e.data;if("result"===t){const e=new Blob([new Uint8Array(o)],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}),t=URL.createObjectURL(e),i=document.createElement("a");i.href=t,i.download=s,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(t),l(n)}else"error"===t&&l(i,new Error(r))},e.onerror=e=>l(i,new Error(e.message||"Export worker xatosi")),e.postMessage(t)}),async importFromFile(e,t={}){this.importState={active:!0,percent:5,message:"Fayl o'qilmoqda..."};try{let n;null||(n=await M(e,this.columns,t,(e,t)=>{this.importState={active:!0,percent:e,message:t}}));const{columns:i,merges:o}=n,l=n.data.map(e=>Object.freeze(e));this.$emit("input",l),this.$emit("update:columns",i);const s=o&&o.length>0;this.internalMerges=s?o:[],this.$emit("update:merges",s?o:[]),this.$emit("update:collapsedGroups",[]),this.$emit("update:hiddenRows",[]),this.$emit("import-success",{data:l,columns:i,merges:o}),this.rowHeights={},this.scrollTop=0,this.scrollLeft=0,this.$nextTick(()=>{this.$refs.scrollBody&&(this.$refs.scrollBody.scrollTop=0,this.$refs.scrollBody.scrollLeft=0),this.$refs.scrollColHeader&&(this.$refs.scrollColHeader.scrollLeft=0),this.$refs.scrollFooter&&(this.$refs.scrollFooter.scrollLeft=0),this.$refs.scrollRowHeaders&&(this.$refs.scrollRowHeaders.scrollTop=0),this.updateVirtualScroll()})}catch(e){this.$emit("import-error",e)}finally{this.importState={active:!1,percent:0,message:""}}},_importViaWorker(e,t,n){return new Promise(async(i,o)=>{let l=!1;const s=(t,n)=>{l||(l=!0,e.terminate(),t(n))};e.onmessage=e=>{const{type:t,percent:n,message:l,data:r,columns:a,merges:c}=e.data;"progress"===t?this.importState={active:!0,percent:n,message:l}:"result"===t?s(i,{data:r,columns:a,merges:c}):"error"===t&&s(o,new Error(l))},e.onerror=e=>s(o,new Error(e.message||"Import worker xatosi"));try{const i=await t.arrayBuffer();e.postMessage({buffer:i,existingColumns:this.columns,options:n},[i])}catch(e){s(o,e)}})},_pushHistory(e){this._historyIndex<this._history.length-1&&this._history.splice(this._historyIndex+1),this._history.push(e),this._history.length>100?this._history.shift():this._historyIndex++},_emitDataChange(e,t){if(!this._historySkip){const n=[],i=[],o=[],l=Math.max(e.length,t.length);for(let s=0;s<l;s++)e[s]!==t[s]&&(n.push(s),i.push(e[s]),o.push(t[s]));n.length>0&&this._pushHistory({type:"data",before:{indices:n,rows:i},after:{indices:n,rows:o}})}this.$emit("input",t)},_emitMergeChange(e,t){this._historySkip||this._pushHistory({type:"merges",before:e,after:t}),this.$emit("update:merges",t)},undo(){if(this._historyIndex<0)return;const e=this._history[this._historyIndex--];this._historySkip=!0;try{if("data"===e.type){const t=this.data.slice();for(let n=0;n<e.before.indices.length;n++)t[e.before.indices[n]]=e.before.rows[n];this.$emit("input",t)}else"merges"===e.type&&(this.internalMerges=e.before.slice(),this.$emit("update:merges",e.before.slice()))}finally{this._historySkip=!1}},redo(){if(this._historyIndex>=this._history.length-1)return;const e=this._history[++this._historyIndex];this._historySkip=!0;try{if("data"===e.type){const t=this.data.slice();for(let n=0;n<e.after.indices.length;n++)t[e.after.indices[n]]=e.after.rows[n];this.$emit("input",t)}else"merges"===e.type&&(this.internalMerges=e.after.slice(),this.$emit("update:merges",e.after.slice()))}finally{this._historySkip=!1}}}};function L(e,t,n,i,o,l,s,r,a,c){"boolean"!=typeof s&&(a=r,r=s,s=!1);const d="function"==typeof n?n.options:n;let h;if(e&&e.render&&(d.render=e.render,d.staticRenderFns=e.staticRenderFns,d._compiled=!0,o&&(d.functional=!0)),i&&(d._scopeId=i),l?(h=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(l)},d._ssrRegister=h):t&&(h=s?function(e){t.call(this,c(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,r(e))}),h)if(d.functional){const e=d.render;d.render=function(t,n){return h.call(n),e(t,n)}}else{const e=d.beforeCreate;d.beforeCreate=e?[].concat(e,h):[h]}return n}const W="undefined"!=typeof navigator&&/msie [6-9]\\b/.test(navigator.userAgent.toLowerCase());function F(e){return(e,t)=>function(e,t){const n=W?t.media||"default":e,i=$[n]||($[n]={ids:new Set,styles:[]});if(!i.ids.has(e)){i.ids.add(e);let n=t.source;if(t.map&&(n+="\n/*# sourceURL="+t.map.sources[0]+" */",n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(t.map))))+" */"),i.element||(i.element=document.createElement("style"),i.element.type="text/css",t.media&&i.element.setAttribute("media",t.media),void 0===T&&(T=document.head||document.getElementsByTagName("head")[0]),T.appendChild(i.element)),"styleSheet"in i.element)i.styles.push(n),i.element.styleSheet.cssText=i.styles.filter(Boolean).join("\n");else{const e=i.ids.size-1,t=document.createTextNode(n),o=i.element.childNodes;o[e]&&i.element.removeChild(o[e]),o.length?i.element.insertBefore(t,o[e]):i.element.appendChild(t)}}}(e,t)}let T;const $={};const G=B;var O=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{ref:"wrapper",staticClass:"eg-wrapper",class:"eg-size-"+e.size,style:e.wrapperStyle,attrs:{tabindex:"0"},on:{keydown:e.onKeyDown,keypress:e.onKeyPress}},[e.contextMenu.visible?n("div",{staticClass:"eg-context-menu",style:{top:e.contextMenu.y+"px",left:e.contextMenu.x+"px"},on:{click:function(e){e.stopPropagation()}}},[n("div",{staticClass:"eg-context-item",on:{click:e.ctxCopy}},[e._v(e._s(e.t.copy))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxCut}},[e._v(e._s(e.t.cut))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxPaste}},[e._v(e._s(e.t.paste))]),e._v(" "),n("div",{staticClass:"eg-context-separator"}),e._v(" "),n("div",{staticClass:"eg-context-item eg-context-submenu-parent"},[e._v("\n "+e._s(e.t.pasteSpecial)+"\n "),n("div",{staticClass:"eg-context-submenu"},[n("div",{staticClass:"eg-context-item",on:{click:function(t){return e.ctxPasteSpecial("all")}}},[e._v(e._s(e.t.pasteAll))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:function(t){return e.ctxPasteSpecial("values")}}},[e._v(e._s(e.t.pasteValues))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:function(t){return e.ctxPasteSpecial("formats")}}},[e._v(e._s(e.t.pasteFormats))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:function(t){return e.ctxPasteSpecial("all",!0)}}},[e._v(e._s(e.t.transpose))])])]),e._v(" "),n("div",{staticClass:"eg-context-separator"}),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxMerge}},[e._v(e._s(e.t.mergeCells))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxUnmerge}},[e._v(e._s(e.t.unmergeCells))]),e._v(" "),n("div",{staticClass:"eg-context-separator"}),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxHideRow}},[e._v(e._s(e.t.hideRows))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxHideColumn}},[e._v(e._s(e.t.hideColumns))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxUnhideRows}},[e._v(e._s(e.t.unhideRows))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxUnhideColumns}},[e._v(e._s(e.t.unhideColumns))]),e._v(" "),e.contextMenu.row<0&&e.contextMenu.col>=0?[n("div",{staticClass:"eg-context-separator"}),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxPinLeft}},[e._v(e._s(e.t.pinLeft))]),e._v(" "),n("div",{staticClass:"eg-context-item",on:{click:e.ctxPinRight}},[e._v(e._s(e.t.pinRight))]),e._v(" "),e.isColPinned(e.contextMenu.col)?n("div",{staticClass:"eg-context-item",on:{click:e.ctxUnpin}},[e._v(e._s(e.t.unpin))]):e._e()]:e._e()],2):e._e(),e._v(" "),n("div",{ref:"container",staticClass:"eg-container",on:{scroll:e.onScroll,click:e.hideContextMenu}},[n("div",{staticClass:"eg-header-area",style:e.headerAreaStyle},[n("div",{staticClass:"eg-corner",style:e.cornerStyle,on:{click:e.selectAll}}),e._v(" "),e.rowGroups.length?n("div",{staticClass:"eg-row-outline-gutter",style:e.rowOutlineGutterStyle}):e._e(),e._v(" "),e.getFrozenWidth()>0?n("div",{staticClass:"eg-frozen-col-header",style:e.frozenColHeaderStyle},[n("div",{style:{position:"relative",height:e.headerRowHeight*e.headerDepth+"px"}},[e._l(e.headerRows,function(t,i){return[e._l(t,function(t){return[e.isCellVisible(t,i,!0)?[n("div",{key:"fh-"+i+"-"+(t.column.key||t.column.title),staticClass:"eg-header-cell",class:{"eg-header-group":t.isGroup},style:e.getHeaderCellStyle(t,i,!0),on:{mousedown:function(n){return e.onColHeaderMouseDown(n,t)},dblclick:function(n){return e.onColHeaderDblClick(n,t)},contextmenu:function(n){return n.preventDefault(),e.onColHeaderContextMenu(n,t)}}},[t.isGroup?n("span",[e._v(e._s(t.column.title))]):e._t("header-"+t.column.key,function(){return[e._v("\n "+e._s(t.column.title)+"\n ")]},{column:t.column}),e._v(" "),t.isGroup?e._e():n("div",{staticClass:"eg-col-resize-handle",on:{mousedown:function(n){return n.stopPropagation(),e.startColResize(n,t.column._flatIndex)}}})],2)]:e._e()]})]})],2)]):e._e(),e._v(" "),n("div",{ref:"scrollColHeader",staticClass:"eg-scroll-col-header",style:e.scrollColHeaderStyle},[n("div",{staticClass:"eg-scroll-col-header-inner",style:{width:e.scrollContentWidth+"px",height:e.headerRowHeight*e.headerDepth+"px",position:"relative"}},[e._l(e.headerRows,function(t,i){return[e._l(t,function(t){return[e.isCellVisible(t,i,!1)?[n("div",{key:"sh-"+i+"-"+(t.column.key||t.column.title),staticClass:"eg-header-cell",class:{"eg-header-group":t.isGroup},style:e.getHeaderCellStyle(t,i,!1),on:{mousedown:function(n){return e.onColHeaderMouseDown(n,t)},dblclick:function(n){return e.onColHeaderDblClick(n,t)},contextmenu:function(n){return n.preventDefault(),e.onColHeaderContextMenu(n,t)}}},[t.isGroup?n("span",[e._v(e._s(t.column.title))]):e._t("header-"+t.column.key,function(){return[e._v("\n "+e._s(t.column.title)+"\n ")]},{column:t.column}),e._v(" "),t.isGroup?e._e():n("div",{staticClass:"eg-col-resize-handle",on:{mousedown:function(n){return n.stopPropagation(),e.startColResize(n,t.column._flatIndex)}}})],2)]:e._e()]})]})],2)]),e._v(" "),e.hasPinnedRight?n("div",{staticClass:"eg-pinned-right-header",style:e.pinnedRightHeaderStyle},[n("div",{style:{position:"relative",height:e.headerRowHeight*e.headerDepth+"px"}},[e._l(e.headerRows,function(t,i){return[e._l(t,function(t){return[e.isCellInZone(t,i,"right")?[n("div",{key:"prh-"+i+"-"+(t.column.key||t.column.title),staticClass:"eg-header-cell",class:{"eg-header-group":t.isGroup},style:e.getHeaderCellStyleForZone(t,i,"right"),on:{mousedown:function(n){return e.onColHeaderMouseDown(n,t)},dblclick:function(n){return e.onColHeaderDblClick(n,t)},contextmenu:function(n){return n.preventDefault(),e.onColHeaderContextMenu(n,t)}}},[t.isGroup?n("span",[e._v(e._s(t.column.title))]):e._t("header-"+t.column.key,function(){return[e._v("\n "+e._s(t.column.title)+"\n ")]},{column:t.column}),e._v(" "),t.isGroup?e._e():n("div",{staticClass:"eg-col-resize-handle",on:{mousedown:function(n){return n.stopPropagation(),e.startColResize(n,t.column._flatIndex)}}})],2)]:e._e()]})]})],2)]):e._e()]),e._v(" "),n("div",{staticClass:"eg-body-area",style:e.bodyAreaStyle},[n("div",{staticClass:"eg-row-header-col",style:e.rowHeaderColStyle},[n("div",{ref:"scrollRowHeaders",staticClass:"eg-scroll-row-headers",style:e.scrollRowHeadersStyle},[n("div",{style:{height:e.totalVirtualHeight+"px",position:"relative"}},[n("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(t){return[e.isRowHidden(t)?e._e():n("div",{key:"srh-"+t,staticClass:"eg-row-header-cell",style:e.getRowHeaderStyle(t),on:{mousedown:function(n){return e.onRowHeaderMouseDown(n,t)},contextmenu:function(n){return n.preventDefault(),e.onRowContextMenu(n,t)}}},[e._v("\n "+e._s(t+1)+"\n "),n("div",{staticClass:"eg-row-resize-handle",on:{mousedown:function(n){return n.stopPropagation(),e.startRowResize(n,t)}}})])]})],2)])])]),e._v(" "),e.rowGroups.length?n("div",{staticClass:"eg-row-outline-col",style:e.rowOutlineColStyle},[e._l(e.rowGroups,function(t){return[n("div",{key:"rg-"+t.startIndex+"-"+t.endIndex,staticClass:"eg-group-btn",class:{collapsed:e.isGroupCollapsed(t)},style:e.getRowGroupBtnStyle(t),on:{click:function(n){return e.toggleGroup(t)}}},[e._v(e._s(e.isGroupCollapsed(t)?"+":"−"))])]})],2):e._e(),e._v(" "),n("div",{ref:"scrollBody",staticClass:"eg-scroll-body",style:e.scrollBodyStyle,on:{scroll:e.onBodyScroll}},[n("div",{staticClass:"eg-scroll-body-inner",style:{width:e.scrollContentWidth+"px",height:e.totalVirtualHeight+"px",position:"relative"}},[e.getFrozenWidth()>0?n("div",{staticClass:"eg-frozen-col-body",style:e.frozenColBodyStyle},[n("div",{style:{height:e.totalVirtualHeight+"px",position:"relative"}},[n("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(t){return[n("transition",{key:"tr-fc-"+t,attrs:{name:e._rowTransitionName}},[e.isRowHidden(t)?e._e():n("div",{key:"fc-row-"+t,staticClass:"eg-row",style:e.getRowStyle(t)},[e._l(e.leafCols,function(i,o){return[e.isColumnPinnedLeft(o)&&!e.isColHidden(o)?[n("div",{key:"fc-cell-"+t+"-"+o,staticClass:"eg-cell",class:e.getCellClasses(t,o),style:e.getCellStyle(t,o),on:{mousedown:function(n){return e.onCellMouseDown(n,t,o)},mouseover:function(n){return e.onCellMouseOver(n,t,o)},mouseup:e.onCellMouseUp,dblclick:function(n){return e.onCellDblClick(t,o)},contextmenu:function(n){return n.preventDefault(),e.onCellContextMenu(n,t,o)}}},[e.isMergedAway(t,o)?n("div",{staticStyle:{display:"none"}}):e.isEditing(t,o)?[e.hasEditSlot(i)?e._t("edit-"+i.key,null,{row:e._dataRaw[t],rowIndex:t,column:i,value:e.getCellValue(t,o),done:function(t){return e.commitEditSlot(t)},cancel:e.cancelEdit}):n("input",{directives:[{name:"model",rawName:"v-model",value:e.editValue,expression:"editValue"}],ref:"editInput",refInFor:!0,staticClass:"eg-cell-input",domProps:{value:e.editValue},on:{blur:e.commitEdit,keydown:function(t){return t.stopPropagation(),e.onEditKeyDown.apply(null,arguments)},input:function(t){t.target.composing||(e.editValue=t.target.value)}}})]:"checkbox"===i.type?[n("label",{staticClass:"eg-checkbox",class:{"eg-checkbox--disabled":!e.isCellEditable(t,o)}},[n("input",{staticClass:"eg-checkbox-input",attrs:{type:"checkbox",disabled:!e.isCellEditable(t,o)},domProps:{checked:e.isCheckboxChecked(t,o)},on:{change:function(n){return e.onCheckboxChange(n,t,o)}}}),e._v(" "),n("span",{staticClass:"eg-checkbox-box"})])]:[e._t("cell-"+i.key,function(){return[e._v("\n "+e._s(e.isSelectLikeCol(i)?e.getSelectLabel(o,e.getCellValue(t,o)):e.getCellValue(t,o))+"\n ")]},{row:e._dataRaw[t],rowIndex:t,column:i,value:e.getCellValue(t,o)})]],2)]:e._e()]})],2)])]})],2)])]):e._e(),e._v(" "),n("div",{staticClass:"eg-main-cells",style:e.mainCellsStyle},[n("div",{style:{height:e.totalVirtualHeight+"px",position:"relative",width:e.scrollableContentWidth+"px"}},[n("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(t){return[n("transition",{key:"tr-m-"+t,attrs:{name:e._rowTransitionName}},[e.isRowHidden(t)?e._e():n("div",{key:"row-"+t,staticClass:"eg-row",style:e.getRowStyle(t),attrs:{"data-row":t}},[e._l(e.leafCols,function(i,o){return[e.isColumnScrollable(o)&&!e.isColHidden(o)?[n("div",{key:"cell-"+t+"-"+o,staticClass:"eg-cell",class:e.getCellClasses(t,o),style:e.getCellStyle(t,o),on:{mousedown:function(n){return e.onCellMouseDown(n,t,o)},mouseover:function(n){return e.onCellMouseOver(n,t,o)},mouseup:e.onCellMouseUp,dblclick:function(n){return e.onCellDblClick(t,o)},contextmenu:function(n){return n.preventDefault(),e.onCellContextMenu(n,t,o)}}},[e.isMergedAway(t,o)?n("div",{staticStyle:{display:"none"}}):e.isEditing(t,o)?[e.hasEditSlot(i)?e._t("edit-"+i.key,null,{row:e._dataRaw[t],rowIndex:t,column:i,value:e.getCellValue(t,o),done:function(t){return e.commitEditSlot(t)},cancel:e.cancelEdit}):n("input",{directives:[{name:"model",rawName:"v-model",value:e.editValue,expression:"editValue"}],ref:"editInput",refInFor:!0,staticClass:"eg-cell-input",domProps:{value:e.editValue},on:{blur:e.commitEdit,keydown:function(t){return t.stopPropagation(),e.onEditKeyDown.apply(null,arguments)},input:function(t){t.target.composing||(e.editValue=t.target.value)}}})]:"checkbox"===i.type?[n("label",{staticClass:"eg-checkbox",class:{"eg-checkbox--disabled":!e.isCellEditable(t,o)}},[n("input",{staticClass:"eg-checkbox-input",attrs:{type:"checkbox",disabled:!e.isCellEditable(t,o)},domProps:{checked:e.isCheckboxChecked(t,o)},on:{change:function(n){return e.onCheckboxChange(n,t,o)}}}),e._v(" "),n("span",{staticClass:"eg-checkbox-box"})])]:[e._t("cell-"+i.key,function(){return[e._v("\n "+e._s(e.isSelectLikeCol(i)?e.getSelectLabel(o,e.getCellValue(t,o)):e.getCellValue(t,o))+"\n ")]},{row:e._dataRaw[t],rowIndex:t,column:i,value:e.getCellValue(t,o)})]],2)]:e._e()]})],2)])]})],2)])]),e._v(" "),e.hasPinnedRight?n("div",{staticClass:"eg-pinned-right-body",style:e.pinnedRightBodyStyle},[n("div",{style:{height:e.totalVirtualHeight+"px",position:"relative"}},[n("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(t){return[n("transition",{key:"tr-pr-"+t,attrs:{name:e._rowTransitionName}},[e.isRowHidden(t)?e._e():n("div",{key:"pr-row-"+t,staticClass:"eg-row",style:e.getRowStyle(t)},[e._l(e.pinnedRightCols,function(i){return[n("div",{key:"pr-cell-"+t+"-"+i._flatIndex,staticClass:"eg-cell",class:e.getCellClasses(t,i._flatIndex),style:e.getCellStyle(t,i._flatIndex),on:{mousedown:function(n){return e.onCellMouseDown(n,t,i._flatIndex)},mouseover:function(n){return e.onCellMouseOver(n,t,i._flatIndex)},mouseup:e.onCellMouseUp,dblclick:function(n){return e.onCellDblClick(t,i._flatIndex)},contextmenu:function(n){return n.preventDefault(),e.onCellContextMenu(n,t,i._flatIndex)}}},[e.isMergedAway(t,i._flatIndex)?n("div",{staticStyle:{display:"none"}}):e.isEditing(t,i._flatIndex)?[e.hasEditSlot(i)?e._t("edit-"+i.key,null,{row:e._dataRaw[t],rowIndex:t,column:i,value:e.getCellValue(t,i._flatIndex),done:function(t){return e.commitEditSlot(t)},cancel:e.cancelEdit}):n("input",{directives:[{name:"model",rawName:"v-model",value:e.editValue,expression:"editValue"}],ref:"editInput",refInFor:!0,staticClass:"eg-cell-input",domProps:{value:e.editValue},on:{blur:e.commitEdit,keydown:function(t){return t.stopPropagation(),e.onEditKeyDown.apply(null,arguments)},input:function(t){t.target.composing||(e.editValue=t.target.value)}}})]:"checkbox"===i.type?[n("label",{staticClass:"eg-checkbox",class:{"eg-checkbox--disabled":!e.isCellEditable(t,i._flatIndex)}},[n("input",{staticClass:"eg-checkbox-input",attrs:{type:"checkbox",disabled:!e.isCellEditable(t,i._flatIndex)},domProps:{checked:e.isCheckboxChecked(t,i._flatIndex)},on:{change:function(n){return e.onCheckboxChange(n,t,i._flatIndex)}}}),e._v(" "),n("span",{staticClass:"eg-checkbox-box"})])]:[e._t("cell-"+i.key,function(){return[e._v("\n "+e._s(e.isSelectLikeCol(i)?e.getSelectLabel(i._flatIndex,e.getCellValue(t,i._flatIndex)):e.getCellValue(t,i._flatIndex))+"\n ")]},{row:e._dataRaw[t],rowIndex:t,column:i,value:e.getCellValue(t,i._flatIndex)})]],2)]})],2)])]})],2)])]):e._e(),e._v(" "),e.fillHandleVisible?n("div",{staticClass:"eg-fill-handle",style:e.fillHandleStyle,on:{mousedown:function(t){return t.stopPropagation(),e.startFillDrag.apply(null,arguments)}}}):e._e(),e._v(" "),e.dragMove.active?n("div",{staticClass:"eg-drag-ghost",style:e.dragGhostStyle}):e._e()])]),e._v(" "),e.colGroups.length?n("div",{staticClass:"eg-col-outline-row",style:e.colOutlineRowStyle},[e._l(e.colGroups,function(t){return[n("div",{key:"cg-"+t.startIndex+"-"+t.endIndex,staticClass:"eg-group-btn",class:{collapsed:e.isGroupCollapsed(t)},style:e.getColGroupBtnStyle(t),on:{click:function(n){return e.toggleGroup(t)}}},[e._v(e._s(e.isGroupCollapsed(t)?"+":"−"))])]})],2):e._e()]),e._v(" "),e.hasFooter?n("div",{staticClass:"eg-footer-area",style:e.footerAreaStyle},[n("div",{staticClass:"eg-footer-label",style:e.footerLabelStyle},[e._v(e._s(e.t.total))]),e._v(" "),e.getFrozenWidth()>0?n("div",{staticClass:"eg-frozen-col-footer",style:e.frozenColFooterStyle},[n("div",{style:{position:"relative",height:e.footerRowHeight+"px"}},[e._l(e.leafCols,function(t,i){return[e.isColumnPinnedLeft(i)&&!e.isColHidden(i)?[n("div",{key:"ff-"+i,staticClass:"eg-footer-cell",style:e.getFooterCellStyle(i)},[e._v("\n "+e._s(e.getColumnTotalDisplay(i))+"\n ")])]:e._e()]})],2)]):e._e(),e._v(" "),n("div",{ref:"scrollFooter",staticClass:"eg-scroll-col-footer",style:e.scrollColHeaderStyle},[n("div",{staticClass:"eg-scroll-col-footer-inner",style:{width:e.scrollContentWidth+"px",height:e.footerRowHeight+"px",position:"relative"}},[e._l(e.leafCols,function(t,i){return[e.isColumnScrollable(i)&&!e.isColHidden(i)?[n("div",{key:"sf-"+i,staticClass:"eg-footer-cell",style:e.getFooterCellStyle(i)},[e._v("\n "+e._s(e.getColumnTotalDisplay(i))+"\n ")])]:e._e()]})],2)]),e._v(" "),e.hasPinnedRight?n("div",{staticClass:"eg-pinned-right-footer",style:e.pinnedRightFooterStyle},[n("div",{style:{position:"relative",height:e.footerRowHeight+"px"}},e._l(e.pinnedRightCols,function(t){return n("div",{key:"prf-"+t._flatIndex,staticClass:"eg-footer-cell",style:e.getFooterCellStyle(t._flatIndex)},[e._v(e._s(e.getColumnTotalDisplay(t._flatIndex)))])}),0)]):e._e()]):e._e(),e._v(" "),e.resizing.active?n("div",{staticClass:"eg-resize-line",style:e.resizeLineStyle}):e._e()]),e._v(" "),e.importState.active?n("div",{staticClass:"eg-import-overlay"},[n("div",{staticClass:"eg-import-dialog"},[n("div",{staticClass:"eg-import-title"},[e._v("Fayl import qilinmoqda")]),e._v(" "),n("div",{staticClass:"eg-import-bar-wrap"},[n("div",{staticClass:"eg-import-bar",style:{width:e.importState.percent+"%"}})]),e._v(" "),n("div",{staticClass:"eg-import-msg"},[e._v(e._s(e.importState.message))]),e._v(" "),n("div",{staticClass:"eg-import-pct"},[e._v(e._s(e.importState.percent)+"%")])])]):e._e(),e._v(" "),e.exportState.active?n("div",{staticClass:"eg-import-overlay"},[e._m(0)]):e._e(),e._v(" "),e.selectDropdown?n("div",{staticClass:"eg-select-dropdown",style:e.dropdownStyle,on:{mousedown:function(e){e.preventDefault()}}},[e.selectDropdown.enableSearch?n("div",{staticClass:"eg-select-search",on:{mousedown:function(e){e.stopPropagation()}}},[n("input",{ref:"dropdownSearch",staticClass:"eg-select-search-input",attrs:{placeholder:"Qidirish...",autocomplete:"off",autocorrect:"off",autocapitalize:"off",spellcheck:"false"},domProps:{value:e.selectDropdown.searchQuery},on:{input:e.onDropdownSearch,keydown:function(t){return t.stopPropagation(),e.onDropdownSearchKeyDown.apply(null,arguments)}}})]):e._e(),e._v(" "),e._l(e.filteredSelectOptions,function(t,i){return n("div",{key:null!=t.id?t.id:t.value,staticClass:"eg-select-option",class:{"eg-select-option--active":t.value===e.selectDropdown.currentValue,"eg-select-option--highlighted":i===e.selectDropdown.highlight},style:e.selectDropdown.tree&&!e.selectDropdown.searchQuery?{paddingLeft:12+16*t.depth+"px"}:null,on:{mousedown:function(n){return n.preventDefault(),e.commitSelectValue(t.value)},mouseover:function(t){e.selectDropdown.highlight=i}}},[e.selectDropdown.tree&&!e.selectDropdown.searchQuery?n("span",{staticClass:"eg-tree-toggle",class:{"eg-tree-toggle--leaf":!t.hasChildren},on:{mousedown:function(t){return t.preventDefault(),t.stopPropagation(),e.toggleTreeNode(i)}}},[e._v(e._s(t.hasChildren?t.expanded?"▾":"▸":""))]):e._e(),e._v(" "),t.color?n("span",{staticClass:"eg-select-dot",style:{background:t.color}}):e._e(),e._v("\n "+e._s(t.label)+"\n ")])})],2):e._e(),e._v(" "),e.invalidCell&&e.invalidCell.rect?n("div",{staticClass:"eg-validation-tooltip",style:e.invalidTooltipStyle},[e._v(e._s(e.invalidCell.message))]):e._e()])};O._withStripped=!0;const P=L({render:O,staticRenderFns:[function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"eg-import-dialog"},[n("div",{staticClass:"eg-import-title"},[e._v("Fayl export qilinmoqda...")]),e._v(" "),n("div",{staticClass:"eg-export-spinner"})])}]},function(e){e&&e("data-v-10cb7c27_0",{source:"\n.eg-wrapper[data-v-10cb7c27] {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-sizing: border-box;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n}\n.eg-container[data-v-10cb7c27] {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: auto;\n position: relative;\n}\n.eg-header-area[data-v-10cb7c27] {\n display: flex;\n flex-shrink: 0;\n box-shadow: 0 2px 0 0 #d0d0d0;\n position: relative;\n z-index: 31;\n}\n.eg-header-cell[data-v-10cb7c27] {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n border-right: 1px solid #ccc;\n border-bottom: 1px solid #ccc;\n background: #f0f0f0;\n font-size: 12px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n position: absolute;\n user-select: none;\n cursor: pointer;\n}\n.eg-header-cell[data-v-10cb7c27]:hover { background: #e0e0e0;\n}\n.eg-header-group[data-v-10cb7c27] { background: #e8eaf6;\n}\n.eg-col-resize-handle[data-v-10cb7c27] {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n width: 5px;\n cursor: col-resize;\n z-index: 5;\n}\n.eg-body-area[data-v-10cb7c27] {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n/* ===== FOOTER AREA (column totals) ===== */\n.eg-footer-area[data-v-10cb7c27] {\n border-top: 1px solid #ccc;\n}\n.eg-frozen-col-footer[data-v-10cb7c27],\n.eg-scroll-col-footer[data-v-10cb7c27],\n.eg-pinned-right-footer[data-v-10cb7c27] {\n position: relative;\n}\n.eg-footer-cell[data-v-10cb7c27] {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n font-weight: 600;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: #f8f9fa;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n.eg-row[data-v-10cb7c27] {\n position: relative;\n}\n.eg-cell[data-v-10cb7c27] {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n border-bottom: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: white;\n cursor: default;\n display: flex;\n align-items: center;\n}\n.eg-cell-active[data-v-10cb7c27] {\n outline: 2px solid #1a73e8 !important;\n outline-offset: -2px;\n z-index: 3;\n}\n.eg-cell-selected[data-v-10cb7c27] {\n background: #e3f2fd !important;\n}\n.eg-cell-fill-preview[data-v-10cb7c27] {\n background: #bbdefb !important;\n outline: 1px dashed #1565c0;\n outline-offset: -1px;\n}\n.eg-cell-copy[data-v-10cb7c27] {\n outline: 1px dashed #1a73e8;\n outline-offset: -1px;\n}\n.eg-cell-merged-hidden[data-v-10cb7c27] {\n display: none !important;\n}\n.eg-cell-invalid[data-v-10cb7c27] {\n outline: 2px solid #e53935 !important;\n outline-offset: -2px;\n z-index: 4;\n}\n.eg-cell-input[data-v-10cb7c27] {\n width: 100%;\n height: 100%;\n border: none;\n outline: none;\n background: white;\n font-size: 13px;\n font-family: inherit;\n padding: 0;\n}\n.eg-row-header-cell[data-v-10cb7c27] {\n box-sizing: border-box;\n background: #f0f0f0;\n border-bottom: 1px solid #e0e0e0;\n border-right: 2px solid #bbb;\n text-align: center;\n font-size: 11px;\n color: #666;\n cursor: pointer;\n user-select: none;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.eg-row-resize-handle[data-v-10cb7c27] {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 4px;\n cursor: row-resize;\n z-index: 5;\n}\n.eg-fill-handle[data-v-10cb7c27] {\n position: absolute;\n width: 6px;\n height: 6px;\n background: #1a73e8;\n border: 1px solid white;\n cursor: crosshair;\n z-index: 10;\n}\n.eg-context-menu[data-v-10cb7c27] {\n position: fixed;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n z-index: 9999;\n min-width: 160px;\n font-size: 13px;\n padding: 4px 0;\n}\n.eg-context-item[data-v-10cb7c27] {\n padding: 6px 16px;\n cursor: pointer;\n position: relative;\n}\n.eg-context-item[data-v-10cb7c27]:hover { background: #f0f0f0;\n}\n.eg-context-separator[data-v-10cb7c27] {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n.eg-context-submenu-parent[data-v-10cb7c27]::after { content: ' ▶'; float: right;\n}\n.eg-context-submenu[data-v-10cb7c27] {\n display: none;\n position: absolute;\n left: 100%;\n top: -4px;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n min-width: 160px;\n padding: 4px 0;\n z-index: 10000;\n}\n.eg-context-submenu-parent:hover .eg-context-submenu[data-v-10cb7c27] { display: block;\n}\n.eg-group-btn[data-v-10cb7c27] {\n position: absolute;\n width: 16px;\n height: 16px;\n line-height: 14px;\n text-align: center;\n font-size: 10px;\n cursor: pointer;\n background: #e8eaf6;\n border: 1px solid #9fa8da;\n border-radius: 2px;\n z-index: 5;\n user-select: none;\n}\n.eg-group-btn.collapsed[data-v-10cb7c27] { background: #c5cae9;\n}\n.eg-drag-ghost[data-v-10cb7c27] {\n position: absolute;\n border: 2px dashed #1a73e8;\n pointer-events: none;\n z-index: 20;\n}\n.eg-resize-line[data-v-10cb7c27] {\n pointer-events: none;\n z-index: 9999;\n}\n\n/* Pin left separator shadow */\n.eg-frozen-col-body[data-v-10cb7c27]::after {\n content: '';\n position: absolute;\n top: 0;\n right: -4px;\n width: 4px;\n height: 100%;\n background: linear-gradient(to right, rgba(0,0,0,0.1), transparent);\n pointer-events: none;\n}\n.eg-pinned-right-body[data-v-10cb7c27] {\n position: sticky;\n right: 0;\n z-index: 10;\n background: white;\n flex-shrink: 0;\n}\n.eg-pinned-right-body[data-v-10cb7c27]::before {\n content: '';\n position: absolute;\n top: 0;\n left: -4px;\n bottom: 0;\n width: 4px;\n background: linear-gradient(to left, rgba(0,0,0,0.10), transparent);\n pointer-events: none;\n}\n.eg-pinned-right-header[data-v-10cb7c27] {\n overflow: hidden;\n flex-shrink: 0;\n border-left: 2px solid #bbb;\n background: #f0f0f0;\n}\n.eg-import-overlay[data-v-10cb7c27] {\n position: absolute;\n inset: 0;\n background: rgba(255, 255, 255, 0.85);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n border-radius: 4px;\n}\n.eg-import-dialog[data-v-10cb7c27] {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 24px 32px;\n min-width: 280px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.15);\n text-align: center;\n}\n.eg-import-title[data-v-10cb7c27] {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n margin-bottom: 16px;\n}\n.eg-import-bar-wrap[data-v-10cb7c27] {\n height: 8px;\n background: #e0e0e0;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 10px;\n}\n.eg-import-bar[data-v-10cb7c27] {\n height: 100%;\n background: linear-gradient(to right, #1a73e8, #4caf50);\n border-radius: 4px;\n transition: width 0.15s ease;\n}\n.eg-import-msg[data-v-10cb7c27] {\n font-size: 12px;\n color: #666;\n margin-bottom: 4px;\n}\n.eg-import-pct[data-v-10cb7c27] {\n font-size: 13px;\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-export-spinner[data-v-10cb7c27] {\n width: 32px;\n height: 32px;\n margin: 12px auto 0;\n border: 3px solid #e0e0e0;\n border-top-color: #1a73e8;\n border-radius: 50%;\n animation: eg-spin-data-v-10cb7c27 0.8s linear infinite;\n}\n@keyframes eg-spin-data-v-10cb7c27 {\nto { transform: rotate(360deg);\n}\n}\n\n/* ===== READONLY CELL ===== */\n.eg-cell-readonly[data-v-10cb7c27] {\n background: #f8f8f8;\n cursor: default;\n}\n.eg-cell-readonly.eg-cell-selected[data-v-10cb7c27] {\n background: #dce8f5;\n}\n.eg-cell-readonly.eg-cell-active[data-v-10cb7c27] {\n outline-color: #aaa;\n}\n\n/* ===== SELECT DROPDOWN ===== */\n.eg-cell-select[data-v-10cb7c27] {\n cursor: pointer;\n}\n.eg-cell-select[data-v-10cb7c27]::after {\n content: '▾';\n position: absolute;\n right: 4px;\n top: 50%;\n transform: translateY(-50%);\n color: #aaa;\n font-size: 11px;\n pointer-events: none;\n line-height: 1;\n}\n\n/* ===== CHECKBOX CELL ===== */\n.eg-cell-checkbox[data-v-10cb7c27] {\n justify-content: center;\n padding: 0 2px;\n}\n.eg-checkbox[data-v-10cb7c27] {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n cursor: pointer;\n}\n.eg-checkbox-input[data-v-10cb7c27] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.eg-checkbox-box[data-v-10cb7c27] {\n position: relative;\n display: block;\n width: 19px;\n height: 19px;\n box-sizing: border-box;\n background-color: #fff;\n background-repeat: no-repeat;\n background-position: center;\n background-size: 0 0;\n border: 1.5px solid #c2c8d1;\n border-radius: 4px;\n transition: background-color .12s ease, border-color .12s ease, background-size .12s ease;\n}\n.eg-checkbox:hover .eg-checkbox-box[data-v-10cb7c27] {\n border-color: #1a73e8;\n}\n.eg-checkbox-input:checked + .eg-checkbox-box[data-v-10cb7c27] {\n background-color: #1a73e8;\n background-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTMuNSA4LjNsMyAzIDYtNi42IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==\");\n background-size: 70% 70%;\n border-color: #1a73e8;\n}\n.eg-checkbox-input:focus-visible + .eg-checkbox-box[data-v-10cb7c27] {\n box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.2);\n}\n.eg-checkbox--disabled[data-v-10cb7c27],\n.eg-checkbox--disabled .eg-checkbox-input[data-v-10cb7c27] {\n cursor: default;\n}\n.eg-checkbox-input:disabled + .eg-checkbox-box[data-v-10cb7c27] {\n background-color: #f0f1f3;\n border-color: #dadde2;\n}\n.eg-checkbox-input:disabled:checked + .eg-checkbox-box[data-v-10cb7c27] {\n background-color: #b9c2ce;\n border-color: #b9c2ce;\n}\n.eg-select-dropdown[data-v-10cb7c27] {\n background: white;\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.15);\n overflow-y: auto;\n max-height: 320px;\n padding: 4px 0;\n}\n.eg-select-search[data-v-10cb7c27] {\n padding: 6px 8px;\n border-bottom: 1px solid #ebebeb;\n position: sticky;\n top: 0;\n background: #fff;\n z-index: 1;\n}\n.eg-select-search-input[data-v-10cb7c27] {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n padding: 4px 8px;\n font-size: 13px;\n outline: none;\n color: #333;\n background: #fff;\n font-family: inherit;\n}\n.eg-select-search-input[data-v-10cb7c27]:focus {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26,115,232,0.15);\n}\n.eg-select-option[data-v-10cb7c27] {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 12px;\n font-size: 13px;\n cursor: pointer;\n white-space: normal;\n word-break: break-word;\n user-select: none;\n color: #333;\n}\n.eg-select-option[data-v-10cb7c27]:hover,\n.eg-select-option--highlighted[data-v-10cb7c27] {\n background: #f0f4ff;\n}\n.eg-select-option--active[data-v-10cb7c27] {\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-select-option--active.eg-select-option--highlighted[data-v-10cb7c27],\n.eg-select-option--active[data-v-10cb7c27]:hover {\n background: #e8f0fe;\n}\n.eg-validation-tooltip[data-v-10cb7c27] {\n background: #fff0f0;\n border: 1px solid #e53935;\n color: #c62828;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n white-space: nowrap;\n pointer-events: none;\n}\n.eg-select-dot[data-v-10cb7c27] {\n width: 9px;\n height: 9px;\n border-radius: 50%;\n flex-shrink: 0;\n display: inline-block;\n}\n.eg-tree-toggle[data-v-10cb7c27] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n font-size: 10px;\n color: #888;\n margin-top: 1px;\n}\n.eg-tree-toggle--leaf[data-v-10cb7c27] {\n visibility: hidden;\n}\n\n/* ===== ROW TRANSITION ===== */\n.eg-row-enter-active[data-v-10cb7c27],\n.eg-row-leave-active[data-v-10cb7c27] {\n transition: opacity 0.18s ease, transform 0.18s ease;\n}\n.eg-row-enter[data-v-10cb7c27],\n.eg-row-leave-to[data-v-10cb7c27] {\n opacity: 0;\n transform: translateY(-4px);\n}\n\n/* ===== SIZE VARIANTS ===== */\n\n/* small */\n.eg-size-small .eg-cell[data-v-10cb7c27] {\n font-size: 12px;\n padding: 0 3px;\n}\n.eg-size-small .eg-cell-input[data-v-10cb7c27] {\n font-size: 12px;\n}\n.eg-size-small .eg-header-cell[data-v-10cb7c27] {\n font-size: 11px;\n}\n.eg-size-small .eg-footer-cell[data-v-10cb7c27] {\n font-size: 12px;\n}\n.eg-size-small .eg-row-header-cell[data-v-10cb7c27] {\n font-size: 10px;\n}\n.eg-size-small .eg-select-option[data-v-10cb7c27] {\n font-size: 12px;\n padding: 4px 10px;\n}\n.eg-size-small .eg-context-menu[data-v-10cb7c27],\n.eg-size-small .eg-context-item[data-v-10cb7c27] {\n font-size: 12px;\n}\n.eg-size-small .eg-context-item[data-v-10cb7c27] {\n padding: 4px 14px;\n}\n\n/* large */\n.eg-size-large .eg-cell[data-v-10cb7c27] {\n font-size: 14px;\n padding: 0 6px;\n}\n.eg-size-large .eg-cell-input[data-v-10cb7c27] {\n font-size: 14px;\n}\n.eg-size-large .eg-header-cell[data-v-10cb7c27] {\n font-size: 13px;\n}\n.eg-size-large .eg-footer-cell[data-v-10cb7c27] {\n font-size: 14px;\n}\n.eg-size-large .eg-row-header-cell[data-v-10cb7c27] {\n font-size: 12px;\n}\n.eg-size-large .eg-select-option[data-v-10cb7c27] {\n font-size: 14px;\n padding: 8px 14px;\n}\n.eg-size-large .eg-context-menu[data-v-10cb7c27],\n.eg-size-large .eg-context-item[data-v-10cb7c27] {\n font-size: 14px;\n}\n.eg-size-large .eg-context-item[data-v-10cb7c27] {\n padding: 8px 18px;\n}\n",map:{version:3,sources:["/Users/macmini/Projects/Realsoft/vue2-excel-grid/src/components/ExcelGrid.vue"],names:[],mappings:";AA0gHA;EACA,aAAA;EACA,sBAAA;EACA,gBAAA;EACA,sBAAA;EACA,sBAAA;EACA,kBAAA;EACA,6EAAA;AACA;AAEA;EACA,aAAA;EACA,sBAAA;EACA,OAAA;EACA,cAAA;EACA,kBAAA;AACA;AAEA;EACA,aAAA;EACA,cAAA;EACA,6BAAA;EACA,kBAAA;EACA,WAAA;AACA;AAEA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,sBAAA;EACA,4BAAA;EACA,6BAAA;EACA,mBAAA;EACA,eAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;EACA,iBAAA;EACA,eAAA;AACA;AAEA,yCAAA,mBAAA;AAAA;AACA,oCAAA,mBAAA;AAAA;AAEA;EACA,kBAAA;EACA,QAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,kBAAA;EACA,UAAA;AACA;AAEA;EACA,aAAA;EACA,OAAA;EACA,gBAAA;AACA;;AAEA,4CAAA;AACA;EACA,0BAAA;AACA;AAEA;;;EAGA,kBAAA;AACA;AAEA;EACA,sBAAA;EACA,+BAAA;EACA,gBAAA;EACA,mBAAA;EACA,eAAA;EACA,gBAAA;EACA,cAAA;EACA,kBAAA;EACA,MAAA;EACA,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;AACA;AAEA;EACA,kBAAA;AACA;AAEA;EACA,sBAAA;EACA,+BAAA;EACA,gCAAA;EACA,gBAAA;EACA,mBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,MAAA;EACA,iBAAA;EACA,eAAA;EACA,aAAA;EACA,mBAAA;AACA;AAEA;EACA,qCAAA;EACA,oBAAA;EACA,UAAA;AACA;AAEA;EACA,8BAAA;AACA;AAEA;EACA,8BAAA;EACA,2BAAA;EACA,oBAAA;AACA;AAEA;EACA,2BAAA;EACA,oBAAA;AACA;AAEA;EACA,wBAAA;AACA;AAEA;EACA,qCAAA;EACA,oBAAA;EACA,UAAA;AACA;AAEA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;EACA,eAAA;EACA,oBAAA;EACA,UAAA;AACA;AAEA;EACA,sBAAA;EACA,mBAAA;EACA,gCAAA;EACA,4BAAA;EACA,kBAAA;EACA,eAAA;EACA,WAAA;EACA,eAAA;EACA,iBAAA;EACA,kBAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;AACA;AAEA;EACA,kBAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,WAAA;EACA,kBAAA;EACA,UAAA;AACA;AAEA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,mBAAA;EACA,uBAAA;EACA,iBAAA;EACA,WAAA;AACA;AAEA;EACA,eAAA;EACA,iBAAA;EACA,sBAAA;EACA,kBAAA;EACA,yCAAA;EACA,aAAA;EACA,gBAAA;EACA,eAAA;EACA,cAAA;AACA;AAEA;EACA,iBAAA;EACA,eAAA;EACA,kBAAA;AACA;AAEA,0CAAA,mBAAA;AAAA;AAEA;EACA,WAAA;EACA,mBAAA;EACA,aAAA;AACA;AAEA,qDAAA,aAAA,EAAA,YAAA;AAAA;AAEA;EACA,aAAA;EACA,kBAAA;EACA,UAAA;EACA,SAAA;EACA,iBAAA;EACA,sBAAA;EACA,kBAAA;EACA,yCAAA;EACA,gBAAA;EACA,cAAA;EACA,cAAA;AACA;AAEA,wEAAA,cAAA;AAAA;AAEA;EACA,kBAAA;EACA,WAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EACA,UAAA;EACA,iBAAA;AACA;AAEA,2CAAA,mBAAA;AAAA;AAEA;EACA,kBAAA;EACA,0BAAA;EACA,oBAAA;EACA,WAAA;AACA;AAEA;EACA,oBAAA;EACA,aAAA;AACA;;AAEA,8BAAA;AACA;EACA,WAAA;EACA,kBAAA;EACA,MAAA;EACA,WAAA;EACA,UAAA;EACA,YAAA;EACA,mEAAA;EACA,oBAAA;AACA;AAEA;EACA,gBAAA;EACA,QAAA;EACA,WAAA;EACA,iBAAA;EACA,cAAA;AACA;AAEA;EACA,WAAA;EACA,kBAAA;EACA,MAAA;EACA,UAAA;EACA,SAAA;EACA,UAAA;EACA,mEAAA;EACA,oBAAA;AACA;AAEA;EACA,gBAAA;EACA,cAAA;EACA,2BAAA;EACA,mBAAA;AACA;AAEA;EACA,kBAAA;EACA,QAAA;EACA,qCAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,YAAA;EACA,kBAAA;AACA;AAEA;EACA,iBAAA;EACA,yBAAA;EACA,kBAAA;EACA,kBAAA;EACA,gBAAA;EACA,uCAAA;EACA,kBAAA;AACA;AAEA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,mBAAA;AACA;AAEA;EACA,WAAA;EACA,mBAAA;EACA,kBAAA;EACA,gBAAA;EACA,mBAAA;AACA;AAEA;EACA,YAAA;EACA,uDAAA;EACA,kBAAA;EACA,4BAAA;AACA;AAEA;EACA,eAAA;EACA,WAAA;EACA,kBAAA;AACA;AAEA;EACA,eAAA;EACA,gBAAA;EACA,cAAA;AACA;AACA;EACA,WAAA;EACA,YAAA;EACA,mBAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;EACA,uDAAA;AACA;AACA;AACA,KAAA,yBAAA;AAAA;AACA;;AAEA,8BAAA;AACA;EACA,mBAAA;EACA,eAAA;AACA;AACA;EACA,mBAAA;AACA;AACA;EACA,mBAAA;AACA;;AAEA,gCAAA;AACA;EACA,eAAA;AACA;AACA;EACA,YAAA;EACA,kBAAA;EACA,UAAA;EACA,QAAA;EACA,2BAAA;EACA,WAAA;EACA,eAAA;EACA,oBAAA;EACA,cAAA;AACA;;AAEA,8BAAA;AACA;EACA,uBAAA;EACA,cAAA;AACA;AACA;EACA,kBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,WAAA;EACA,YAAA;EACA,eAAA;AACA;AACA;EACA,kBAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;EACA,UAAA;EACA,eAAA;AACA;AACA;EACA,kBAAA;EACA,cAAA;EACA,WAAA;EACA,YAAA;EACA,sBAAA;EACA,sBAAA;EACA,4BAAA;EACA,2BAAA;EACA,oBAAA;EACA,2BAAA;EACA,kBAAA;EACA,yFAAA;AACA;AACA;EACA,qBAAA;AACA;AACA;EACA,yBAAA;EACA,+SAAA;EACA,wBAAA;EACA,qBAAA;AACA;AACA;EACA,6CAAA;AACA;AACA;;EAEA,eAAA;AACA;AACA;EACA,yBAAA;EACA,qBAAA;AACA;AACA;EACA,yBAAA;EACA,qBAAA;AACA;AAEA;EACA,iBAAA;EACA,yBAAA;EACA,kBAAA;EACA,uCAAA;EACA,gBAAA;EACA,iBAAA;EACA,cAAA;AACA;AACA;EACA,gBAAA;EACA,gCAAA;EACA,gBAAA;EACA,MAAA;EACA,gBAAA;EACA,UAAA;AACA;AACA;EACA,WAAA;EACA,sBAAA;EACA,yBAAA;EACA,kBAAA;EACA,gBAAA;EACA,eAAA;EACA,aAAA;EACA,WAAA;EACA,gBAAA;EACA,oBAAA;AACA;AACA;EACA,qBAAA;EACA,2CAAA;AACA;AAEA;EACA,aAAA;EACA,uBAAA;EACA,QAAA;EACA,iBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,sBAAA;EACA,iBAAA;EACA,WAAA;AACA;AACA;;EAEA,mBAAA;AACA;AACA;EACA,gBAAA;EACA,cAAA;AACA;AACA;;EAEA,mBAAA;AACA;AAEA;EACA,mBAAA;EACA,yBAAA;EACA,cAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;EACA,yCAAA;EACA,mBAAA;EACA,oBAAA;AACA;AAEA;EACA,UAAA;EACA,WAAA;EACA,kBAAA;EACA,cAAA;EACA,qBAAA;AACA;AAEA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,eAAA;EACA,WAAA;EACA,eAAA;AACA;AACA;EACA,kBAAA;AACA;;AAEA,+BAAA;AACA;;EAEA,oDAAA;AACA;AACA;;EAEA,UAAA;EACA,2BAAA;AACA;;AAEA,8BAAA;;AAEA,UAAA;AACA;EACA,eAAA;EACA,cAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;EACA,iBAAA;AACA;AACA;;EAEA,eAAA;AACA;AACA;EACA,iBAAA;AACA;;AAEA,UAAA;AACA;EACA,eAAA;EACA,cAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;EACA,iBAAA;AACA;AACA;;EAEA,eAAA;AACA;AACA;EACA,iBAAA;AACA",file:"ExcelGrid.vue",sourcesContent:["<template>\n <div\n class=\"eg-wrapper\"\n :class=\"'eg-size-' + size\"\n :style=\"wrapperStyle\"\n tabindex=\"0\"\n @keydown=\"onKeyDown\"\n @keypress=\"onKeyPress\"\n ref=\"wrapper\"\n >\n \x3c!-- Context menu --\x3e\n <div\n v-if=\"contextMenu.visible\"\n class=\"eg-context-menu\"\n :style=\"{ top: contextMenu.y + 'px', left: contextMenu.x + 'px' }\"\n @click.stop\n >\n <div class=\"eg-context-item\" @click=\"ctxCopy\">{{ t.copy }}</div>\n <div class=\"eg-context-item\" @click=\"ctxCut\">{{ t.cut }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPaste\">{{ t.paste }}</div>\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item eg-context-submenu-parent\">\n {{ t.pasteSpecial }}\n <div class=\"eg-context-submenu\">\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('all')\">{{ t.pasteAll }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('values')\">{{ t.pasteValues }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('formats')\">{{ t.pasteFormats }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('all', true)\">{{ t.transpose }}</div>\n </div>\n </div>\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item\" @click=\"ctxMerge\">{{ t.mergeCells }}</div>\n <div class=\"eg-context-item\" @click=\"ctxUnmerge\">{{ t.unmergeCells }}</div>\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item\" @click=\"ctxHideRow\">{{ t.hideRows }}</div>\n <div class=\"eg-context-item\" @click=\"ctxHideColumn\">{{ t.hideColumns }}</div>\n <div class=\"eg-context-item\" @click=\"ctxUnhideRows\">{{ t.unhideRows }}</div>\n <div class=\"eg-context-item\" @click=\"ctxUnhideColumns\">{{ t.unhideColumns }}</div>\n <template v-if=\"contextMenu.row < 0 && contextMenu.col >= 0\">\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item\" @click=\"ctxPinLeft\">{{ t.pinLeft }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPinRight\">{{ t.pinRight }}</div>\n <div class=\"eg-context-item\" v-if=\"isColPinned(contextMenu.col)\" @click=\"ctxUnpin\">{{ t.unpin }}</div>\n </template>\n </div>\n\n \x3c!-- Scroll container --\x3e\n <div class=\"eg-container\" ref=\"container\" @scroll=\"onScroll\" @click=\"hideContextMenu\">\n\n \x3c!-- ===== HEADER AREA ===== --\x3e\n <div class=\"eg-header-area\" :style=\"headerAreaStyle\">\n\n \x3c!-- Corner cell --\x3e\n <div\n class=\"eg-corner\"\n :style=\"cornerStyle\"\n @click=\"selectAll\"\n ></div>\n\n \x3c!-- Row group outline gutter (left) --\x3e\n <div v-if=\"rowGroups.length\" class=\"eg-row-outline-gutter\" :style=\"rowOutlineGutterStyle\"></div>\n\n \x3c!-- Frozen columns header --\x3e\n <div v-if=\"getFrozenWidth() > 0\" class=\"eg-frozen-col-header\" :style=\"frozenColHeaderStyle\">\n <div :style=\"{ position: 'relative', height: (headerRowHeight * headerDepth) + 'px' }\">\n <template v-for=\"(row, rowIdx) in headerRows\">\n <template v-for=\"cell in row\">\n <template v-if=\"isCellVisible(cell, rowIdx, true)\">\n <div\n :key=\"'fh-' + rowIdx + '-' + (cell.column.key || cell.column.title)\"\n class=\"eg-header-cell\"\n :class=\"{ 'eg-header-group': cell.isGroup }\"\n :style=\"getHeaderCellStyle(cell, rowIdx, true)\"\n @mousedown=\"onColHeaderMouseDown($event, cell)\"\n @dblclick=\"onColHeaderDblClick($event, cell)\"\n @contextmenu.prevent=\"onColHeaderContextMenu($event, cell)\"\n >\n <slot v-if=\"!cell.isGroup\" :name=\"'header-' + cell.column.key\" :column=\"cell.column\">\n {{ cell.column.title }}\n </slot>\n <span v-else>{{ cell.column.title }}</span>\n <div\n v-if=\"!cell.isGroup\"\n class=\"eg-col-resize-handle\"\n @mousedown.stop=\"startColResize($event, cell.column._flatIndex)\"\n ></div>\n </div>\n </template>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Scrollable columns header --\x3e\n <div class=\"eg-scroll-col-header\" ref=\"scrollColHeader\" :style=\"scrollColHeaderStyle\">\n <div class=\"eg-scroll-col-header-inner\" :style=\"{ width: scrollContentWidth + 'px', height: (headerRowHeight * headerDepth) + 'px', position: 'relative' }\">\n <template v-for=\"(row, rowIdx) in headerRows\">\n <template v-for=\"cell in row\">\n <template v-if=\"isCellVisible(cell, rowIdx, false)\">\n <div\n :key=\"'sh-' + rowIdx + '-' + (cell.column.key || cell.column.title)\"\n class=\"eg-header-cell\"\n :class=\"{ 'eg-header-group': cell.isGroup }\"\n :style=\"getHeaderCellStyle(cell, rowIdx, false)\"\n @mousedown=\"onColHeaderMouseDown($event, cell)\"\n @dblclick=\"onColHeaderDblClick($event, cell)\"\n @contextmenu.prevent=\"onColHeaderContextMenu($event, cell)\"\n >\n <slot v-if=\"!cell.isGroup\" :name=\"'header-' + cell.column.key\" :column=\"cell.column\">\n {{ cell.column.title }}\n </slot>\n <span v-else>{{ cell.column.title }}</span>\n <div\n v-if=\"!cell.isGroup\"\n class=\"eg-col-resize-handle\"\n @mousedown.stop=\"startColResize($event, cell.column._flatIndex)\"\n ></div>\n </div>\n </template>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Pinned Right columns header --\x3e\n <div v-if=\"hasPinnedRight\" class=\"eg-pinned-right-header\" :style=\"pinnedRightHeaderStyle\">\n <div :style=\"{ position: 'relative', height: (headerRowHeight * headerDepth) + 'px' }\">\n <template v-for=\"(row, rowIdx) in headerRows\">\n <template v-for=\"cell in row\">\n <template v-if=\"isCellInZone(cell, rowIdx, 'right')\">\n <div\n :key=\"'prh-' + rowIdx + '-' + (cell.column.key || cell.column.title)\"\n class=\"eg-header-cell\"\n :class=\"{ 'eg-header-group': cell.isGroup }\"\n :style=\"getHeaderCellStyleForZone(cell, rowIdx, 'right')\"\n @mousedown=\"onColHeaderMouseDown($event, cell)\"\n @dblclick=\"onColHeaderDblClick($event, cell)\"\n @contextmenu.prevent=\"onColHeaderContextMenu($event, cell)\"\n >\n <slot v-if=\"!cell.isGroup\" :name=\"'header-' + cell.column.key\" :column=\"cell.column\">\n {{ cell.column.title }}\n </slot>\n <span v-else>{{ cell.column.title }}</span>\n <div\n v-if=\"!cell.isGroup\"\n class=\"eg-col-resize-handle\"\n @mousedown.stop=\"startColResize($event, cell.column._flatIndex)\"\n ></div>\n </div>\n </template>\n </template>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- ===== BODY AREA ===== --\x3e\n <div class=\"eg-body-area\" :style=\"bodyAreaStyle\">\n\n \x3c!-- Row number header column --\x3e\n <div class=\"eg-row-header-col\" :style=\"rowHeaderColStyle\">\n \x3c!-- Scrollable row headers --\x3e\n <div class=\"eg-scroll-row-headers\" :style=\"scrollRowHeadersStyle\" ref=\"scrollRowHeaders\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <div\n v-if=\"!isRowHidden(ri)\"\n :key=\"'srh-' + ri\"\n class=\"eg-row-header-cell\"\n :style=\"getRowHeaderStyle(ri)\"\n @mousedown=\"onRowHeaderMouseDown($event, ri)\"\n @contextmenu.prevent=\"onRowContextMenu($event, ri)\"\n >\n {{ ri + 1 }}\n <div class=\"eg-row-resize-handle\" @mousedown.stop=\"startRowResize($event, ri)\"></div>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Row group outline gutter (left side of rows) --\x3e\n <div v-if=\"rowGroups.length\" class=\"eg-row-outline-col\" :style=\"rowOutlineColStyle\">\n <template v-for=\"group in rowGroups\">\n <div\n :key=\"'rg-' + group.startIndex + '-' + group.endIndex\"\n class=\"eg-group-btn\"\n :class=\"{ collapsed: isGroupCollapsed(group) }\"\n :style=\"getRowGroupBtnStyle(group)\"\n @click=\"toggleGroup(group)\"\n >{{ isGroupCollapsed(group) ? '+' : '−' }}</div>\n </template>\n </div>\n\n \x3c!-- Main scroll body --\x3e\n <div class=\"eg-scroll-body\" ref=\"scrollBody\" :style=\"scrollBodyStyle\" @scroll=\"onBodyScroll\">\n <div class=\"eg-scroll-body-inner\" :style=\"{ width: scrollContentWidth + 'px', height: totalVirtualHeight + 'px', position: 'relative' }\">\n\n \x3c!-- Pinned left column body --\x3e\n <div v-if=\"getFrozenWidth() > 0\" class=\"eg-frozen-col-body\" :style=\"frozenColBodyStyle\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <transition :key=\"'tr-fc-' + ri\" :name=\"_rowTransitionName\">\n <div v-if=\"!isRowHidden(ri)\" :key=\"'fc-row-' + ri\" class=\"eg-row\" :style=\"getRowStyle(ri)\">\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnPinnedLeft(ci) && !isColHidden(ci)\">\n <div\n :key=\"'fc-cell-' + ri + '-' + ci\"\n class=\"eg-cell\"\n :class=\"getCellClasses(ri, ci)\"\n :style=\"getCellStyle(ri, ci)\"\n @mousedown=\"onCellMouseDown($event, ri, ci)\"\n @mouseover=\"onCellMouseOver($event, ri, ci)\"\n @mouseup=\"onCellMouseUp\"\n @dblclick=\"onCellDblClick(ri, ci)\"\n @contextmenu.prevent=\"onCellContextMenu($event, ri, ci)\"\n >\n <div v-if=\"isMergedAway(ri, ci)\" style=\"display:none\"></div>\n <template v-else-if=\"isEditing(ri, ci)\">\n <slot v-if=\"hasEditSlot(col)\" :name=\"'edit-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <input v-else ref=\"editInput\" class=\"eg-cell-input\" v-model=\"editValue\" @blur=\"commitEdit\" @keydown.stop=\"onEditKeyDown\" />\n </template>\n <template v-else-if=\"col.type === 'checkbox'\">\n <label class=\"eg-checkbox\" :class=\"{ 'eg-checkbox--disabled': !isCellEditable(ri, ci) }\">\n <input\n type=\"checkbox\"\n class=\"eg-checkbox-input\"\n :checked=\"isCheckboxChecked(ri, ci)\"\n :disabled=\"!isCellEditable(ri, ci)\"\n @change=\"onCheckboxChange($event, ri, ci)\"\n />\n <span class=\"eg-checkbox-box\"></span>\n </label>\n </template>\n <template v-else>\n <slot :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\">\n {{ isSelectLikeCol(col) ? getSelectLabel(ci, getCellValue(ri, ci)) : getCellValue(ri, ci) }}\n </slot>\n </template>\n </div>\n </template>\n </template>\n </div>\n </transition>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- Main data cells --\x3e\n <div class=\"eg-main-cells\" :style=\"mainCellsStyle\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative', width: scrollableContentWidth + 'px' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <transition :key=\"'tr-m-' + ri\" :name=\"_rowTransitionName\">\n <div\n v-if=\"!isRowHidden(ri)\"\n :key=\"'row-' + ri\"\n class=\"eg-row\"\n :style=\"getRowStyle(ri)\"\n :data-row=\"ri\"\n >\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnScrollable(ci) && !isColHidden(ci)\">\n <div\n :key=\"'cell-' + ri + '-' + ci\"\n class=\"eg-cell\"\n :class=\"getCellClasses(ri, ci)\"\n :style=\"getCellStyle(ri, ci)\"\n @mousedown=\"onCellMouseDown($event, ri, ci)\"\n @mouseover=\"onCellMouseOver($event, ri, ci)\"\n @mouseup=\"onCellMouseUp\"\n @dblclick=\"onCellDblClick(ri, ci)\"\n @contextmenu.prevent=\"onCellContextMenu($event, ri, ci)\"\n >\n <div v-if=\"isMergedAway(ri, ci)\" style=\"display:none\"></div>\n <template v-else-if=\"isEditing(ri, ci)\">\n <slot v-if=\"hasEditSlot(col)\" :name=\"'edit-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <input\n v-else\n ref=\"editInput\"\n class=\"eg-cell-input\"\n v-model=\"editValue\"\n @blur=\"commitEdit\"\n @keydown.stop=\"onEditKeyDown\"\n />\n </template>\n <template v-else-if=\"col.type === 'checkbox'\">\n <label class=\"eg-checkbox\" :class=\"{ 'eg-checkbox--disabled': !isCellEditable(ri, ci) }\">\n <input\n type=\"checkbox\"\n class=\"eg-checkbox-input\"\n :checked=\"isCheckboxChecked(ri, ci)\"\n :disabled=\"!isCellEditable(ri, ci)\"\n @change=\"onCheckboxChange($event, ri, ci)\"\n />\n <span class=\"eg-checkbox-box\"></span>\n </label>\n </template>\n <template v-else>\n <slot :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\">\n {{ isSelectLikeCol(col) ? getSelectLabel(ci, getCellValue(ri, ci)) : getCellValue(ri, ci) }}\n </slot>\n </template>\n </div>\n </template>\n </template>\n </div>\n </transition>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- Pinned Right column body --\x3e\n <div v-if=\"hasPinnedRight\" class=\"eg-pinned-right-body\" :style=\"pinnedRightBodyStyle\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <transition :key=\"'tr-pr-' + ri\" :name=\"_rowTransitionName\">\n <div v-if=\"!isRowHidden(ri)\" :key=\"'pr-row-' + ri\" class=\"eg-row\" :style=\"getRowStyle(ri)\">\n <template v-for=\"col in pinnedRightCols\">\n <div\n :key=\"'pr-cell-' + ri + '-' + col._flatIndex\"\n class=\"eg-cell\"\n :class=\"getCellClasses(ri, col._flatIndex)\"\n :style=\"getCellStyle(ri, col._flatIndex)\"\n @mousedown=\"onCellMouseDown($event, ri, col._flatIndex)\"\n @mouseover=\"onCellMouseOver($event, ri, col._flatIndex)\"\n @mouseup=\"onCellMouseUp\"\n @dblclick=\"onCellDblClick(ri, col._flatIndex)\"\n @contextmenu.prevent=\"onCellContextMenu($event, ri, col._flatIndex)\"\n >\n <div v-if=\"isMergedAway(ri, col._flatIndex)\" style=\"display:none\"></div>\n <template v-else-if=\"isEditing(ri, col._flatIndex)\">\n <slot v-if=\"hasEditSlot(col)\" :name=\"'edit-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, col._flatIndex)\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <input v-else ref=\"editInput\" class=\"eg-cell-input\" v-model=\"editValue\" @blur=\"commitEdit\" @keydown.stop=\"onEditKeyDown\" />\n </template>\n <template v-else-if=\"col.type === 'checkbox'\">\n <label class=\"eg-checkbox\" :class=\"{ 'eg-checkbox--disabled': !isCellEditable(ri, col._flatIndex) }\">\n <input\n type=\"checkbox\"\n class=\"eg-checkbox-input\"\n :checked=\"isCheckboxChecked(ri, col._flatIndex)\"\n :disabled=\"!isCellEditable(ri, col._flatIndex)\"\n @change=\"onCheckboxChange($event, ri, col._flatIndex)\"\n />\n <span class=\"eg-checkbox-box\"></span>\n </label>\n </template>\n <template v-else>\n <slot :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, col._flatIndex)\">\n {{ isSelectLikeCol(col) ? getSelectLabel(col._flatIndex, getCellValue(ri, col._flatIndex)) : getCellValue(ri, col._flatIndex) }}\n </slot>\n </template>\n </div>\n </template>\n </div>\n </transition>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- Fill handle (shown on primary range) --\x3e\n <div\n v-if=\"fillHandleVisible\"\n class=\"eg-fill-handle\"\n :style=\"fillHandleStyle\"\n @mousedown.stop=\"startFillDrag\"\n ></div>\n\n \x3c!-- Drag move ghost border --\x3e\n <div\n v-if=\"dragMove.active\"\n class=\"eg-drag-ghost\"\n :style=\"dragGhostStyle\"\n ></div>\n </div>\n </div>\n\n \x3c!-- Column group outline row (top, above body) --\x3e\n <div v-if=\"colGroups.length\" class=\"eg-col-outline-row\" :style=\"colOutlineRowStyle\">\n <template v-for=\"group in colGroups\">\n <div\n :key=\"'cg-' + group.startIndex + '-' + group.endIndex\"\n class=\"eg-group-btn\"\n :class=\"{ collapsed: isGroupCollapsed(group) }\"\n :style=\"getColGroupBtnStyle(group)\"\n @click=\"toggleGroup(group)\"\n >{{ isGroupCollapsed(group) ? '+' : '−' }}</div>\n </template>\n </div>\n\n </div>\x3c!-- end body-area --\x3e\n\n \x3c!-- ===== FOOTER AREA (column totals) ===== --\x3e\n <div v-if=\"hasFooter\" class=\"eg-footer-area\" :style=\"footerAreaStyle\">\n\n \x3c!-- \"Jami\" label cell (mirrors corner cell) --\x3e\n <div class=\"eg-footer-label\" :style=\"footerLabelStyle\">{{ t.total }}</div>\n\n \x3c!-- Frozen columns footer --\x3e\n <div v-if=\"getFrozenWidth() > 0\" class=\"eg-frozen-col-footer\" :style=\"frozenColFooterStyle\">\n <div :style=\"{ position: 'relative', height: footerRowHeight + 'px' }\">\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnPinnedLeft(ci) && !isColHidden(ci)\">\n <div :key=\"'ff-' + ci\" class=\"eg-footer-cell\" :style=\"getFooterCellStyle(ci)\">\n {{ getColumnTotalDisplay(ci) }}\n </div>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Scrollable footer (horizontally synced with body via scrollFooter ref) --\x3e\n <div class=\"eg-scroll-col-footer\" ref=\"scrollFooter\" :style=\"scrollColHeaderStyle\">\n <div class=\"eg-scroll-col-footer-inner\" :style=\"{ width: scrollContentWidth + 'px', height: footerRowHeight + 'px', position: 'relative' }\">\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnScrollable(ci) && !isColHidden(ci)\">\n <div :key=\"'sf-' + ci\" class=\"eg-footer-cell\" :style=\"getFooterCellStyle(ci)\">\n {{ getColumnTotalDisplay(ci) }}\n </div>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Pinned Right columns footer --\x3e\n <div v-if=\"hasPinnedRight\" class=\"eg-pinned-right-footer\" :style=\"pinnedRightFooterStyle\">\n <div :style=\"{ position: 'relative', height: footerRowHeight + 'px' }\">\n <div\n v-for=\"col in pinnedRightCols\"\n :key=\"'prf-' + col._flatIndex\"\n class=\"eg-footer-cell\"\n :style=\"getFooterCellStyle(col._flatIndex)\"\n >{{ getColumnTotalDisplay(col._flatIndex) }}</div>\n </div>\n </div>\n </div>\x3c!-- end footer-area --\x3e\n\n \x3c!-- Resize indicator line --\x3e\n <div v-if=\"resizing.active\" class=\"eg-resize-line\" :style=\"resizeLineStyle\"></div>\n </div>\x3c!-- end container --\x3e\n\n \x3c!-- Import progress overlay --\x3e\n <div v-if=\"importState.active\" class=\"eg-import-overlay\">\n <div class=\"eg-import-dialog\">\n <div class=\"eg-import-title\">Fayl import qilinmoqda</div>\n <div class=\"eg-import-bar-wrap\">\n <div class=\"eg-import-bar\" :style=\"{ width: importState.percent + '%' }\"></div>\n </div>\n <div class=\"eg-import-msg\">{{ importState.message }}</div>\n <div class=\"eg-import-pct\">{{ importState.percent }}%</div>\n </div>\n </div>\n\n \x3c!-- Export progress overlay --\x3e\n <div v-if=\"exportState.active\" class=\"eg-import-overlay\">\n <div class=\"eg-import-dialog\">\n <div class=\"eg-import-title\">Fayl export qilinmoqda...</div>\n <div class=\"eg-export-spinner\"></div>\n </div>\n </div>\n\n \x3c!-- Select dropdown overlay --\x3e\n <div\n v-if=\"selectDropdown\"\n class=\"eg-select-dropdown\"\n :style=\"dropdownStyle\"\n @mousedown.prevent\n >\n <div v-if=\"selectDropdown.enableSearch\" class=\"eg-select-search\" @mousedown.stop>\n <input\n ref=\"dropdownSearch\"\n class=\"eg-select-search-input\"\n :value=\"selectDropdown.searchQuery\"\n placeholder=\"Qidirish...\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n @input=\"onDropdownSearch\"\n @keydown.stop=\"onDropdownSearchKeyDown\"\n />\n </div>\n <div\n v-for=\"(opt, idx) in filteredSelectOptions\"\n :key=\"opt.id != null ? opt.id : opt.value\"\n class=\"eg-select-option\"\n :class=\"{\n 'eg-select-option--active': opt.value === selectDropdown.currentValue,\n 'eg-select-option--highlighted': idx === selectDropdown.highlight\n }\"\n :style=\"selectDropdown.tree && !selectDropdown.searchQuery ? { paddingLeft: (12 + opt.depth * 16) + 'px' } : null\"\n @mousedown.prevent=\"commitSelectValue(opt.value)\"\n @mouseover=\"selectDropdown.highlight = idx\"\n >\n <span\n v-if=\"selectDropdown.tree && !selectDropdown.searchQuery\"\n class=\"eg-tree-toggle\"\n :class=\"{ 'eg-tree-toggle--leaf': !opt.hasChildren }\"\n @mousedown.prevent.stop=\"toggleTreeNode(idx)\"\n >{{ opt.hasChildren ? (opt.expanded ? '▾' : '▸') : '' }}</span>\n <span v-if=\"opt.color\" class=\"eg-select-dot\" :style=\"{ background: opt.color }\"></span>\n {{ opt.label }}\n </div>\n </div>\n\n \x3c!-- Validation error tooltip (cellValidator) --\x3e\n <div\n v-if=\"invalidCell && invalidCell.rect\"\n class=\"eg-validation-tooltip\"\n :style=\"invalidTooltipStyle\"\n >{{ invalidCell.message }}</div>\n </div>\n</template>\n\n<script>\nimport {\n normalizeRange, isCellInRange, isCellInAnyRange,\n buildRange, singleCellRange, getPrimaryRange\n} from '../utils/selection.js'\nimport { flattenColumns, buildHeaderRows, columnTreeDepth } from '../utils/headerTree.js'\nimport { calcVisibleRows, buildCumulativeHeights, findVisibleRange, getRowTop, getRowHeight } from '../utils/virtualScroll.js'\nimport { generateFillValues } from '../utils/fillHandle.js'\nimport {\n matrixToTSV, tsvToMatrix, extractMatrix, transposeMatrix, applyMatrix, tileMatrix\n} from '../utils/clipboard.js'\nimport {\n mergeHasConflict, findMergeAtCell, isMergeAnchor, addMerge,\n removeMergeAt, resolveMergeAnchor\n} from '../utils/merge.js'\nimport {\n addGroup, removeGroup, toggleGroupCollapse, isGroupCollapsed as _isGroupCollapsed,\n getCollapsedRowIndices, getCollapsedColIndices\n} from '../utils/outline.js'\nimport { exportToExcel as _exportToExcel } from '../utils/excelExport.js'\nimport { importFromFile as _importFromFile } from '../utils/excelImport.js'\nimport { createExcelImportWorker, createExcelExportWorker } from '../utils/workerFactory'\nimport { getMessages } from '../utils/i18n.js'\n\nconst SIZE_CONFIG = {\n small: { row: 22, header: 24, footer: 24 },\n middle: { row: 28, header: 28, footer: 28 },\n large: { row: 36, header: 36, footer: 36 }\n}\nconst ROW_HEADER_WIDTH = 50\nconst DEFAULT_COL_WIDTH = 100\nconst OUTLINE_GUTTER = 20\n\nexport default {\n name: 'ExcelGrid',\n\n // v-model → :data + @input\n model: { prop: 'data', event: 'input' },\n\n props: {\n /** Array of row objects keyed by columns[].key */\n data: { type: Array, default: () => [] },\n\n /** Column definitions (may be nested via children[]) */\n columns: { type: Array, default: () => [] },\n\n /** Indices of hidden leaf columns */\n hiddenColumns: { type: Array, default: () => [] },\n\n /** Indices of hidden rows */\n hiddenRows: { type: Array, default: () => [] },\n\n /** Initial merge descriptors [{ startRow, startCol, endRow, endCol }] */\n merges: { type: Array, default: () => [] },\n\n /** Collapsed group descriptors [{ type, startIndex, endIndex }] */\n collapsedGroups: { type: Array, default: () => [] },\n\n /** Height of the grid container in px */\n height: { type: Number, default: 400 },\n\n /** Width of the grid container (px or 'auto') */\n width: { type: [Number, String], default: '100%' },\n\n /**\n * Called before drag-drop: (sourceRange, targetRange) => bool\n * Return false to cancel the drop.\n */\n beforeDrop: { type: Function, default: null },\n\n /**\n * UI locale for the context menu.\n * Supported: 'en' | 'ru' | 'uz' | 'uz-Cyrl'\n */\n locale: { type: String, default: 'en' },\n\n /**\n * Pinned columns map: { [colKey]: 'left' | 'right' }\n * Pinned-left columns render as a fixed zone on the left.\n * Pinned-right columns render as a fixed zone on the right.\n * Emit 'update:pinnedColumns' to support .sync modifier.\n */\n pinnedColumns: { type: Object, default: () => ({}) },\n\n /**\n * Dynamic cell editability: (row, rowIndex, col, colIndex) => boolean\n * Overrides column-level editable when provided.\n * row = plain data object, col = column definition.\n */\n cellEditable: { type: Function, default: null },\n\n /**\n * Cell value validation: (value, row, rowIndex, col, colIndex) => boolean\n * Return false to reject the write — the cell keeps its old value and\n * an error tooltip is shown anchored to that cell.\n */\n cellValidator: { type: Function, default: null },\n\n /**\n * Error tooltip text for a rejected value.\n * Function: (value, row, rowIndex, col, colIndex) => string\n * String: same message for every rejected cell.\n * Default: locale's \"invalidValue\" message.\n */\n cellValidationMessage: { type: [Function, String], default: null },\n\n /**\n * Bulk-write strictness (paste / fill handle / Delete).\n * false (default): invalid cells in the affected range are skipped,\n * the rest of the operation still applies.\n * true: if ANY cell in the affected range fails validation, the whole\n * bulk operation is rejected — nothing is written.\n */\n strictBulkValidation: { type: Boolean, default: false },\n\n /** Visual size of the grid. Affects row/header height and font sizes. */\n size: { type: String, default: 'middle' },\n\n /** Enable CSS list transitions on row enter/leave (data changes). Disabled during scroll. */\n transitionEnabled: { type: Boolean, default: false },\n\n /** Auto-fit all column widths to content (same as double-clicking each resize handle). Re-applies on data/columns change. */\n autoWidth: { type: Boolean, default: false }\n },\n\n data() {\n return {\n // Selection\n activeCell: { row: 0, col: 0 },\n selectionRanges: [], // [{ startRow, startCol, endRow, endCol }]\n isMouseDown: false,\n mouseDownRange: null,\n isCtrlDrag: false,\n\n // Edit\n editingCell: null, // { row, col }\n editValue: '',\n editOriginalValue: '',\n\n // Scroll\n scrollTop: 0,\n scrollLeft: 0,\n headerViewportWidth: null,\n\n // Virtual scroll\n virtualStartIndex: 0,\n virtualEndIndex: 0,\n virtualOffsetTop: 0,\n totalVirtualHeight: 0,\n\n // Column widths (flat index → px)\n colWidths: {},\n\n // Row heights (row index → px)\n rowHeights: {},\n\n // Import/export progress\n importState: { active: false, percent: 0, message: '' },\n exportState: { active: false },\n\n // Resize state\n _isScrolling: false,\n\n resizing: {\n active: false,\n type: null, // 'col' | 'row'\n index: -1,\n startX: 0,\n startY: 0,\n startSize: 0,\n currentPos: 0\n },\n\n // Fill handle\n fillDrag: {\n active: false,\n sourceRange: null,\n currentRow: -1,\n currentCol: -1\n },\n\n // Drag-move\n dragMove: {\n active: false,\n sourceRange: null,\n startX: 0,\n startY: 0,\n targetRow: -1,\n targetCol: -1,\n copyMode: false\n },\n\n // Clipboard (internal)\n clipboardMatrix: null,\n clipboardRange: null,\n clipboardMode: null, // 'copy' | 'cut'\n\n // Context menu\n contextMenu: { visible: false, x: 0, y: 0, row: -1, col: -1 },\n\n // Select dropdown\n selectDropdown: null, // { row, col, options, currentValue, rect }\n\n // Cell value validation (cellValidator)\n invalidCell: null, // { row, col, message, rect }\n\n // Merges (controlled via prop + update:merges)\n internalMerges: [],\n\n // Groups\n internalGroups: [], // [{ type, startIndex, endIndex }]\n internalCollapsedGroups: [],\n\n // Header\n headerDepth: 1,\n headerRows: [],\n\n // Leaf columns cache\n leafCols: [],\n\n // Footer column totals (flat col index → number | null)\n columnTotals: {}\n }\n },\n\n computed: {\n t() { return getMessages(this.locale) },\n\n pinnedLeftSet() {\n const s = new Set()\n this.leafCols.forEach(col => { if (this.pinnedColumns[col.key] === 'left') s.add(col._flatIndex) })\n return s\n },\n\n pinnedRightSet() {\n const s = new Set()\n this.leafCols.forEach(col => { if (this.pinnedColumns[col.key] === 'right') s.add(col._flatIndex) })\n return s\n },\n\n pinnedLeftCols() { return this.leafCols.filter(col => this.pinnedLeftSet.has(col._flatIndex)) },\n pinnedRightCols() { return this.leafCols.filter(col => this.pinnedRightSet.has(col._flatIndex)) },\n\n hasPinnedLeft() { return this.pinnedLeftSet.size > 0 },\n hasPinnedRight() { return this.pinnedRightSet.size > 0 },\n\n hasFooter() { return this.leafCols.some(col => col.showTotal) },\n\n pinnedLeftWidth() {\n let w = 0\n for (const col of this.pinnedLeftCols) {\n if (!this.isColHidden(col._flatIndex)) w += this.getColW(col._flatIndex)\n }\n return w\n },\n\n pinnedRightWidth() {\n let w = 0\n for (const col of this.pinnedRightCols) {\n if (!this.isColHidden(col._flatIndex)) w += this.getColW(col._flatIndex)\n }\n return w\n },\n\n totalRows() { return this.data.length },\n\n _rowTransitionName() {\n return this.transitionEnabled && !this._isScrolling ? 'eg-row' : ''\n },\n\n _sizeConf() { return SIZE_CONFIG[this.size] || SIZE_CONFIG.middle },\n _rowH() { return this._sizeConf.row },\n _headerRowH() { return this._sizeConf.header },\n _footerRowH() { return this._sizeConf.footer },\n\n headerRowHeight() { return this._headerRowH },\n footerRowHeight() { return this._footerRowH },\n\n // Pre-built cumulative height array — O(n) once, then cached by Vue.\n // findVisibleRange() uses binary search against this cached array → O(log n) per scroll.\n // Invalidated only when row count, custom heights, or hidden rows change.\n cumulativeHeights() {\n return buildCumulativeHeights(this.totalRows, this._rowH, this.rowHeights, this.hiddenRowSet)\n },\n\n hiddenColSet() { return new Set(this.hiddenColumns) },\n\n hiddenRowSet() {\n const s = new Set(this.hiddenRows)\n // Also add rows hidden by collapsed row groups\n const collapsed = getCollapsedRowIndices(this.internalGroups, this.internalCollapsedGroups)\n collapsed.forEach(i => s.add(i))\n return s\n },\n\n hiddenColFromGroupSet() {\n return getCollapsedColIndices(this.internalGroups, this.internalCollapsedGroups)\n },\n\n effectiveHiddenColSet() {\n const s = new Set(this.hiddenColSet)\n this.hiddenColFromGroupSet.forEach(i => s.add(i))\n return s\n },\n\n rowGroups() { return this.internalGroups.filter(g => g.type === 'row') },\n colGroups() { return this.internalGroups.filter(g => g.type === 'column') },\n\n visibleRowRange() {\n const rows = []\n for (let i = this.virtualStartIndex; i <= this.virtualEndIndex; i++) rows.push(i)\n return rows\n },\n\n primaryRange() {\n return getPrimaryRange(this.selectionRanges, this.activeCell)\n },\n\n fillHandleVisible() {\n return this.primaryRange && !this.editingCell\n },\n\n fillHandleStyle() {\n if (!this.primaryRange) return {}\n const r = normalizeRange(this.primaryRange)\n // O(1) lookup via cumulativeHeights instead of O(endRow) getRowTop loop\n const bottom = this.cumulativeHeights[r.endRow + 1] || 0\n const right = this.getColLeft(r.endCol + 1)\n return {\n position: 'absolute',\n width: '6px',\n height: '6px',\n background: '#1a73e8',\n border: '1px solid white',\n bottom: (this.totalVirtualHeight - bottom + 1) + 'px',\n right: (this.scrollContentWidth - right + 1) + 'px',\n cursor: 'crosshair',\n zIndex: 10\n }\n },\n\n filteredSelectOptions() {\n if (!this.selectDropdown) return []\n const { options, searchQuery, enableSearch, tree, allFlatOptions } = this.selectDropdown\n if (!enableSearch || !searchQuery || !searchQuery.trim()) return options\n const q = searchQuery.trim().toLowerCase()\n const pool = tree && allFlatOptions ? allFlatOptions : options\n return pool.filter(o => String(o.label || '').toLowerCase().includes(q))\n },\n\n dropdownStyle() {\n if (!this.selectDropdown) return {}\n const { rect } = this.selectDropdown\n const optionH = this._rowH + 4\n const searchH = this.selectDropdown.enableSearch ? 42 : 0\n const approxHeight = Math.min(this.filteredSelectOptions.length * optionH + 8 + searchH, 320)\n const spaceBelow = window.innerHeight - rect.bottom\n const top = spaceBelow >= approxHeight ? rect.bottom : rect.top - approxHeight\n return {\n position: 'fixed',\n top: top + 'px',\n left: rect.left + 'px',\n minWidth: Math.max(rect.width, 120) + 'px',\n maxWidth: Math.min(360, window.innerWidth - rect.left - 16) + 'px',\n zIndex: 9999\n }\n },\n\n invalidTooltipStyle() {\n if (!this.invalidCell || !this.invalidCell.rect) return {}\n const { rect } = this.invalidCell\n return {\n position: 'fixed',\n top: (rect.bottom + 4) + 'px',\n left: rect.left + 'px',\n zIndex: 9999\n }\n },\n\n dragGhostStyle() {\n if (!this.dragMove.active || this.dragMove.targetRow < 0) return {}\n const r = normalizeRange(this.dragMove.sourceRange)\n const rows = r.endRow - r.startRow\n const cols = r.endCol - r.startCol\n // O(1) lookup via cumulativeHeights instead of O(targetRow) getRowTop loop\n const top = this.cumulativeHeights[this.dragMove.targetRow] || 0\n const left = this.getColLeft(this.dragMove.targetCol)\n const width = this.getRangeWidth(this.dragMove.targetCol, this.dragMove.targetCol + cols)\n const height = this.getRangeHeight(this.dragMove.targetRow, this.dragMove.targetRow + rows)\n return {\n position: 'absolute',\n top: top + 'px',\n left: left + 'px',\n width: width + 'px',\n height: height + 'px',\n border: '2px dashed #1a73e8',\n pointerEvents: 'none',\n zIndex: 20\n }\n },\n\n wrapperStyle() {\n return {\n width: typeof this.width === 'number' ? this.width + 'px' : this.width,\n height: this.height + 'px',\n position: 'relative',\n outline: 'none',\n userSelect: 'none'\n }\n },\n\n headerAreaStyle() {\n return {\n display: 'flex',\n flexDirection: 'row',\n position: 'sticky',\n top: 0,\n zIndex: 30,\n background: '#f8f9fa'\n }\n },\n\n cornerStyle() {\n const outlineW = this.rowGroups.length ? OUTLINE_GUTTER : 0\n return {\n width: (ROW_HEADER_WIDTH + outlineW) + 'px',\n minWidth: (ROW_HEADER_WIDTH + outlineW) + 'px',\n height: (this._headerRowH * this.headerDepth) + 'px',\n background: '#f0f0f0',\n borderRight: '1px solid #ccc',\n borderBottom: '1px solid #ccc',\n boxSizing: 'border-box',\n flexShrink: 0\n }\n },\n\n rowOutlineGutterStyle() {\n return { width: OUTLINE_GUTTER + 'px', flexShrink: 0 }\n },\n\n frozenColHeaderStyle() {\n const w = this.getFrozenWidth()\n return {\n width: w + 'px',\n minWidth: w + 'px',\n overflow: 'hidden',\n flexShrink: 0\n }\n },\n\n scrollColHeaderStyle() {\n // Once measured, lock the header to the body's exact clientWidth (flex: 0 0 <px>)\n // instead of growing independently via flex:1 — this guarantees identical\n // scrollWidth-clientWidth (max scrollLeft) on both, so syncing scrollLeft never clamps\n // one before the other. (padding-right can't do this: clientWidth always includes\n // padding, so padding never actually shrinks it.)\n if (this.headerViewportWidth != null) {\n return { flex: '0 0 ' + this.headerViewportWidth + 'px', overflow: 'hidden' }\n }\n return { flex: 1, overflow: 'hidden' }\n },\n\n bodyAreaStyle() {\n return {\n display: 'flex',\n flexDirection: 'row',\n flex: 1,\n position: 'relative'\n }\n },\n\n rowHeaderColStyle() {\n return {\n width: ROW_HEADER_WIDTH + 'px',\n minWidth: ROW_HEADER_WIDTH + 'px',\n flexShrink: 0,\n display: 'flex',\n flexDirection: 'column',\n position: 'sticky',\n left: 0,\n zIndex: 20,\n background: '#f0f0f0'\n }\n },\n\n rowOutlineColStyle() {\n return {\n width: OUTLINE_GUTTER + 'px',\n minWidth: OUTLINE_GUTTER + 'px',\n flexShrink: 0,\n position: 'relative',\n background: '#fafafa'\n }\n },\n\n scrollBodyStyle() {\n return {\n flex: 1,\n overflow: 'auto',\n position: 'relative'\n }\n },\n\n frozenColBodyStyle() {\n const w = this.getFrozenWidth()\n return {\n position: 'sticky',\n left: 0,\n width: w + 'px',\n zIndex: 10,\n background: 'white',\n overflow: 'hidden'\n }\n },\n\n mainCellsStyle() {\n const left = this.getFrozenWidth()\n return {\n position: 'absolute',\n left: left + 'px',\n top: 0,\n bottom: 0\n }\n },\n\n scrollContentWidth() {\n let w = 0\n for (let i = 0; i < this.leafCols.length; i++) {\n if (!this.isColHidden(i) && !this.pinnedRightSet.has(i)) w += this.getColW(i)\n }\n // Extra right padding so pinned-right zone doesn't cover the last scrollable column\n if (this.hasPinnedRight) w += this.pinnedRightWidth\n return w\n },\n\n scrollableContentWidth() {\n let w = 0\n for (let i = 0; i < this.leafCols.length; i++) {\n if (!this.isColHidden(i) && this.isColumnScrollable(i)) w += this.getColW(i)\n }\n return w\n },\n\n pinnedRightBodyStyle() {\n return {\n position: 'sticky',\n right: 0,\n width: this.pinnedRightWidth + 'px',\n zIndex: 10,\n background: 'white',\n flexShrink: 0\n }\n },\n\n pinnedRightHeaderStyle() {\n return {\n width: this.pinnedRightWidth + 'px',\n minWidth: this.pinnedRightWidth + 'px',\n overflow: 'hidden',\n flexShrink: 0,\n borderLeft: '1px solid #ccc'\n }\n },\n\n scrollRowHeadersStyle() {\n return { flex: 1, overflow: 'hidden', position: 'relative' }\n },\n\n footerAreaStyle() {\n return {\n display: 'flex',\n flexDirection: 'row',\n flexShrink: 0,\n position: 'sticky',\n bottom: 0,\n zIndex: 30,\n background: '#f8f9fa',\n boxShadow: '0 -2px 0 0 #d0d0d0'\n }\n },\n\n footerLabelStyle() {\n const outlineW = this.rowGroups.length ? OUTLINE_GUTTER : 0\n return {\n width: (ROW_HEADER_WIDTH + outlineW) + 'px',\n minWidth: (ROW_HEADER_WIDTH + outlineW) + 'px',\n height: this._footerRowH + 'px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: '#f0f0f0',\n borderRight: '1px solid #ccc',\n borderTop: '1px solid #ccc',\n boxSizing: 'border-box',\n flexShrink: 0,\n fontSize: '12px',\n fontWeight: 600\n }\n },\n\n frozenColFooterStyle() {\n const w = this.getFrozenWidth()\n return {\n width: w + 'px',\n minWidth: w + 'px',\n height: this._footerRowH + 'px',\n overflow: 'hidden',\n flexShrink: 0\n }\n },\n\n pinnedRightFooterStyle() {\n return {\n width: this.pinnedRightWidth + 'px',\n minWidth: this.pinnedRightWidth + 'px',\n height: this._footerRowH + 'px',\n overflow: 'hidden',\n flexShrink: 0,\n borderLeft: '1px solid #ccc'\n }\n },\n\n colOutlineRowStyle() {\n return {\n position: 'absolute',\n top: -OUTLINE_GUTTER + 'px',\n left: 0,\n right: 0,\n height: OUTLINE_GUTTER + 'px'\n }\n },\n\n resizeLineStyle() {\n if (!this.resizing.active) return {}\n if (this.resizing.type === 'col') {\n return {\n position: 'fixed',\n top: 0,\n left: this.resizing.currentPos + 'px',\n width: '2px',\n height: '100%',\n background: '#1a73e8',\n pointerEvents: 'none',\n zIndex: 9999\n }\n } else {\n return {\n position: 'fixed',\n left: 0,\n top: this.resizing.currentPos + 'px',\n height: '2px',\n width: '100%',\n background: '#1a73e8',\n pointerEvents: 'none',\n zIndex: 9999\n }\n }\n }\n },\n\n watch: {\n size() {\n this.rowHeights = {}\n this.$nextTick(() => this.updateVirtualScroll())\n },\n\n autoWidth(v) {\n if (v) this._applyAutoWidth()\n },\n\n columns: {\n immediate: true,\n handler(cols) {\n this.leafCols = flattenColumns(cols)\n this.headerDepth = columnTreeDepth(cols)\n this.headerRows = buildHeaderRows(cols, this.headerDepth)\n // Init colWidths\n this.leafCols.forEach(col => {\n if (this.colWidths[col._flatIndex] === undefined) {\n this.colWidths[col._flatIndex] = col.width || DEFAULT_COL_WIDTH\n }\n })\n this.computeColumnTotals()\n this.updateVirtualScroll()\n this._applyAutoWidth()\n }\n },\n merges: {\n immediate: true,\n handler(v) { this.internalMerges = v ? [...v] : [] }\n },\n collapsedGroups: {\n immediate: true,\n handler(v) { this.internalCollapsedGroups = v ? [...v] : [] }\n },\n data: {\n immediate: true,\n handler(newData) {\n // Keep _dataRaw in sync so template uses fresh data without triggering dependArray\n this._dataRaw = newData || []\n this.updateVirtualScroll()\n this.computeColumnTotals()\n // Force re-render so cell values refresh even when virtualScroll indices don't change\n // (e.g. editing a cell: same row count, same scroll position, only content changed)\n this.$forceUpdate()\n this._applyAutoWidth()\n }\n },\n totalRows() { this.updateVirtualScroll() },\n hiddenRowSet() { this.updateVirtualScroll() },\n totalVirtualHeight() { this.$nextTick(this.syncHeaderViewportWidth) }\n },\n\n created() {\n // Non-reactive row data reference — avoids Vue 2 dependArray(data) O(n) loop.\n // Vue 2 calls dependArray() for EVERY `data[ri]` access in the template,\n // iterating all 50,000 rows each time. With 1200 cells per render that's\n // 60 million iterations per render (~600ms). _dataRaw bypasses this.\n this._dataRaw = this.data || []\n\n // Non-reactive caches: same (w,h) → same style object → Vue 2 skips DOM style writes\n this._cellStyleCache = Object.create(null)\n this._rowStyleCache = Object.create(null)\n this._rowHeaderStyleCache = Object.create(null)\n // rAF handles for scroll/resize throttle\n this._scrollRAF = null\n this._resizeRAF = null\n\n // Undo/redo history — non-reactive to avoid Vue overhead.\n // Each entry: { type: 'data'|'merges', before: {...}, after: {...} }\n // 'data' stores only diff rows: { indices: number[], rows: object[] }\n this._history = []\n this._historyIndex = -1\n this._historySkip = false // true while undo/redo is in progress\n\n },\n\n mounted() {\n this.updateVirtualScroll()\n this.$nextTick(this.syncHeaderViewportWidth)\n document.addEventListener('mousemove', this.onGlobalMouseMove)\n document.addEventListener('mouseup', this.onGlobalMouseUp)\n document.addEventListener('keydown', this.onGlobalKeyDown)\n document.addEventListener('mousedown', this._onDocMouseDownForDropdown, true)\n window.addEventListener('resize', this.syncHeaderViewportWidth)\n window.addEventListener('scroll', this._onWindowScrollForDropdown, true)\n },\n\n beforeDestroy() {\n document.removeEventListener('mousemove', this.onGlobalMouseMove)\n document.removeEventListener('mouseup', this.onGlobalMouseUp)\n document.removeEventListener('keydown', this.onGlobalKeyDown)\n document.removeEventListener('mousedown', this._onDocMouseDownForDropdown, true)\n window.removeEventListener('resize', this.syncHeaderViewportWidth)\n window.removeEventListener('scroll', this._onWindowScrollForDropdown, true)\n clearTimeout(this._scrollEndTimer)\n },\n\n methods: {\n // =========================================================\n // VIRTUAL SCROLL\n // =========================================================\n updateVirtualScroll() {\n const footerH = this.hasFooter ? this._footerRowH : 0\n const containerH = this.height - (this._headerRowH * this.headerDepth) - footerH\n // Use binary search against the Vue-cached cumulativeHeights → O(log n) per scroll\n const result = findVisibleRange(\n this.scrollTop, Math.max(containerH, 100), this.totalRows, this.cumulativeHeights\n )\n this.virtualStartIndex = result.startIndex\n this.virtualEndIndex = result.endIndex\n this.virtualOffsetTop = result.offsetTop\n this.totalVirtualHeight = result.totalHeight\n },\n\n onBodyScroll(e) {\n const target = e.target\n // Sync sibling scroll containers immediately (visual alignment, no layout trigger)\n if (this.$refs.scrollColHeader) this.$refs.scrollColHeader.scrollLeft = target.scrollLeft\n if (this.$refs.scrollFooter) this.$refs.scrollFooter.scrollLeft = target.scrollLeft\n if (this.$refs.scrollRowHeaders) this.$refs.scrollRowHeaders.scrollTop = target.scrollTop\n\n // Close dropdown on scroll — fixed-position dropdown drifts away from the moving cell\n if (this.selectDropdown) this.closeSelectDropdown()\n\n // Suppress row transitions during scroll to avoid animation on every virtual-scroll paint\n if (this.transitionEnabled) {\n this._isScrolling = true\n clearTimeout(this._scrollEndTimer)\n this._scrollEndTimer = setTimeout(() => { this._isScrolling = false }, 150)\n }\n\n // Throttle reactive state updates + virtual scroll recalc to one rAF per frame\n if (!this._scrollRAF) {\n this._scrollRAF = requestAnimationFrame(() => {\n this._scrollRAF = null\n this.scrollTop = target.scrollTop\n this.scrollLeft = target.scrollLeft\n this.updateVirtualScroll()\n })\n }\n },\n\n onScroll(e) {},\n\n // .eg-scroll-col-header (overflow:hidden, synced via JS) has no native scrollbar, but\n // .eg-scroll-body (overflow:auto) does — its real vertical scrollbar eats into its\n // clientWidth. If both containers just grow via flex:1, the header ends up wider than\n // the body, so its max scrollLeft is smaller and syncing clamps it before the body\n // reaches its rightmost scroll position — column borders desync right at the scroll\n // end. Locking the header to the body's exact measured clientWidth (scrollColHeaderStyle)\n // makes both max scrollLeft values identical.\n syncHeaderViewportWidth() {\n const el = this.$refs.scrollBody\n if (!el) return\n if (this.headerViewportWidth !== el.clientWidth) this.headerViewportWidth = el.clientWidth\n },\n\n // =========================================================\n // HELPERS\n // =========================================================\n isRowHidden(ri) { return this.hiddenRowSet.has(ri) },\n isColHidden(ci) { return this.effectiveHiddenColSet.has(ci) },\n\n getColW(ci) {\n return this.colWidths[ci] !== undefined ? this.colWidths[ci] : (this.leafCols[ci] ? this.leafCols[ci].width || DEFAULT_COL_WIDTH : DEFAULT_COL_WIDTH)\n },\n\n getRowH(ri) {\n if (this.isRowHidden(ri)) return 0\n return this.rowHeights[ri] || this._rowH\n },\n\n getColLeft(ci) {\n let left = 0\n for (let i = 0; i < ci && i < this.leafCols.length; i++) {\n if (!this.isColHidden(i)) left += this.getColW(i)\n }\n return left\n },\n\n getFrozenWidth() {\n return this.hasPinnedLeft ? this.pinnedLeftWidth : 0\n },\n\n isColumnPinnedLeft(ci) {\n return this.pinnedLeftSet.has(ci)\n },\n\n isColumnPinnedRight(ci) {\n return this.pinnedRightSet.has(ci)\n },\n\n isColumnScrollable(ci) {\n return !this.isColumnPinnedLeft(ci) && !this.isColumnPinnedRight(ci)\n },\n\n getZoneColLeft(ci, zone) {\n let left = 0\n if (zone === 'left') {\n for (const col of this.pinnedLeftCols) {\n if (col._flatIndex === ci) break\n if (!this.isColHidden(col._flatIndex)) left += this.getColW(col._flatIndex)\n }\n } else if (zone === 'right') {\n for (const col of this.pinnedRightCols) {\n if (col._flatIndex === ci) break\n if (!this.isColHidden(col._flatIndex)) left += this.getColW(col._flatIndex)\n }\n } else {\n for (let i = 0; i < this.leafCols.length; i++) {\n const col = this.leafCols[i]\n if (col._flatIndex === ci) break\n if (!this.isColHidden(col._flatIndex) && this.isColumnScrollable(col._flatIndex)) {\n left += this.getColW(col._flatIndex)\n }\n }\n }\n return left\n },\n\n isCellInZone(cell, rowIdx, zone) {\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const z = this.isColumnPinnedLeft(i) ? 'left' : (this.isColumnPinnedRight(i) ? 'right' : 'scroll')\n if (z === zone) return true\n }\n return false\n }\n const ci = cell.column._flatIndex\n if (this.isColHidden(ci)) return false\n const z = this.isColumnPinnedLeft(ci) ? 'left' : (this.isColumnPinnedRight(ci) ? 'right' : 'scroll')\n return z === zone\n },\n\n getHeaderCellStyleForZone(cell, rowIndex, zone) {\n let leftLeafIdx = cell._firstLeafFlatIndex\n if (cell.isGroup) {\n const end = leftLeafIdx + cell.colspan - 1\n for (let i = leftLeafIdx; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const z = this.isColumnPinnedLeft(i) ? 'left' : (this.isColumnPinnedRight(i) ? 'right' : 'scroll')\n if (z === zone) { leftLeafIdx = i; break }\n }\n }\n const left = this.getZoneColLeft(leftLeafIdx, zone)\n let width = 0\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const z = this.isColumnPinnedLeft(i) ? 'left' : (this.isColumnPinnedRight(i) ? 'right' : 'scroll')\n if (z !== zone) continue\n width += this.getColW(i)\n }\n } else {\n width = this.getColW(cell.column._flatIndex)\n }\n return {\n position: 'absolute',\n top: (this._headerRowH * rowIndex) + 'px',\n left: left + 'px',\n width: width + 'px',\n height: (this._headerRowH * cell.rowspan) + 'px',\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n boxSizing: 'border-box',\n borderRight: '1px solid #ccc', borderBottom: '1px solid #ccc',\n background: '#f0f0f0', fontSize: '12px', fontWeight: 600,\n overflow: 'hidden', whiteSpace: 'nowrap', userSelect: 'none', cursor: 'pointer'\n }\n },\n\n getRangeWidth(startCol, endCol) {\n let w = 0\n for (let i = startCol; i <= endCol && i < this.leafCols.length; i++) {\n if (!this.isColHidden(i)) w += this.getColW(i)\n }\n return w\n },\n\n getRangeHeight(startRow, endRow) {\n let h = 0\n for (let i = startRow; i <= endRow && i < this.totalRows; i++) {\n if (!this.isRowHidden(i)) h += this.getRowH(i)\n }\n return h\n },\n\n getCellValue(ri, ci) {\n const col = this.leafCols[ci]\n if (!col) return ''\n const row = this._dataRaw[ri]\n if (!row) return ''\n const v = row[col.key]\n return v !== undefined ? v : ''\n },\n\n isEditing(ri, ci) {\n return this.editingCell && this.editingCell.row === ri && this.editingCell.col === ci\n },\n\n isMergedAway(ri, ci) {\n const merge = findMergeAtCell(ri, ci, this.internalMerges)\n if (!merge) return false\n const nm = normalizeRange(merge)\n return !(nm.startRow === ri && nm.startCol === ci)\n },\n\n getMergeSpan(ri, ci) {\n const merge = findMergeAtCell(ri, ci, this.internalMerges)\n if (!merge) return { rowspan: 1, colspan: 1 }\n const nm = normalizeRange(merge)\n return {\n rowspan: nm.endRow - nm.startRow + 1,\n colspan: nm.endCol - nm.startCol + 1\n }\n },\n\n isSelected(ri, ci) {\n return isCellInAnyRange(ri, ci, this.selectionRanges)\n },\n\n isActiveCell(ri, ci) {\n return this.activeCell.row === ri && this.activeCell.col === ci\n },\n\n isCopyHighlighted(ri, ci) {\n if (!this.clipboardRange || this.clipboardMode !== 'copy') return false\n return isCellInRange(ri, ci, this.clipboardRange)\n },\n\n // =========================================================\n // STYLES\n // =========================================================\n getRowStyle(ri) {\n const h = this.getRowH(ri)\n const key = h\n if (this._rowStyleCache[key]) return this._rowStyleCache[key]\n const style = { height: h + 'px', minHeight: h + 'px' }\n this._rowStyleCache[key] = style\n return style\n },\n\n getRowHeaderStyle(ri) {\n const h = this.getRowH(ri)\n const key = h\n if (this._rowHeaderStyleCache[key]) return this._rowHeaderStyleCache[key]\n const style = { height: h + 'px' }\n this._rowHeaderStyleCache[key] = style\n return style\n },\n\n getCellStyle(ri, ci) {\n const merge = findMergeAtCell(ri, ci, this.internalMerges)\n const nm = merge ? normalizeRange(merge) : null\n let w = this.getColW(ci)\n let h = this.getRowH(ri)\n\n if (nm && nm.startRow === ri && nm.startCol === ci) {\n w = this.getRangeWidth(nm.startCol, nm.endCol)\n h = this.getRangeHeight(nm.startRow, nm.endRow)\n }\n\n // Position cells with the same absolute-left arithmetic the header uses\n // (getZoneColLeft), instead of relying on flex layout to accumulate widths —\n // flex and a JS integer sum can drift apart by a few px over many columns,\n // desyncing the body's column borders from the header's.\n const zone = this.isColumnPinnedLeft(ci) ? 'left' : (this.isColumnPinnedRight(ci) ? 'right' : 'scroll')\n const left = this.getZoneColLeft(ci, zone)\n\n // Cache by dimensions+position — same (w,h,left) → same object ref → Vue 2 skips DOM style writes\n const key = w + '_' + h + '_' + left\n if (this._cellStyleCache[key]) return this._cellStyleCache[key]\n const style = { position: 'absolute', left: left + 'px', top: '0px', width: w + 'px', minWidth: w + 'px', height: h + 'px' }\n this._cellStyleCache[key] = style\n return style\n },\n\n getCellClasses(ri, ci) {\n const col = this.leafCols[ci]\n return {\n 'eg-cell-active': this.isActiveCell(ri, ci),\n 'eg-cell-selected': this.isSelected(ri, ci) && !this.isActiveCell(ri, ci),\n 'eg-cell-fill-preview': this.isInFillPreview(ri, ci),\n 'eg-cell-copy': this.isCopyHighlighted(ri, ci),\n 'eg-cell-merged-hidden': this.isMergedAway(ri, ci),\n 'eg-cell-select': !!(col && this.isSelectLikeCol(col)),\n 'eg-cell-checkbox': !!(col && col.type === 'checkbox'),\n 'eg-cell-readonly': !this.isCellEditable(ri, ci),\n 'eg-cell-invalid': !!(this.invalidCell && this.invalidCell.row === ri && this.invalidCell.col === ci)\n }\n },\n\n isInFillPreview(ri, ci) {\n if (!this.fillDrag.active || !this.fillDrag.sourceRange) return false\n const src = this.fillDrag.sourceRange\n const targetRow = this.fillDrag.currentRow\n const targetCol = this.fillDrag.currentCol\n if (targetRow > src.endRow) {\n return ri > src.endRow && ri <= targetRow && ci >= src.startCol && ci <= src.endCol\n }\n if (targetCol > src.endCol) {\n return ci > src.endCol && ci <= targetCol && ri >= src.startRow && ri <= src.endRow\n }\n return false\n },\n\n getHeaderCellStyle(cell, rowIndex, isFrozen) {\n const zone = isFrozen ? 'left' : 'scroll'\n let leftLeafIdx = cell._firstLeafFlatIndex\n if (cell.isGroup) {\n const end = leftLeafIdx + cell.colspan - 1\n for (let i = leftLeafIdx; i <= end; i++) {\n if (this.isColHidden(i)) continue\n if (isFrozen ? this.isColumnPinnedLeft(i) : this.isColumnScrollable(i)) { leftLeafIdx = i; break }\n }\n }\n const left = this.getZoneColLeft(leftLeafIdx, zone)\n\n let width = 0\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const inZone = isFrozen ? this.isColumnPinnedLeft(i) : this.isColumnScrollable(i)\n if (!inZone) continue\n width += this.getColW(i)\n }\n } else {\n width = this.getColW(cell.column._flatIndex)\n }\n\n return {\n position: 'absolute',\n top: (this._headerRowH * rowIndex) + 'px',\n left: left + 'px',\n width: width + 'px',\n height: (this._headerRowH * cell.rowspan) + 'px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxSizing: 'border-box',\n borderRight: '1px solid #ccc',\n borderBottom: '1px solid #ccc',\n background: '#f0f0f0',\n fontSize: '12px',\n fontWeight: 600,\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n cursor: 'pointer'\n }\n },\n\n // =========================================================\n // FOOTER (column totals)\n // =========================================================\n computeColumnTotals() {\n const totals = {}\n const rows = this._dataRaw || []\n this.leafCols.forEach(col => {\n if (!col.showTotal) return\n let sum = 0\n let hasNumber = false\n for (let ri = 0; ri < rows.length; ri++) {\n const row = rows[ri]\n const v = row ? row[col.key] : undefined\n if (typeof v === 'number' && isFinite(v)) {\n sum += v\n hasNumber = true\n }\n }\n totals[col._flatIndex] = hasNumber ? Math.round(sum * 1e6) / 1e6 : null\n })\n this.columnTotals = totals\n },\n\n getColumnTotalDisplay(ci) {\n const col = this.leafCols[ci]\n if (!col || !col.showTotal) return ''\n const v = this.columnTotals[ci]\n return v === null || v === undefined ? '-' : v\n },\n\n getFooterCellStyle(ci) {\n const zone = this.isColumnPinnedLeft(ci) ? 'left' : (this.isColumnPinnedRight(ci) ? 'right' : 'scroll')\n const left = this.getZoneColLeft(ci, zone)\n const w = this.getColW(ci)\n return {\n position: 'absolute',\n left: left + 'px',\n top: '0px',\n width: w + 'px',\n minWidth: w + 'px',\n height: this._footerRowH + 'px'\n }\n },\n\n isCellVisible(cell, rowIndex, isFrozen) {\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const inZone = isFrozen ? this.isColumnPinnedLeft(i) : this.isColumnScrollable(i)\n if (inZone) return true\n }\n return false\n }\n const ci = cell.column._flatIndex\n if (this.isColHidden(ci)) return false\n return isFrozen ? this.isColumnPinnedLeft(ci) : this.isColumnScrollable(ci)\n },\n\n getRowGroupBtnStyle(group) {\n const top = getRowTop(group.startIndex, this._rowH, this.rowHeights) - this.scrollTop\n return {\n position: 'absolute',\n top: top + 'px',\n left: '2px',\n width: '16px',\n height: '16px',\n lineHeight: '16px',\n textAlign: 'center',\n fontSize: '10px',\n cursor: 'pointer',\n background: '#e8eaf6',\n border: '1px solid #9fa8da',\n borderRadius: '2px',\n zIndex: 5\n }\n },\n\n getColGroupBtnStyle(group) {\n const left = this.getColLeft(group.startIndex) - this.scrollLeft\n return {\n position: 'absolute',\n left: left + 'px',\n top: '2px',\n width: '16px',\n height: '16px',\n lineHeight: '16px',\n textAlign: 'center',\n fontSize: '10px',\n cursor: 'pointer',\n background: '#e8eaf6',\n border: '1px solid #9fa8da',\n borderRadius: '2px'\n }\n },\n\n // =========================================================\n // CELL SELECTION (3.1)\n // =========================================================\n selectCell(row, col, addToSelection = false) {\n if (row < 0 || row >= this.totalRows || col < 0 || col >= this.leafCols.length) return\n this.activeCell = { row, col }\n if (addToSelection) {\n this.selectionRanges.push(singleCellRange(row, col))\n } else {\n this.selectionRanges = [singleCellRange(row, col)]\n }\n this.emitSelectionChange()\n },\n\n selectRange(startRow, startCol, endRow, endCol, replace = true) {\n const range = { startRow, startCol, endRow, endCol }\n if (replace) {\n this.selectionRanges = [range]\n } else {\n this.selectionRanges.push(range)\n }\n this.emitSelectionChange()\n },\n\n selectRow(ri) {\n this.activeCell = { row: ri, col: 0 }\n this.selectionRanges = [{ startRow: ri, endRow: ri, startCol: 0, endCol: this.leafCols.length - 1 }]\n this.emitSelectionChange()\n },\n\n selectColumn(ci) {\n this.activeCell = { row: 0, col: ci }\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: ci, endCol: ci }]\n this.emitSelectionChange()\n },\n\n selectAll() {\n this.activeCell = { row: 0, col: 0 }\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: 0, endCol: this.leafCols.length - 1 }]\n this.emitSelectionChange()\n },\n\n emitSelectionChange() {\n this.$emit('selection-change', {\n activeCell: { ...this.activeCell },\n ranges: this.selectionRanges.map(r => normalizeRange(r))\n })\n },\n\n onCellMouseDown(e, ri, ci) {\n this.invalidCell = null\n if (this.editingCell) {\n const editCol = this.leafCols[this.editingCell.col]\n if (editCol && this.hasEditSlot(editCol)) {\n this.cancelEdit()\n } else {\n this.commitEdit()\n }\n }\n this.hideContextMenu()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n\n if (e.ctrlKey || e.metaKey) {\n if (this.selectDropdown) this.closeSelectDropdown()\n // Ctrl+click: start new independent range\n this.activeCell = { row: ri, col: ci }\n this.mouseDownRange = { startRow: ri, startCol: ci, endRow: ri, endCol: ci }\n this.selectionRanges.push(this.mouseDownRange)\n this.isCtrlDrag = true\n } else if (e.shiftKey && this.selectionRanges.length > 0) {\n if (this.selectDropdown) this.closeSelectDropdown()\n // Shift+click: extend primary range to this cell\n this.selectionRanges = [buildRange(this.activeCell, { row: ri, col: ci })]\n } else {\n const wasActive = this.activeCell.row === ri && this.activeCell.col === ci\n this.activeCell = { row: ri, col: ci }\n this.mouseDownRange = { startRow: ri, startCol: ci, endRow: ri, endCol: ci }\n this.selectionRanges = [this.mouseDownRange]\n this.isCtrlDrag = false\n\n // Open select dropdown on click if cell was already active (2nd click)\n const col = this.leafCols[ci]\n if (wasActive && col && this.isSelectLikeCol(col) && !this.hasEditSlot(col) && this.isCellEditable(ri, ci)) {\n this.openSelectDropdown(ri, ci, e)\n e.preventDefault()\n this.isMouseDown = true\n this.emitSelectionChange()\n return\n }\n if (this.selectDropdown) this.closeSelectDropdown()\n }\n\n this.isMouseDown = true\n this.emitSelectionChange()\n e.preventDefault()\n },\n\n onCellMouseOver(e, ri, ci) {\n if (!this.isMouseDown) return\n if (this.fillDrag.active) return\n if (this.dragMove.active) {\n this.dragMove.targetRow = ri\n this.dragMove.targetCol = ci\n return\n }\n\n // Extend current drag range\n if (this.mouseDownRange) {\n this.mouseDownRange.endRow = ri\n this.mouseDownRange.endCol = ci\n this.emitSelectionChange()\n }\n },\n\n onCellMouseUp() {\n this.isMouseDown = false\n },\n\n onCellDblClick(ri, ci) {\n const col = this.leafCols[ci]\n if (!this.isCellEditable(ri, ci)) return\n if (col && this.isSelectLikeCol(col) && !this.hasEditSlot(col)) {\n this.openSelectDropdownForActive()\n return\n }\n if (col && col.type === 'checkbox') return\n this.startEditing(ri, ci, null, true)\n },\n\n onRowHeaderMouseDown(e, ri) {\n const endCol = this.leafCols.length - 1\n const fullRow = { startRow: ri, endRow: ri, startCol: 0, endCol }\n\n if (e.ctrlKey || e.metaKey) {\n if (e.shiftKey && this.selectionRanges.length > 0) {\n // Ctrl+Shift: active qatordan ri gacha yangi range qo'sh\n const sr = Math.min(this.activeCell.row, ri)\n const er = Math.max(this.activeCell.row, ri)\n this.selectionRanges = [...this.selectionRanges, { startRow: sr, endRow: er, startCol: 0, endCol }]\n } else {\n // Ctrl+click: qatorni toggle qil\n const idx = this.selectionRanges.findIndex(r =>\n r.startRow === ri && r.endRow === ri && r.startCol === 0 && r.endCol === endCol\n )\n if (idx >= 0) {\n const next = this.selectionRanges.filter((_, i) => i !== idx)\n this.selectionRanges = next.length ? next : [fullRow]\n } else {\n this.activeCell = { row: ri, col: 0 }\n this.selectionRanges = [...this.selectionRanges, fullRow]\n }\n }\n } else if (e.shiftKey && this.selectionRanges.length > 0) {\n const sr = Math.min(this.activeCell.row, ri)\n const er = Math.max(this.activeCell.row, ri)\n this.selectionRanges = [{ startRow: sr, endRow: er, startCol: 0, endCol }]\n } else {\n this.activeCell = { row: ri, col: 0 }\n this.selectionRanges = [fullRow]\n }\n\n this.mouseDownRange = this.selectionRanges[this.selectionRanges.length - 1]\n this.isMouseDown = true\n this.emitSelectionChange()\n e.preventDefault()\n },\n\n onColHeaderMouseDown(e, cell) {\n if (cell.isGroup) return\n const ci = cell.column._flatIndex\n if (e.shiftKey && this.selectionRanges.length > 0) {\n const sc = Math.min(this.activeCell.col, ci)\n const ec = Math.max(this.activeCell.col, ci)\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: sc, endCol: ec }]\n } else {\n this.activeCell = { row: 0, col: ci }\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: ci, endCol: ci }]\n }\n this.isMouseDown = true\n this.mouseDownRange = this.selectionRanges[0]\n this.emitSelectionChange()\n e.preventDefault()\n },\n\n onColHeaderDblClick(e, cell) {\n if (cell.isGroup) return\n this.autoFitColumn(cell.column._flatIndex)\n },\n\n onColHeaderContextMenu(e, cell) {\n if (!cell.isGroup) {\n this.contextMenu = { visible: true, x: e.clientX, y: e.clientY, row: -1, col: cell.column._flatIndex }\n }\n },\n\n onRowContextMenu(e, ri) {\n this.contextMenu = { visible: true, x: e.clientX, y: e.clientY, row: ri, col: -1 }\n },\n\n onCellContextMenu(e, ri, ci) {\n if (!this.isSelected(ri, ci)) {\n this.selectCell(ri, ci)\n }\n this.contextMenu = { visible: true, x: e.clientX, y: e.clientY, row: ri, col: ci }\n },\n\n hideContextMenu() {\n this.contextMenu.visible = false\n },\n\n // =========================================================\n // KEYBOARD NAVIGATION (3.2)\n // =========================================================\n onKeyDown(e) {\n // Handle open select dropdown keyboard navigation\n if (this.selectDropdown) {\n const key = e.key\n if (key === 'Escape') {\n e.preventDefault()\n this.closeSelectDropdown()\n return\n }\n if (key === 'Enter' || key === ' ') {\n e.preventDefault()\n const h = this.selectDropdown.highlight\n if (h >= 0 && this.selectDropdown.options[h]) {\n this.commitSelectValue(this.selectDropdown.options[h].value)\n } else {\n this.closeSelectDropdown()\n }\n return\n }\n if (key === 'ArrowDown') {\n e.preventDefault()\n this.selectDropdown.highlight = Math.min(this.selectDropdown.highlight + 1, this.selectDropdown.options.length - 1)\n return\n }\n if (key === 'ArrowUp') {\n e.preventDefault()\n this.selectDropdown.highlight = Math.max(this.selectDropdown.highlight - 1, 0)\n return\n }\n if (this.selectDropdown.tree && !this.selectDropdown.searchQuery && (key === 'ArrowRight' || key === 'ArrowLeft')) {\n e.preventDefault()\n const opt = this.selectDropdown.options[this.selectDropdown.highlight]\n if (opt && opt.hasChildren) {\n if (key === 'ArrowRight' && !opt.expanded) this.toggleTreeNode(this.selectDropdown.highlight)\n if (key === 'ArrowLeft' && opt.expanded) this.toggleTreeNode(this.selectDropdown.highlight)\n }\n return\n }\n return // block other keys while dropdown open\n }\n\n if (this.editingCell) return // handled by onEditKeyDown\n const key = e.key\n\n if ((e.ctrlKey || e.metaKey) && key === 'z' && !e.shiftKey) {\n e.preventDefault()\n this.undo()\n return\n }\n if ((e.ctrlKey || e.metaKey) && (key === 'y' || (key === 'z' && e.shiftKey))) {\n e.preventDefault()\n this.redo()\n return\n }\n\n if (key === 'a' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.selectAll()\n return\n }\n\n if (key === 'c' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.copySelection()\n return\n }\n if (key === 'x' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.cutSelection()\n return\n }\n if (key === 'v' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.pasteAt(this.activeCell.row, this.activeCell.col)\n return\n }\n\n if (key === 'Delete' || key === 'Backspace') {\n e.preventDefault()\n this.clearSelection()\n return\n }\n\n if (key === 'F2') {\n e.preventDefault()\n const activeCol = this.leafCols[this.activeCell.col]\n if (activeCol && this.isSelectLikeCol(activeCol) && !this.hasEditSlot(activeCol)) {\n this.openSelectDropdownForActive()\n } else if (activeCol && activeCol.type === 'checkbox') {\n this.toggleCheckbox(this.activeCell.row, this.activeCell.col)\n } else {\n this.startEditing(this.activeCell.row, this.activeCell.col, null, true)\n }\n return\n }\n\n if (key === ' ') {\n const activeCol = this.leafCols[this.activeCell.col]\n if (activeCol && activeCol.type === 'checkbox') {\n e.preventDefault()\n this.toggleCheckbox(this.activeCell.row, this.activeCell.col)\n return\n }\n }\n\n if (key === 'Escape') {\n this.clipboardRange = null\n this.clipboardMatrix = null\n this.clipboardMode = null\n return\n }\n\n if (key === 'Tab') {\n e.preventDefault()\n this.moveActive(0, e.shiftKey ? -1 : 1, e)\n return\n }\n if (key === 'Enter') {\n e.preventDefault()\n this.moveActive(e.shiftKey ? -1 : 1, 0, e)\n return\n }\n\n const arrows = { ArrowUp: [-1,0], ArrowDown: [1,0], ArrowLeft: [0,-1], ArrowRight: [0,1] }\n if (arrows[key]) {\n e.preventDefault()\n const [dr, dc] = arrows[key]\n if (e.shiftKey) {\n // Extend selection\n const pr = this.primaryRange || singleCellRange(this.activeCell.row, this.activeCell.col)\n const nm = normalizeRange(pr)\n // Determine which edge to extend\n if (dr === -1) pr.startRow = Math.max(0, nm.startRow - 1)\n if (dr === 1) pr.endRow = Math.min(this.totalRows - 1, nm.endRow + 1)\n if (dc === -1) pr.startCol = Math.max(0, nm.startCol - 1)\n if (dc === 1) pr.endCol = Math.min(this.leafCols.length - 1, nm.endCol + 1)\n this.selectionRanges = [pr]\n this.emitSelectionChange()\n } else {\n this.moveActive(dr, dc, e)\n }\n return\n }\n\n if (key === 'Home') {\n e.preventDefault()\n if (e.ctrlKey) {\n this.selectCell(0, 0)\n } else {\n this.selectCell(this.activeCell.row, 0)\n }\n return\n }\n if (key === 'End') {\n e.preventDefault()\n if (e.ctrlKey) {\n this.selectCell(this.totalRows - 1, this.leafCols.length - 1)\n } else {\n this.selectCell(this.activeCell.row, this.leafCols.length - 1)\n }\n return\n }\n if (key === 'PageUp') {\n e.preventDefault()\n const visibleRows = Math.floor((this.height - this._headerRowH * this.headerDepth) / this._rowH)\n this.moveActive(-visibleRows, 0, e)\n return\n }\n if (key === 'PageDown') {\n e.preventDefault()\n const visibleRows = Math.floor((this.height - this._headerRowH * this.headerDepth) / this._rowH)\n this.moveActive(visibleRows, 0, e)\n return\n }\n },\n\n onKeyPress(e) {\n if (this.editingCell) return\n if (this.selectDropdown) return\n if (e.ctrlKey || e.metaKey || e.altKey) return\n if (e.key && e.key.length === 1) {\n const activeCol = this.leafCols[this.activeCell.col]\n if (activeCol && this.isSelectLikeCol(activeCol) && !this.hasEditSlot(activeCol)) {\n e.preventDefault()\n this.openSelectDropdownForActive()\n return\n }\n if (activeCol && activeCol.type === 'checkbox') {\n e.preventDefault()\n return\n }\n this.startEditing(this.activeCell.row, this.activeCell.col, e.key, false)\n }\n },\n\n onGlobalKeyDown(e) {\n // Handled by focused wrapper\n },\n\n moveActive(dr, dc, e) {\n let row = this.activeCell.row + dr\n let col = this.activeCell.col + dc\n\n // Tab: wrap around rows\n if (e && e.key === 'Tab') {\n if (col >= this.leafCols.length) { col = 0; row++ }\n if (col < 0) { col = this.leafCols.length - 1; row-- }\n }\n\n row = Math.max(0, Math.min(this.totalRows - 1, row))\n col = Math.max(0, Math.min(this.leafCols.length - 1, col))\n\n // Skip hidden cols\n while (this.isColHidden(col) && col > 0 && col < this.leafCols.length - 1) {\n col += dc >= 0 ? 1 : -1\n }\n // Skip hidden rows\n while (this.isRowHidden(row) && row > 0 && row < this.totalRows - 1) {\n row += dr >= 0 ? 1 : -1\n }\n\n // Handle merge navigation\n const resolved = resolveMergeAnchor(row, col, this.internalMerges)\n row = resolved.row\n col = resolved.col\n\n this.activeCell = { row, col }\n this.selectionRanges = [singleCellRange(row, col)]\n this.emitSelectionChange()\n this.scrollToCell(row, col)\n },\n\n scrollToCell(ri, ci) {\n const scrollBody = this.$refs.scrollBody\n if (!scrollBody) return\n const rowTop = getRowTop(ri, this._rowH, this.rowHeights)\n const rowBottom = rowTop + this.getRowH(ri)\n const colLeft = this.getColLeft(ci) - this.getFrozenWidth()\n const colRight = colLeft + this.getColW(ci)\n\n if (rowTop < scrollBody.scrollTop) scrollBody.scrollTop = rowTop\n if (rowBottom > scrollBody.scrollTop + scrollBody.clientHeight) {\n scrollBody.scrollTop = rowBottom - scrollBody.clientHeight\n }\n if (colLeft < scrollBody.scrollLeft) scrollBody.scrollLeft = colLeft\n if (colRight > scrollBody.scrollLeft + scrollBody.clientWidth) {\n scrollBody.scrollLeft = colRight - scrollBody.clientWidth\n }\n },\n\n // =========================================================\n // EDITING (used by 3.1 + 3.2)\n // =========================================================\n startEditing(ri, ci, initialChar = null, keepValue = false) {\n const col = this.leafCols[ci]\n if (!col || !this.isCellEditable(ri, ci)) return\n if (ri < 0 || ri >= this.totalRows) return\n if (!this.hasEditSlot(col)) {\n if (this.isSelectLikeCol(col)) { this.openSelectDropdownForActive(); return }\n if (col.type === 'checkbox') return\n }\n\n this.invalidCell = null\n this.editingCell = { row: ri, col: ci }\n if (initialChar !== null) {\n this.editValue = initialChar\n } else if (keepValue) {\n this.editValue = String(this.getCellValue(ri, ci) || '')\n } else {\n this.editValue = ''\n }\n this.editOriginalValue = String(this.getCellValue(ri, ci) || '')\n\n this.$nextTick(() => {\n const inputs = this.$refs.editInput\n if (inputs) {\n const input = Array.isArray(inputs) ? inputs[0] : inputs\n if (input) {\n input.focus()\n if (keepValue) {\n input.setSelectionRange(input.value.length, input.value.length)\n }\n }\n }\n })\n },\n\n commitEdit() {\n if (!this.editingCell) return\n const { row, col } = this.editingCell\n const colDef = this.leafCols[col]\n if (!colDef) { this.editingCell = null; return }\n\n if (!this.isCellValueValid(row, col, this.editValue)) {\n this._showInvalidCell(row, col, this.editValue)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n return\n }\n\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[row] = { ...oldData[row], [colDef.key]: this.editValue }\n this._emitDataChange(oldData, newData)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n cancelEdit() {\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n hasEditSlot(col) {\n return !!(col && col.key && this.$scopedSlots && this.$scopedSlots['edit-' + col.key])\n },\n\n commitEditSlot(value) {\n if (!this.editingCell) return\n const { row, col } = this.editingCell\n const colDef = this.leafCols[col]\n if (!colDef) { this.editingCell = null; return }\n if (!this.isCellValueValid(row, col, value)) {\n this._showInvalidCell(row, col, value)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n return\n }\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[row] = { ...oldData[row], [colDef.key]: value }\n this._emitDataChange(oldData, newData)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n // =========================================================\n // CHECKBOX CELLS\n // =========================================================\n\n isCheckboxChecked(ri, ci) {\n const v = this.getCellValue(ri, ci)\n if (v === true || v === 1) return true\n if (typeof v === 'string') return ['true', '1'].includes(v.trim().toLowerCase())\n return false\n },\n\n setCheckboxValue(ri, ci, value) {\n const col = this.leafCols[ci]\n if (!col || !this.isCellEditable(ri, ci)) return\n if (!this.isCellValueValid(ri, ci, value)) {\n this._showInvalidCell(ri, ci, value)\n return\n }\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[ri] = { ...oldData[ri], [col.key]: value }\n this._emitDataChange(oldData, newData)\n },\n\n toggleCheckbox(ri, ci) {\n this.setCheckboxValue(ri, ci, !this.isCheckboxChecked(ri, ci))\n },\n\n onCheckboxChange(e, ri, ci) {\n this.setCheckboxValue(ri, ci, e.target.checked)\n },\n\n // =========================================================\n // CELL EDITABILITY (3.17)\n // =========================================================\n\n isCellEditable(ri, ci) {\n const col = this.leafCols[ci]\n if (!col) return false\n const row = this._dataRaw[ri] || {}\n if (typeof this.cellEditable === 'function') return !!this.cellEditable(row, ri, col, ci)\n if (typeof col.editable === 'function') return !!col.editable(row, ri)\n if (col.editable === false) return false\n return true\n },\n\n // =========================================================\n // CELL VALUE VALIDATION (cellValidator)\n // =========================================================\n\n isCellValueValid(ri, ci, value) {\n if (typeof this.cellValidator !== 'function') return true\n const col = this.leafCols[ci]\n if (!col) return true\n const row = this._dataRaw[ri] || {}\n return !!this.cellValidator(value, row, ri, col, ci)\n },\n\n _resolveValidationMessage(ri, ci, value) {\n const col = this.leafCols[ci]\n const row = this._dataRaw[ri] || {}\n if (typeof this.cellValidationMessage === 'function') return this.cellValidationMessage(value, row, ri, col, ci)\n if (typeof this.cellValidationMessage === 'string' && this.cellValidationMessage) return this.cellValidationMessage\n return this.t.invalidValue\n },\n\n // Find the on-screen rect of the cell currently being committed — the edit\n // input if still mounted (commitEdit, before editingCell is cleared), else\n // the active cell element (checkbox/select commits, which never enter edit mode).\n _invalidCellRect() {\n const inputs = this.$refs.editInput\n const input = Array.isArray(inputs) ? inputs[0] : inputs\n if (input) return input.getBoundingClientRect()\n const el = this.$el ? this.$el.querySelector('.eg-cell-active') : null\n return el ? el.getBoundingClientRect() : null\n },\n\n _showInvalidCell(ri, ci, value) {\n const message = this._resolveValidationMessage(ri, ci, value)\n this.invalidCell = { row: ri, col: ci, message, rect: this._invalidCellRect() }\n this.$emit('cell-validation-error', { row: ri, col: ci, value, message })\n },\n\n // Resolve+emit messages for a batch of rejected { row, col, value } entries\n // from a bulk write (paste / fill / clear). blocked=true means the whole\n // operation was aborted (strictBulkValidation); false means these cells\n // were skipped but the rest of the write still applied.\n _emitBulkValidationError(skipped, blocked) {\n const cells = skipped.map(c => ({\n row: c.row, col: c.col, value: c.value,\n message: this._resolveValidationMessage(c.row, c.col, c.value)\n }))\n this.$emit('bulk-validation-error', { blocked, cells })\n },\n\n // =========================================================\n // SELECT DROPDOWN (3.16) + TREE SELECT (3.22)\n // =========================================================\n\n isSelectLikeCol(col) {\n return !!(col && (col.type === 'select' || col.type === 'treeSelect'))\n },\n\n treeOptionKeys(col) {\n return {\n valueKey: col.optionValueKey || 'value',\n labelKey: col.optionLabelKey || 'label',\n childrenKey: col.optionChildrenKey || 'children'\n }\n },\n\n // Flattens a tree of option nodes into a visible list, respecting which\n // node ids are currently expanded. Each returned entry carries enough\n // info (depth/hasChildren/expanded/id) to render indentation + a toggle.\n _treeFlatten(nodes, keys, expandedMap, depth, pathArr) {\n let out = []\n ;(nodes || []).forEach((node, idx) => {\n const path = pathArr.concat(idx)\n const id = path.join('-')\n const children = Array.isArray(node[keys.childrenKey]) ? node[keys.childrenKey] : []\n const hasChildren = children.length > 0\n const expanded = hasChildren && !!expandedMap[id]\n out.push({\n value: node[keys.valueKey],\n label: node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey]),\n color: node.color,\n id, depth, hasChildren, expanded\n })\n if (expanded) {\n out = out.concat(this._treeFlatten(children, keys, expandedMap, depth + 1, path))\n }\n })\n return out\n },\n\n _treeFlattenAll(nodes, keys, depth, result) {\n ;(nodes || []).forEach(node => {\n const children = Array.isArray(node[keys.childrenKey]) ? node[keys.childrenKey] : []\n result.push({\n value: node[keys.valueKey],\n label: node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey]),\n color: node.color,\n id: String(node[keys.valueKey]),\n depth: 0, hasChildren: false, expanded: false\n })\n if (children.length) this._treeFlattenAll(children, keys, depth + 1, result)\n })\n return result\n },\n\n _treeFindLabel(nodes, keys, value) {\n for (const node of (nodes || [])) {\n if (String(node[keys.valueKey]) === value) {\n return node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey])\n }\n const found = this._treeFindLabel(node[keys.childrenKey], keys, value)\n if (found != null) return found\n }\n return null\n },\n\n // Returns the chain of ancestor node ids leading to `value` (inclusive),\n // so callers can pre-expand exactly that branch when opening the dropdown.\n _treeFindPath(nodes, keys, value, pathArr) {\n for (let idx = 0; idx < (nodes || []).length; idx++) {\n const node = nodes[idx]\n const path = pathArr.concat(idx)\n if (String(node[keys.valueKey]) === value) return [path.join('-')]\n const children = node[keys.childrenKey]\n if (Array.isArray(children)) {\n const sub = this._treeFindPath(children, keys, value, path)\n if (sub) return [path.join('-')].concat(sub)\n }\n }\n return null\n },\n\n colOptions(ci) {\n const col = this.leafCols[ci]\n if (!col || !Array.isArray(col.options)) return []\n if (col.type === 'treeSelect') {\n return this._treeFlatten(col.options, this.treeOptionKeys(col), {}, 0, [])\n }\n return col.options.map(o =>\n typeof o === 'string' ? { value: o, label: o } : { value: o.value, label: o.label != null ? o.label : String(o.value), color: o.color }\n )\n },\n\n getSelectLabel(ci, value) {\n const col = this.leafCols[ci]\n const v = value == null ? '' : String(value)\n if (col && col.type === 'treeSelect') {\n if (!Array.isArray(col.options)) return v\n const found = this._treeFindLabel(col.options, this.treeOptionKeys(col), v)\n return found != null ? found : v\n }\n const opts = this.colOptions(ci)\n if (!opts.length) return v\n const found = opts.find(o => String(o.value) === v)\n return found ? found.label : v\n },\n\n _buildSelectDropdownState(ri, ci, col, currentValue, rect) {\n const rectObj = { top: rect.top, bottom: rect.bottom, left: rect.left, width: rect.width }\n const enableSearch = !!col.enableSearch\n if (col.type === 'treeSelect') {\n const keys = this.treeOptionKeys(col)\n const path = (Array.isArray(col.options) && this._treeFindPath(col.options, keys, currentValue, [])) || []\n const expandedMap = {}\n path.forEach(id => { expandedMap[id] = true })\n const flat = this._treeFlatten(col.options, keys, expandedMap, 0, [])\n return {\n row: ri, col: ci, tree: true, keys, expandedMap,\n options: flat,\n allFlatOptions: enableSearch ? this._treeFlattenAll(col.options || [], keys, 0, []) : null,\n currentValue,\n highlight: flat.findIndex(o => String(o.value) === currentValue),\n rect: rectObj,\n enableSearch,\n searchQuery: ''\n }\n }\n const opts = this.colOptions(ci)\n return {\n row: ri, col: ci, tree: false,\n options: opts,\n allFlatOptions: null,\n currentValue,\n highlight: opts.findIndex(o => String(o.value) === currentValue),\n rect: rectObj,\n enableSearch,\n searchQuery: ''\n }\n },\n\n toggleTreeNode(idx) {\n if (!this.selectDropdown || !this.selectDropdown.tree) return\n const opt = this.selectDropdown.options[idx]\n if (!opt || !opt.hasChildren) return\n const { row, col, expandedMap, keys } = this.selectDropdown\n const colDef = this.leafCols[col]\n if (!colDef) return\n const nextExpandedMap = { ...expandedMap }\n if (nextExpandedMap[opt.id]) delete nextExpandedMap[opt.id]\n else nextExpandedMap[opt.id] = true\n const flat = this._treeFlatten(colDef.options, keys, nextExpandedMap, 0, [])\n const newHighlight = flat.findIndex(o => o.id === opt.id)\n this.selectDropdown = {\n ...this.selectDropdown,\n row, col,\n expandedMap: nextExpandedMap,\n options: flat,\n highlight: newHighlight >= 0 ? newHighlight : this.selectDropdown.highlight\n }\n },\n\n openSelectDropdown(ri, ci, e) {\n const col = this.leafCols[ci]\n if (!col || !Array.isArray(col.options)) return\n const el = e.currentTarget\n const rect = el.getBoundingClientRect()\n const currentValue = String(this.getCellValue(ri, ci) != null ? this.getCellValue(ri, ci) : '')\n this.selectDropdown = this._buildSelectDropdownState(ri, ci, col, currentValue, rect)\n if (col.enableSearch) {\n this.$nextTick(() => { this.$refs.dropdownSearch && this.$refs.dropdownSearch.focus() })\n }\n },\n\n openSelectDropdownForActive() {\n const { row, col } = this.activeCell\n const colDef = this.leafCols[col]\n if (!colDef || !Array.isArray(colDef.options)) return\n this.$nextTick(() => {\n const el = this.$el ? this.$el.querySelector('.eg-cell-active') : null\n if (!el) return\n const rect = el.getBoundingClientRect()\n const currentValue = String(this.getCellValue(row, col) != null ? this.getCellValue(row, col) : '')\n this.selectDropdown = this._buildSelectDropdownState(row, col, colDef, currentValue, rect)\n if (colDef.enableSearch) {\n this.$nextTick(() => { this.$refs.dropdownSearch && this.$refs.dropdownSearch.focus() })\n }\n })\n },\n\n commitSelectValue(value) {\n if (!this.selectDropdown) return\n const { row, col } = this.selectDropdown\n const colDef = this.leafCols[col]\n if (colDef) {\n if (!this.isCellValueValid(row, col, value)) {\n this._showInvalidCell(row, col, value)\n this.closeSelectDropdown()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n return\n }\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[row] = { ...oldData[row], [colDef.key]: value }\n this._emitDataChange(oldData, newData)\n if (typeof colDef.onSelect === 'function') {\n const _opt = this.filteredSelectOptions.find(o => o.value === value)\n colDef.onSelect({ value, label: _opt ? _opt.label : String(value) }, newData[row], row, colDef)\n }\n }\n this.closeSelectDropdown()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n onDropdownSearch(e) {\n if (!this.selectDropdown) return\n this.selectDropdown.searchQuery = e.target.value\n this.selectDropdown.highlight = 0\n },\n\n onDropdownSearchKeyDown(e) {\n if (!this.selectDropdown) return\n const { key } = e\n if (key === 'Escape') {\n this.closeSelectDropdown()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n } else if (key === 'ArrowDown') {\n e.preventDefault()\n this.selectDropdown.highlight = Math.min(\n (this.selectDropdown.highlight || 0) + 1,\n this.filteredSelectOptions.length - 1\n )\n } else if (key === 'ArrowUp') {\n e.preventDefault()\n this.selectDropdown.highlight = Math.max((this.selectDropdown.highlight || 0) - 1, 0)\n } else if (key === 'Enter') {\n e.preventDefault()\n const h = this.selectDropdown.highlight\n const opts = this.filteredSelectOptions\n if (h >= 0 && opts[h]) this.commitSelectValue(opts[h].value)\n }\n },\n\n closeSelectDropdown() {\n this.selectDropdown = null\n },\n\n _onWindowScrollForDropdown(e) {\n if (!this.selectDropdown) return\n const dd = this.$el ? this.$el.querySelector('.eg-select-dropdown') : null\n if (dd && dd.contains(e.target)) return\n this.closeSelectDropdown()\n },\n\n _onDocMouseDownForDropdown(e) {\n if (!this.selectDropdown) return\n const dd = this.$el ? this.$el.querySelector('.eg-select-dropdown') : null\n if (dd && dd.contains(e.target)) return\n // Click outside dropdown — close it\n this.closeSelectDropdown()\n },\n\n onEditKeyDown(e) {\n if (e.key === 'Escape') {\n e.preventDefault()\n this.cancelEdit()\n } else if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n this.commitEdit()\n this.moveActive(1, 0, e)\n } else if (e.key === 'Tab') {\n e.preventDefault()\n this.commitEdit()\n this.moveActive(0, e.shiftKey ? -1 : 1, e)\n }\n },\n\n clearSelection() {\n if (!this.selectionRanges.length) return\n const oldData = this.data\n const newData = oldData.slice()\n let hasChanges = false\n const skipped = []\n for (const range of this.selectionRanges) {\n const r = {\n startRow: Math.min(range.startRow, range.endRow),\n endRow: Math.max(range.startRow, range.endRow),\n startCol: Math.min(range.startCol, range.endCol),\n endCol: Math.max(range.startCol, range.endCol)\n }\n for (let ri = r.startRow; ri <= r.endRow && ri < newData.length; ri++) {\n let rowUpdated = null\n for (let ci = r.startCol; ci <= r.endCol; ci++) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, '')) { skipped.push({ row: ri, col: ci, value: '' }); continue }\n if (!rowUpdated) rowUpdated = { ...newData[ri] }\n rowUpdated[col.key] = ''\n hasChanges = true\n }\n }\n if (rowUpdated) newData[ri] = rowUpdated\n }\n }\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n return\n }\n if (hasChanges) this._emitDataChange(oldData, newData)\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n },\n\n // =========================================================\n // FILL HANDLE (3.3)\n // =========================================================\n startFillDrag(e) {\n const pr = this.primaryRange\n if (!pr) return\n this.fillDrag.active = true\n this.fillDrag.sourceRange = normalizeRange(pr)\n this.fillDrag.currentRow = pr.endRow\n this.fillDrag.currentCol = pr.endCol\n e.preventDefault()\n },\n\n onGlobalMouseMove(e) {\n if (this.resizing.active) {\n this.handleResizeMove(e)\n return\n }\n if (this.fillDrag.active) {\n this.handleFillDragMove(e)\n return\n }\n if (this.dragMove.active) {\n this.handleDragMoveMove(e)\n return\n }\n },\n\n onGlobalMouseUp(e) {\n if (this.resizing.active) {\n this.commitResize()\n return\n }\n if (this.fillDrag.active) {\n this.commitFillDrag()\n return\n }\n if (this.dragMove.active) {\n this.commitDragMove(e)\n return\n }\n this.isMouseDown = false\n },\n\n handleFillDragMove(e) {\n const scrollBody = this.$refs.scrollBody\n if (!scrollBody) return\n const rect = scrollBody.getBoundingClientRect()\n const relY = e.clientY - rect.top + scrollBody.scrollTop\n const relX = e.clientX - rect.left + scrollBody.scrollLeft\n\n // Binary search on cumulativeHeights — O(log n) instead of O(n)\n const cum = this.cumulativeHeights\n const total = this.totalRows\n let lo = 0, hi = total - 1\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (cum[mid + 1] <= relY) lo = mid + 1\n else hi = mid\n }\n const row = Math.min(lo, total - 1)\n\n // Columns are few — O(columns) is fast\n let left = this.getFrozenWidth()\n let col = 0\n for (let i = 0; i < this.leafCols.length; i++) {\n if (!this.isColumnScrollable(i)) continue\n left += this.getColW(i)\n if (relX < left) { col = i; break }\n col = i\n }\n\n this.fillDrag.currentRow = row\n this.fillDrag.currentCol = col\n },\n\n commitFillDrag() {\n const src = this.fillDrag.sourceRange\n if (!src) { this.fillDrag.active = false; return }\n\n const targetRow = this.fillDrag.currentRow\n const targetCol = this.fillDrag.currentCol\n\n const oldData = this.data\n const newData = oldData.slice() // only affected rows get new objects\n let targetRange\n const skipped = []\n\n if (targetRow > src.endRow) {\n // Fill down\n const count = targetRow - src.endRow\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n if (!col) continue\n const srcValues = []\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n srcValues.push(oldData[ri] ? oldData[ri][col.key] : '')\n }\n const filled = generateFillValues(srcValues, count)\n for (let k = 0; k < count; k++) {\n const ri = src.endRow + 1 + k\n if (ri < this.totalRows && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, filled[k])) { skipped.push({ row: ri, col: ci, value: filled[k] }); continue }\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n newData[ri][col.key] = filled[k]\n }\n }\n }\n targetRange = { startRow: src.endRow + 1, endRow: targetRow, startCol: src.startCol, endCol: src.endCol }\n } else if (targetCol > src.endCol) {\n // Fill right\n const count = targetCol - src.endCol\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n const srcValues = []\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n srcValues.push(col && oldData[ri] ? oldData[ri][col.key] : '')\n }\n const filled = generateFillValues(srcValues, count)\n for (let k = 0; k < count; k++) {\n const ci = src.endCol + 1 + k\n if (ci < this.leafCols.length) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, filled[k])) { skipped.push({ row: ri, col: ci, value: filled[k] }); continue }\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n newData[ri][col.key] = filled[k]\n }\n }\n }\n }\n targetRange = { startRow: src.startRow, endRow: src.endRow, startCol: src.endCol + 1, endCol: targetCol }\n }\n\n if (targetRange) {\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n } else {\n this._emitDataChange(oldData, newData)\n this.$emit('fill', { sourceRange: src, targetRange, values: newData })\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n }\n }\n\n this.fillDrag.active = false\n this.fillDrag.sourceRange = null\n },\n\n doubleFillDown() {\n const pr = this.primaryRange\n if (!pr) return\n const src = normalizeRange(pr)\n // Find adjacent column filled length\n const adjCol = src.startCol > 0 ? src.startCol - 1 : src.endCol + 1\n if (adjCol < 0 || adjCol >= this.leafCols.length) return\n const adjColDef = this.leafCols[adjCol]\n if (!adjColDef) return\n let lastFilledRow = src.endRow\n for (let ri = src.endRow + 1; ri < this.totalRows; ri++) {\n if (this.data[ri] && this.data[ri][adjColDef.key] !== '' && this.data[ri][adjColDef.key] !== undefined) {\n lastFilledRow = ri\n } else break\n }\n if (lastFilledRow <= src.endRow) return\n\n const count = lastFilledRow - src.endRow\n const oldData = this.data\n const newData = oldData.slice()\n const skipped = []\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n if (!col) continue\n const srcValues = []\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n srcValues.push(oldData[ri] ? oldData[ri][col.key] : '')\n }\n const filled = generateFillValues(srcValues, count)\n for (let k = 0; k < count; k++) {\n const ri = src.endRow + 1 + k\n if (ri < this.totalRows && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, filled[k])) { skipped.push({ row: ri, col: ci, value: filled[k] }); continue }\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n newData[ri][col.key] = filled[k]\n }\n }\n }\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n return\n }\n this._emitDataChange(oldData, newData)\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n },\n\n // =========================================================\n // COPY / PASTE (3.4)\n // =========================================================\n copySelection() {\n const pr = this.primaryRange\n if (!pr) return\n const r = normalizeRange(pr)\n this.clipboardMatrix = extractMatrix(this.data, this.leafCols, r)\n this.clipboardRange = r\n this.clipboardMode = 'copy'\n\n // Write to system clipboard as TSV\n const tsv = matrixToTSV(this.clipboardMatrix)\n if (navigator.clipboard) {\n navigator.clipboard.writeText(tsv).catch(() => {})\n }\n },\n\n cutSelection() {\n this.copySelection()\n this.clipboardMode = 'cut'\n },\n\n async pasteAt(targetRow, targetCol, options = {}) {\n const { mode = 'all', transpose = false } = options\n\n let matrix = null\n\n // Try system clipboard first\n if (navigator.clipboard) {\n try {\n const text = await navigator.clipboard.readText()\n if (text) matrix = tsvToMatrix(text)\n } catch (_) {}\n }\n\n // Fall back to internal clipboard\n if (!matrix && this.clipboardMatrix) {\n matrix = this.clipboardMatrix\n }\n\n if (!matrix) return\n\n if (transpose) matrix = transposeMatrix(matrix)\n\n // ── Excel-style selection broadcast / tiling ──────────────────────────\n // When pasting over a multi-cell selection and the clipboard matrix is\n // smaller than the selection, tile the matrix to fill the selection —\n // provided the selection dimensions are exact multiples of the matrix\n // dimensions (matches Excel's tiling rule exactly).\n // Cut-paste is never tiled (source must be cleared exactly once).\n const isCopyPaste = !this.clipboardMode || this.clipboardMode === 'copy'\n if (isCopyPaste) {\n const pr = this.primaryRange\n if (pr) {\n const sel = normalizeRange(pr)\n const selRows = sel.endRow - sel.startRow + 1\n const selCols = sel.endCol - sel.startCol + 1\n const matRows = matrix.length\n const matCols = matrix[0] ? matrix[0].length : 0\n\n // Only tile when selection is strictly larger than clipboard\n if ((selRows > matRows || selCols > matCols) &&\n selRows % matRows === 0 && selCols % matCols === 0) {\n matrix = tileMatrix(matrix, selRows, selCols)\n // Paste at selection origin, not just at the active cell\n targetRow = sel.startRow\n targetCol = sel.startCol\n }\n }\n }\n\n const oldData = this.data\n const { newData, affectedRange, skipped } = applyMatrix(\n oldData, this.leafCols, targetRow, targetCol, matrix, mode,\n (ri, ci) => this.isCellEditable(ri, ci),\n (ri, ci, v) => this.isCellValueValid(ri, ci, v)\n )\n\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n return\n }\n\n if (this.clipboardMode === 'cut' && this.clipboardRange) {\n // Clear source — ensure each source row is a new copy before mutation\n const src = this.clipboardRange\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci) && this.isCellValueValid(ri, ci, '')) {\n newData[ri][col.key] = ''\n }\n }\n }\n this.clipboardRange = null\n this.clipboardMode = null\n }\n\n this._emitDataChange(oldData, newData)\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n },\n\n ctxCopy() { this.hideContextMenu(); this.copySelection() },\n ctxCut() { this.hideContextMenu(); this.cutSelection() },\n ctxPaste() { this.hideContextMenu(); this.pasteAt(this.activeCell.row, this.activeCell.col) },\n ctxPasteSpecial(mode, transpose = false) {\n this.hideContextMenu()\n this.pasteAt(this.activeCell.row, this.activeCell.col, { mode, transpose })\n },\n\n // =========================================================\n // RESIZE (3.5)\n // =========================================================\n startColResize(e, ci) {\n const rect = this.$refs.wrapper.getBoundingClientRect()\n this.resizing = {\n active: true,\n type: 'col',\n index: ci,\n startX: e.clientX,\n startY: e.clientY,\n startSize: this.getColW(ci),\n currentPos: e.clientX\n }\n e.preventDefault()\n },\n\n startRowResize(e, ri) {\n this.resizing = {\n active: true,\n type: 'row',\n index: ri,\n startX: e.clientX,\n startY: e.clientY,\n startSize: this.getRowH(ri),\n currentPos: e.clientY\n }\n e.preventDefault()\n },\n\n handleResizeMove(e) {\n if (this.resizing.type === 'col') {\n this.resizing.currentPos = e.clientX\n const newWidth = Math.max(20, this.resizing.startSize + (e.clientX - this.resizing.startX))\n this.$set(this.colWidths, this.resizing.index, newWidth)\n this._cellStyleCache = Object.create(null)\n } else {\n this.resizing.currentPos = e.clientY\n this._resizePreviewHeight = Math.max(10, this.resizing.startSize + (e.clientY - this.resizing.startY))\n // Batch $set + updateVirtualScroll to rAF — prevents O(n) cumulativeHeights rebuild\n // on every mousemove (200+ Hz). At most 60 rebuilds/sec instead of 200+.\n if (!this._resizeRAF) {\n this._resizeRAF = requestAnimationFrame(() => {\n this._resizeRAF = null\n this._rowStyleCache = Object.create(null)\n this._rowHeaderStyleCache = Object.create(null)\n this._cellStyleCache = Object.create(null)\n this.$set(this.rowHeights, this.resizing.index, this._resizePreviewHeight)\n this.updateVirtualScroll()\n })\n }\n }\n },\n\n commitResize() {\n if (this._resizeRAF) {\n cancelAnimationFrame(this._resizeRAF)\n this._resizeRAF = null\n // Apply the last pending preview height immediately on commit\n if (this.resizing.type === 'row' && this._resizePreviewHeight !== undefined) {\n this.$set(this.rowHeights, this.resizing.index, this._resizePreviewHeight)\n }\n }\n if (this.resizing.type === 'col') {\n this._cellStyleCache = Object.create(null)\n const newSize = this.colWidths[this.resizing.index]\n this.$emit('column-resize', { index: this.resizing.index, newSize })\n } else {\n this._rowStyleCache = Object.create(null)\n this._rowHeaderStyleCache = Object.create(null)\n this._cellStyleCache = Object.create(null)\n const newSize = this.rowHeights[this.resizing.index]\n this.$emit('row-resize', { index: this.resizing.index, newSize })\n this.updateVirtualScroll()\n }\n this.resizing.active = false\n },\n\n autoFitColumn(ci) {\n const col = this.leafCols[ci]\n if (!col) return\n let maxLen = (col.title || '').length\n for (let ri = 0; ri < Math.min(this.totalRows, 500); ri++) {\n const v = this.data[ri] ? String(this.data[ri][col.key] || '') : ''\n if (v.length > maxLen) maxLen = v.length\n }\n const newWidth = Math.max(50, maxLen * 8 + 16)\n this.$set(this.colWidths, ci, newWidth)\n this._cellStyleCache = Object.create(null)\n this.$emit('column-resize', { index: ci, newSize: newWidth })\n },\n\n _applyAutoWidth() {\n if (!this.autoWidth || !this.leafCols.length) return\n this.leafCols.forEach((col, ci) => {\n let maxLen = (col.title || '').length\n for (let ri = 0; ri < Math.min(this.totalRows, 500); ri++) {\n const v = this.data[ri] ? String(this.data[ri][col.key] || '') : ''\n if (v.length > maxLen) maxLen = v.length\n }\n this.$set(this.colWidths, ci, Math.max(50, maxLen * 8 + 16))\n })\n this._cellStyleCache = Object.create(null)\n },\n\n ctxHideRow() {\n this.hideContextMenu()\n const pr = this.primaryRange\n if (!pr) return\n const r = normalizeRange(pr)\n const newHidden = [...this.hiddenRows]\n for (let ri = r.startRow; ri <= r.endRow; ri++) {\n if (!newHidden.includes(ri)) newHidden.push(ri)\n }\n this.$emit('update:hiddenRows', newHidden)\n },\n\n ctxHideColumn() {\n this.hideContextMenu()\n const pr = this.primaryRange\n if (!pr) return\n const r = normalizeRange(pr)\n const newHidden = [...this.hiddenColumns]\n for (let ci = r.startCol; ci <= r.endCol; ci++) {\n if (!newHidden.includes(ci)) newHidden.push(ci)\n }\n this.$emit('update:hiddenColumns', newHidden)\n },\n\n ctxUnhideRows() {\n this.hideContextMenu()\n this.$emit('update:hiddenRows', [])\n },\n\n ctxUnhideColumns() {\n this.hideContextMenu()\n this.$emit('update:hiddenColumns', [])\n },\n\n ctxPinLeft() {\n const col = this.leafCols[this.contextMenu.col]\n if (!col) { this.hideContextMenu(); return }\n this.$emit('update:pinnedColumns', { ...this.pinnedColumns, [col.key]: 'left' })\n this.hideContextMenu()\n },\n\n ctxPinRight() {\n const col = this.leafCols[this.contextMenu.col]\n if (!col) { this.hideContextMenu(); return }\n this.$emit('update:pinnedColumns', { ...this.pinnedColumns, [col.key]: 'right' })\n this.hideContextMenu()\n },\n\n ctxUnpin() {\n const col = this.leafCols[this.contextMenu.col]\n if (!col) { this.hideContextMenu(); return }\n const next = { ...this.pinnedColumns }\n delete next[col.key]\n this.$emit('update:pinnedColumns', next)\n this.hideContextMenu()\n },\n\n isColPinned(ci) {\n const col = this.leafCols[ci]\n return !!(col && this.pinnedColumns[col.key])\n },\n\n // =========================================================\n // MERGE CELLS (3.8)\n // =========================================================\n mergeCells(range) {\n const oldMerges = this.internalMerges.slice()\n const { merges: newMerges, conflict } = addMerge(range, this.internalMerges)\n if (conflict) {\n this.$emit('merge-error', { range, reason: 'Overlaps existing merge' })\n return false\n }\n this.internalMerges = newMerges\n this._emitMergeChange(oldMerges, [...newMerges])\n return true\n },\n\n unmergeCells(row, col) {\n const oldMerges = this.internalMerges.slice()\n const newMerges = removeMergeAt(row, col, this.internalMerges)\n this.internalMerges = newMerges\n this._emitMergeChange(oldMerges, [...newMerges])\n },\n\n ctxMerge() {\n this.hideContextMenu()\n const pr = this.primaryRange\n if (!pr) return\n this.mergeCells(normalizeRange(pr))\n },\n\n ctxUnmerge() {\n this.hideContextMenu()\n this.unmergeCells(this.activeCell.row, this.activeCell.col)\n },\n\n // =========================================================\n // GROUPING (3.7)\n // =========================================================\n groupRows(startIndex, endIndex) {\n this.internalGroups = addGroup('row', startIndex, endIndex, this.internalGroups)\n },\n\n groupColumns(startIndex, endIndex) {\n this.internalGroups = addGroup('column', startIndex, endIndex, this.internalGroups)\n },\n\n ungroupRows(startIndex, endIndex) {\n this.internalGroups = removeGroup('row', startIndex, endIndex, this.internalGroups)\n },\n\n ungroupColumns(startIndex, endIndex) {\n this.internalGroups = removeGroup('column', startIndex, endIndex, this.internalGroups)\n },\n\n toggleGroup(group) {\n this.internalCollapsedGroups = toggleGroupCollapse(group, this.internalCollapsedGroups)\n this.$emit('update:collapsedGroups', [...this.internalCollapsedGroups])\n this.$emit('group-toggle', { group, collapsed: _isGroupCollapsed(group, this.internalCollapsedGroups) })\n this.updateVirtualScroll()\n },\n\n isGroupCollapsed(group) {\n return _isGroupCollapsed(group, this.internalCollapsedGroups)\n },\n\n expandAll() {\n this.internalCollapsedGroups = []\n this.$emit('update:collapsedGroups', [])\n this.updateVirtualScroll()\n },\n\n collapseAll() {\n this.internalCollapsedGroups = [...this.internalGroups]\n this.$emit('update:collapsedGroups', [...this.internalGroups])\n this.updateVirtualScroll()\n },\n\n // =========================================================\n // DRAG AND DROP (3.9)\n // =========================================================\n startDragMove(e, ri, ci) {\n const pr = this.primaryRange\n if (!pr) return\n this.dragMove = {\n active: true,\n sourceRange: normalizeRange(pr),\n startX: e.clientX,\n startY: e.clientY,\n targetRow: ri,\n targetCol: ci,\n copyMode: e.ctrlKey || e.metaKey\n }\n this.$emit('cell-drag-start', { sourceRange: this.dragMove.sourceRange })\n e.preventDefault()\n },\n\n handleDragMoveMove(e) {\n this.dragMove.copyMode = e.ctrlKey || e.metaKey\n this.$emit('cell-drag-move', {\n sourceRange: this.dragMove.sourceRange,\n targetRow: this.dragMove.targetRow,\n targetCol: this.dragMove.targetCol\n })\n },\n\n async commitDragMove(e) {\n if (!this.dragMove.active) return\n const { sourceRange, targetRow, targetCol, copyMode } = this.dragMove\n\n const targetRange = {\n startRow: targetRow,\n startCol: targetCol,\n endRow: targetRow + (sourceRange.endRow - sourceRange.startRow),\n endCol: targetCol + (sourceRange.endCol - sourceRange.startCol)\n }\n\n let proceed = true\n if (this.beforeDrop) {\n proceed = await Promise.resolve(this.beforeDrop(sourceRange, targetRange))\n }\n\n if (proceed) {\n const oldData = this.data\n const matrix = extractMatrix(oldData, this.leafCols, sourceRange)\n const { newData } = applyMatrix(oldData, this.leafCols, targetRow, targetCol, matrix, 'all', (ri, ci) => this.isCellEditable(ri, ci))\n\n if (!copyMode) {\n // Clear source — ensure each source row is a new copy before mutation\n for (let ri = sourceRange.startRow; ri <= sourceRange.endRow; ri++) {\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n for (let ci = sourceRange.startCol; ci <= sourceRange.endCol; ci++) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci)) newData[ri][col.key] = ''\n }\n }\n }\n this._emitDataChange(oldData, newData)\n this.$emit('cell-drop', { sourceRange, targetRange })\n }\n\n this.dragMove = { active: false, sourceRange: null, startX: 0, startY: 0, targetRow: -1, targetCol: -1, copyMode: false }\n this.isMouseDown = false\n },\n\n // =========================================================\n // EXCEL EXPORT (3.11)\n // =========================================================\n async exportToExcel(options = {}) {\n const {\n filename = 'export.xlsx',\n sheetName = 'Sheet1',\n scope = 'all',\n includeHidden = false\n } = options\n\n this.$emit('export-start')\n this.exportState = { active: true }\n\n const workerPayload = {\n data: this.data,\n columns: this.columns,\n leafCols: this.leafCols,\n merges: this.internalMerges,\n hiddenCols: [...this.effectiveHiddenColSet],\n hiddenRows: [...this.hiddenRowSet],\n range: scope === 'selection' ? this.primaryRange : null,\n filename,\n sheetName,\n scope,\n includeHidden\n }\n\n try {\n const worker = createExcelExportWorker()\n if (worker) {\n await this._exportViaWorker(worker, workerPayload)\n } else {\n // Fallback: synchronous export on main thread\n _exportToExcel({\n ...workerPayload,\n hiddenColSet: this.effectiveHiddenColSet,\n hiddenRowSet: this.hiddenRowSet\n })\n }\n this.$emit('export-success')\n } catch (err) {\n this.$emit('export-error', err)\n } finally {\n this.exportState = { active: false }\n }\n },\n\n _exportViaWorker(worker, payload) {\n return new Promise((resolve, reject) => {\n let done = false\n const finish = (fn, val) => {\n if (done) return\n done = true\n worker.terminate()\n fn(val)\n }\n\n worker.onmessage = (e) => {\n const { type, buffer, filename, message } = e.data\n if (type === 'result') {\n // Trigger browser download from the main thread (DOM required)\n const blob = new Blob([new Uint8Array(buffer)], {\n type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n finish(resolve)\n } else if (type === 'error') {\n finish(reject, new Error(message))\n }\n }\n\n worker.onerror = (err) => finish(reject, new Error(err.message || 'Export worker xatosi'))\n\n worker.postMessage(payload)\n })\n },\n\n // =========================================================\n // EXCEL IMPORT (3.12)\n // =========================================================\n async importFromFile(file, options = {}) {\n this.importState = { active: true, percent: 5, message: \"Fayl o'qilmoqda...\" }\n\n try {\n let result\n const worker = createExcelImportWorker()\n\n if (worker) {\n result = await this._importViaWorker(worker, file, options)\n } else {\n // Fallback: chunked async on main thread\n result = await _importFromFile(\n file,\n this.columns,\n options,\n (percent, message) => { this.importState = { active: true, percent, message } }\n )\n }\n\n const { columns, merges } = result\n // Freeze each row so Vue 2 skips deep Object.defineProperty observation.\n // Vue 2 checks Object.isExtensible() — frozen objects are skipped entirely.\n // All edit paths already use spread ({ ...row }) so frozen rows are safe.\n const data = result.data.map(row => Object.freeze(row))\n this.$emit('input', data)\n this.$emit('update:columns', columns)\n\n const hasMerges = merges && merges.length > 0\n this.internalMerges = hasMerges ? merges : []\n this.$emit('update:merges', hasMerges ? merges : [])\n this.$emit('update:collapsedGroups', [])\n this.$emit('update:hiddenRows', [])\n this.$emit('import-success', { data, columns, merges })\n\n // Reset scroll state and re-derive virtual scroll after DOM settles.\n // Without this, stale scrollTop + old rowHeights cause blank bands on scroll.\n this.rowHeights = {}\n this.scrollTop = 0\n this.scrollLeft = 0\n this.$nextTick(() => {\n if (this.$refs.scrollBody) {\n this.$refs.scrollBody.scrollTop = 0\n this.$refs.scrollBody.scrollLeft = 0\n }\n if (this.$refs.scrollColHeader) this.$refs.scrollColHeader.scrollLeft = 0\n if (this.$refs.scrollFooter) this.$refs.scrollFooter.scrollLeft = 0\n if (this.$refs.scrollRowHeaders) this.$refs.scrollRowHeaders.scrollTop = 0\n this.updateVirtualScroll()\n })\n } catch (err) {\n this.$emit('import-error', err)\n } finally {\n this.importState = { active: false, percent: 0, message: '' }\n }\n },\n\n _importViaWorker(worker, file, options) {\n return new Promise(async (resolve, reject) => {\n let done = false\n const finish = (fn, val) => {\n if (done) return\n done = true\n worker.terminate()\n fn(val)\n }\n\n worker.onmessage = (e) => {\n const { type, percent, message, data, columns, merges } = e.data\n if (type === 'progress') {\n this.importState = { active: true, percent, message }\n } else if (type === 'result') {\n finish(resolve, { data, columns, merges })\n } else if (type === 'error') {\n finish(reject, new Error(message))\n }\n }\n\n worker.onerror = (err) => finish(reject, new Error(err.message || 'Import worker xatosi'))\n\n try {\n // Transfer the ArrayBuffer zero-copy into the worker\n const buffer = await file.arrayBuffer()\n worker.postMessage(\n { buffer, existingColumns: this.columns, options },\n [buffer]\n )\n } catch (err) {\n finish(reject, err)\n }\n })\n },\n\n // =========================================================\n // UNDO / REDO (3.13)\n // =========================================================\n\n // Push one entry onto the non-reactive history stack.\n // Truncates the redo tail, caps at 100 entries.\n _pushHistory(entry) {\n if (this._historyIndex < this._history.length - 1) {\n this._history.splice(this._historyIndex + 1)\n }\n this._history.push(entry)\n if (this._history.length > 100) {\n this._history.shift()\n // historyIndex stays the same — it now points to the new last entry\n } else {\n this._historyIndex++\n }\n },\n\n // Emit 'input' and record a data diff entry (only changed row indices).\n // Use slice() + per-row spread in callers so unchanged rows keep the\n // same reference — then the identity check here catches only real changes.\n _emitDataChange(oldData, newData) {\n if (!this._historySkip) {\n const indices = []\n const beforeRows = []\n const afterRows = []\n const len = Math.max(oldData.length, newData.length)\n for (let i = 0; i < len; i++) {\n if (oldData[i] !== newData[i]) {\n indices.push(i)\n beforeRows.push(oldData[i])\n afterRows.push(newData[i])\n }\n }\n if (indices.length > 0) {\n this._pushHistory({\n type: 'data',\n before: { indices, rows: beforeRows },\n after: { indices, rows: afterRows }\n })\n }\n }\n this.$emit('input', newData)\n },\n\n // Emit 'update:merges' and record a merges diff entry.\n _emitMergeChange(oldMerges, newMerges) {\n if (!this._historySkip) {\n this._pushHistory({\n type: 'merges',\n before: oldMerges,\n after: newMerges\n })\n }\n this.$emit('update:merges', newMerges)\n },\n\n undo() {\n if (this._historyIndex < 0) return\n const entry = this._history[this._historyIndex--]\n this._historySkip = true\n try {\n if (entry.type === 'data') {\n const newData = this.data.slice()\n for (let i = 0; i < entry.before.indices.length; i++) {\n newData[entry.before.indices[i]] = entry.before.rows[i]\n }\n this.$emit('input', newData)\n } else if (entry.type === 'merges') {\n this.internalMerges = entry.before.slice()\n this.$emit('update:merges', entry.before.slice())\n }\n } finally {\n this._historySkip = false\n }\n },\n\n redo() {\n if (this._historyIndex >= this._history.length - 1) return\n const entry = this._history[++this._historyIndex]\n this._historySkip = true\n try {\n if (entry.type === 'data') {\n const newData = this.data.slice()\n for (let i = 0; i < entry.after.indices.length; i++) {\n newData[entry.after.indices[i]] = entry.after.rows[i]\n }\n this.$emit('input', newData)\n } else if (entry.type === 'merges') {\n this.internalMerges = entry.after.slice()\n this.$emit('update:merges', entry.after.slice())\n }\n } finally {\n this._historySkip = false\n }\n }\n }\n}\n<\/script>\n\n<style scoped>\n.eg-wrapper {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-sizing: border-box;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n}\n\n.eg-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.eg-header-area {\n display: flex;\n flex-shrink: 0;\n box-shadow: 0 2px 0 0 #d0d0d0;\n position: relative;\n z-index: 31;\n}\n\n.eg-header-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n border-right: 1px solid #ccc;\n border-bottom: 1px solid #ccc;\n background: #f0f0f0;\n font-size: 12px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n position: absolute;\n user-select: none;\n cursor: pointer;\n}\n\n.eg-header-cell:hover { background: #e0e0e0; }\n.eg-header-group { background: #e8eaf6; }\n\n.eg-col-resize-handle {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n width: 5px;\n cursor: col-resize;\n z-index: 5;\n}\n\n.eg-body-area {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n/* ===== FOOTER AREA (column totals) ===== */\n.eg-footer-area {\n border-top: 1px solid #ccc;\n}\n\n.eg-frozen-col-footer,\n.eg-scroll-col-footer,\n.eg-pinned-right-footer {\n position: relative;\n}\n\n.eg-footer-cell {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n font-weight: 600;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: #f8f9fa;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.eg-row {\n position: relative;\n}\n\n.eg-cell {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n border-bottom: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: white;\n cursor: default;\n display: flex;\n align-items: center;\n}\n\n.eg-cell-active {\n outline: 2px solid #1a73e8 !important;\n outline-offset: -2px;\n z-index: 3;\n}\n\n.eg-cell-selected {\n background: #e3f2fd !important;\n}\n\n.eg-cell-fill-preview {\n background: #bbdefb !important;\n outline: 1px dashed #1565c0;\n outline-offset: -1px;\n}\n\n.eg-cell-copy {\n outline: 1px dashed #1a73e8;\n outline-offset: -1px;\n}\n\n.eg-cell-merged-hidden {\n display: none !important;\n}\n\n.eg-cell-invalid {\n outline: 2px solid #e53935 !important;\n outline-offset: -2px;\n z-index: 4;\n}\n\n.eg-cell-input {\n width: 100%;\n height: 100%;\n border: none;\n outline: none;\n background: white;\n font-size: 13px;\n font-family: inherit;\n padding: 0;\n}\n\n.eg-row-header-cell {\n box-sizing: border-box;\n background: #f0f0f0;\n border-bottom: 1px solid #e0e0e0;\n border-right: 2px solid #bbb;\n text-align: center;\n font-size: 11px;\n color: #666;\n cursor: pointer;\n user-select: none;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.eg-row-resize-handle {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 4px;\n cursor: row-resize;\n z-index: 5;\n}\n\n.eg-fill-handle {\n position: absolute;\n width: 6px;\n height: 6px;\n background: #1a73e8;\n border: 1px solid white;\n cursor: crosshair;\n z-index: 10;\n}\n\n.eg-context-menu {\n position: fixed;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n z-index: 9999;\n min-width: 160px;\n font-size: 13px;\n padding: 4px 0;\n}\n\n.eg-context-item {\n padding: 6px 16px;\n cursor: pointer;\n position: relative;\n}\n\n.eg-context-item:hover { background: #f0f0f0; }\n\n.eg-context-separator {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n\n.eg-context-submenu-parent::after { content: ' ▶'; float: right; }\n\n.eg-context-submenu {\n display: none;\n position: absolute;\n left: 100%;\n top: -4px;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n min-width: 160px;\n padding: 4px 0;\n z-index: 10000;\n}\n\n.eg-context-submenu-parent:hover .eg-context-submenu { display: block; }\n\n.eg-group-btn {\n position: absolute;\n width: 16px;\n height: 16px;\n line-height: 14px;\n text-align: center;\n font-size: 10px;\n cursor: pointer;\n background: #e8eaf6;\n border: 1px solid #9fa8da;\n border-radius: 2px;\n z-index: 5;\n user-select: none;\n}\n\n.eg-group-btn.collapsed { background: #c5cae9; }\n\n.eg-drag-ghost {\n position: absolute;\n border: 2px dashed #1a73e8;\n pointer-events: none;\n z-index: 20;\n}\n\n.eg-resize-line {\n pointer-events: none;\n z-index: 9999;\n}\n\n/* Pin left separator shadow */\n.eg-frozen-col-body::after {\n content: '';\n position: absolute;\n top: 0;\n right: -4px;\n width: 4px;\n height: 100%;\n background: linear-gradient(to right, rgba(0,0,0,0.1), transparent);\n pointer-events: none;\n}\n\n.eg-pinned-right-body {\n position: sticky;\n right: 0;\n z-index: 10;\n background: white;\n flex-shrink: 0;\n}\n\n.eg-pinned-right-body::before {\n content: '';\n position: absolute;\n top: 0;\n left: -4px;\n bottom: 0;\n width: 4px;\n background: linear-gradient(to left, rgba(0,0,0,0.10), transparent);\n pointer-events: none;\n}\n\n.eg-pinned-right-header {\n overflow: hidden;\n flex-shrink: 0;\n border-left: 2px solid #bbb;\n background: #f0f0f0;\n}\n\n.eg-import-overlay {\n position: absolute;\n inset: 0;\n background: rgba(255, 255, 255, 0.85);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n border-radius: 4px;\n}\n\n.eg-import-dialog {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 24px 32px;\n min-width: 280px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.15);\n text-align: center;\n}\n\n.eg-import-title {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n margin-bottom: 16px;\n}\n\n.eg-import-bar-wrap {\n height: 8px;\n background: #e0e0e0;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 10px;\n}\n\n.eg-import-bar {\n height: 100%;\n background: linear-gradient(to right, #1a73e8, #4caf50);\n border-radius: 4px;\n transition: width 0.15s ease;\n}\n\n.eg-import-msg {\n font-size: 12px;\n color: #666;\n margin-bottom: 4px;\n}\n\n.eg-import-pct {\n font-size: 13px;\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-export-spinner {\n width: 32px;\n height: 32px;\n margin: 12px auto 0;\n border: 3px solid #e0e0e0;\n border-top-color: #1a73e8;\n border-radius: 50%;\n animation: eg-spin 0.8s linear infinite;\n}\n@keyframes eg-spin {\n to { transform: rotate(360deg); }\n}\n\n/* ===== READONLY CELL ===== */\n.eg-cell-readonly {\n background: #f8f8f8;\n cursor: default;\n}\n.eg-cell-readonly.eg-cell-selected {\n background: #dce8f5;\n}\n.eg-cell-readonly.eg-cell-active {\n outline-color: #aaa;\n}\n\n/* ===== SELECT DROPDOWN ===== */\n.eg-cell-select {\n cursor: pointer;\n}\n.eg-cell-select::after {\n content: '▾';\n position: absolute;\n right: 4px;\n top: 50%;\n transform: translateY(-50%);\n color: #aaa;\n font-size: 11px;\n pointer-events: none;\n line-height: 1;\n}\n\n/* ===== CHECKBOX CELL ===== */\n.eg-cell-checkbox {\n justify-content: center;\n padding: 0 2px;\n}\n.eg-checkbox {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n cursor: pointer;\n}\n.eg-checkbox-input {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.eg-checkbox-box {\n position: relative;\n display: block;\n width: 19px;\n height: 19px;\n box-sizing: border-box;\n background-color: #fff;\n background-repeat: no-repeat;\n background-position: center;\n background-size: 0 0;\n border: 1.5px solid #c2c8d1;\n border-radius: 4px;\n transition: background-color .12s ease, border-color .12s ease, background-size .12s ease;\n}\n.eg-checkbox:hover .eg-checkbox-box {\n border-color: #1a73e8;\n}\n.eg-checkbox-input:checked + .eg-checkbox-box {\n background-color: #1a73e8;\n background-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTMuNSA4LjNsMyAzIDYtNi42IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==\");\n background-size: 70% 70%;\n border-color: #1a73e8;\n}\n.eg-checkbox-input:focus-visible + .eg-checkbox-box {\n box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.2);\n}\n.eg-checkbox--disabled,\n.eg-checkbox--disabled .eg-checkbox-input {\n cursor: default;\n}\n.eg-checkbox-input:disabled + .eg-checkbox-box {\n background-color: #f0f1f3;\n border-color: #dadde2;\n}\n.eg-checkbox-input:disabled:checked + .eg-checkbox-box {\n background-color: #b9c2ce;\n border-color: #b9c2ce;\n}\n\n.eg-select-dropdown {\n background: white;\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.15);\n overflow-y: auto;\n max-height: 320px;\n padding: 4px 0;\n}\n.eg-select-search {\n padding: 6px 8px;\n border-bottom: 1px solid #ebebeb;\n position: sticky;\n top: 0;\n background: #fff;\n z-index: 1;\n}\n.eg-select-search-input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n padding: 4px 8px;\n font-size: 13px;\n outline: none;\n color: #333;\n background: #fff;\n font-family: inherit;\n}\n.eg-select-search-input:focus {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26,115,232,0.15);\n}\n\n.eg-select-option {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 12px;\n font-size: 13px;\n cursor: pointer;\n white-space: normal;\n word-break: break-word;\n user-select: none;\n color: #333;\n}\n.eg-select-option:hover,\n.eg-select-option--highlighted {\n background: #f0f4ff;\n}\n.eg-select-option--active {\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-select-option--active.eg-select-option--highlighted,\n.eg-select-option--active:hover {\n background: #e8f0fe;\n}\n\n.eg-validation-tooltip {\n background: #fff0f0;\n border: 1px solid #e53935;\n color: #c62828;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n white-space: nowrap;\n pointer-events: none;\n}\n\n.eg-select-dot {\n width: 9px;\n height: 9px;\n border-radius: 50%;\n flex-shrink: 0;\n display: inline-block;\n}\n\n.eg-tree-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n font-size: 10px;\n color: #888;\n margin-top: 1px;\n}\n.eg-tree-toggle--leaf {\n visibility: hidden;\n}\n\n/* ===== ROW TRANSITION ===== */\n.eg-row-enter-active,\n.eg-row-leave-active {\n transition: opacity 0.18s ease, transform 0.18s ease;\n}\n.eg-row-enter,\n.eg-row-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n\n/* ===== SIZE VARIANTS ===== */\n\n/* small */\n.eg-size-small .eg-cell {\n font-size: 12px;\n padding: 0 3px;\n}\n.eg-size-small .eg-cell-input {\n font-size: 12px;\n}\n.eg-size-small .eg-header-cell {\n font-size: 11px;\n}\n.eg-size-small .eg-footer-cell {\n font-size: 12px;\n}\n.eg-size-small .eg-row-header-cell {\n font-size: 10px;\n}\n.eg-size-small .eg-select-option {\n font-size: 12px;\n padding: 4px 10px;\n}\n.eg-size-small .eg-context-menu,\n.eg-size-small .eg-context-item {\n font-size: 12px;\n}\n.eg-size-small .eg-context-item {\n padding: 4px 14px;\n}\n\n/* large */\n.eg-size-large .eg-cell {\n font-size: 14px;\n padding: 0 6px;\n}\n.eg-size-large .eg-cell-input {\n font-size: 14px;\n}\n.eg-size-large .eg-header-cell {\n font-size: 13px;\n}\n.eg-size-large .eg-footer-cell {\n font-size: 14px;\n}\n.eg-size-large .eg-row-header-cell {\n font-size: 12px;\n}\n.eg-size-large .eg-select-option {\n font-size: 14px;\n padding: 8px 14px;\n}\n.eg-size-large .eg-context-menu,\n.eg-size-large .eg-context-item {\n font-size: 14px;\n}\n.eg-size-large .eg-context-item {\n padding: 8px 18px;\n}\n</style>\n"]},media:void 0})},G,"data-v-10cb7c27",false,void 0,!1,F,void 0,void 0);var K={install(e){e.component("ExcelGrid",P)}};e.ExcelGrid=P,e.default=K,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
6
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("xlsx")):"function"==typeof define&&define.amd?define(["exports","xlsx"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).Vue2ExcelGrid={},e.XLSX)}(this,function(e,n){"use strict";function t(e){if(e&&e.__esModule)return e;var n=Object.create(null);return e&&Object.keys(e).forEach(function(t){if("default"!==t){var i=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,i.get?i:{enumerable:!0,get:function(){return e[t]}})}}),n.default=e,Object.freeze(n)}var i=t(n);function o(e){return{startRow:Math.min(e.startRow,e.endRow),endRow:Math.max(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endCol:Math.max(e.startCol,e.endCol)}}function l(e,n,t){const i=o(t);return e>=i.startRow&&e<=i.endRow&&n>=i.startCol&&n<=i.endCol}function s(e,n){return{startRow:e,endRow:e,startCol:n,endCol:n}}function r(e){const n=[];return function e(t){for(const i of t)i.children&&i.children.length?e(i.children):n.push({...i,_flatIndex:n.length})}(e),n}function a(e){return function e(n){let t=0;for(const i of n)t=i.children&&i.children.length?Math.max(t,1+e(i.children)):Math.max(t,1);return t}(e)}function c(e,n){const t=Array.from({length:n},()=>[]);let i=0;return function e(o,l){for(const s of o)if(s.children&&s.children.length){const n=d(s),o=i;t[l].push({column:s,colspan:n,rowspan:1,isGroup:!0,_firstLeafFlatIndex:o}),e(s.children,l+1)}else{const e=n-l;t[l].push({column:{...s,_flatIndex:i},colspan:1,rowspan:e,isGroup:!1,_firstLeafFlatIndex:i}),i++}}(e,0),t}function d(e){return e.children&&0!==e.children.length?e.children.reduce((e,n)=>e+d(n),0):1}function h(e,n,t=[]){let i=0;for(let o=0;o<e;o++)i+=void 0!==t[o]?t[o]:n;return i}const p=["Dushanba","Seshanba","Chorshanba","Payshanba","Juma","Shanba","Yakshanba"],u=["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],g=["January","February","March","April","May","June","July","August","September","October","November","December"],f=/^\d{4}[-/]\d{2}[-/]\d{2}$/;function A(e){return"string"==typeof e&&f.test(e.trim())}function C(e){if("boolean"==typeof e)return!1;const n=String(e).trim();return""!==n&&(!A(n)&&!isNaN(Number(n)))}function w(e,n){const t=function(e){if(0===e.length)return{type:"copy",step:0,base:null};if(1===e.length)return{type:"copy",step:0,base:e[0]};const n=e.map(e=>p.indexOf(e));if(n.every(e=>e>=0))return{type:"weekday_uz",step:e.length>=2&&(n[1]-n[0]+7)%7||1,base:n[0]};const t=e.map(e=>u.indexOf(e));if(t.every(e=>e>=0))return{type:"weekday_en",step:e.length>=2&&(t[1]-t[0]+7)%7||1,base:t[0]};const i=e.map(e=>g.indexOf(e));if(i.every(e=>e>=0))return{type:"month",step:e.length>=2&&(i[1]-i[0]+12)%12||1,base:i[0]};if(e.every(A)){const n=e.map(e=>new Date(e).getTime());if(n.every(e=>!isNaN(e))){const e=n.slice(1).map((e,t)=>e-n[t]);if(e.every(n=>Math.abs(n-e[0])<864e5))return{type:"date",step:e[0],base:n[0]}}}if(e.every(C)){const n=e.map(e=>Number(e)),t=n.slice(1).map((e,t)=>e-n[t]);if(t.every(e=>Math.abs(e-t[0])<1e-10))return{type:"number",step:t[0],base:n[0]}}return{type:"copy",step:0,base:e[0]}}(e),i=e.length,o=[];for(let l=0;l<n;l++){const n=i+l;switch(t.type){case"number":o.push(t.base+t.step*n);break;case"weekday_uz":o.push(p[(t.base+t.step*n)%7]);break;case"weekday_en":o.push(u[(t.base+t.step*n)%7]);break;case"month":o.push(g[(t.base+t.step*n)%12]);break;case"date":{const e=new Date(t.base+t.step*n);o.push(e.toISOString().split("T")[0]);break}default:o.push(e[n%i])}}return o}function m(e,n,t){const i=[];for(let o=t.startRow;o<=t.endRow;o++){const l=[];for(let i=t.startCol;i<=t.endCol;i++){const t=n[i],s=t&&e[o]?e[o][t.key]:"";l.push(void 0!==s?s:"")}i.push(l)}return i}function v(e,n,t,i,o,l="all",s=null,r=null){const a=e.slice(),c=Math.min(t+o.length-1,e.length-1),d=Math.min(i+(o[0]?o[0].length-1:0),n.length-1),h=[];for(let p=t;p<=c;p++){const c=o[p-t];if(!c)continue;let u=null;for(let t=i;t<=d;t++){const o=n[t];if(o&&((s?s(p,t):!1!==o.editable)&&("all"===l||"values"===l))){const n=c[t-i],l=void 0!==n?n:"";if(r&&!r(p,t,l)){h.push({row:p,col:t,value:l});continue}u||(u={...e[p]||{}},a[p]=u),u[o.key]=l}}}return{newData:a,affectedRange:{startRow:t,endRow:c,startCol:i,endCol:d},skipped:h}}function x(e,n){const t=o(e);return n.some(e=>function(e,n){const t=o(e),i=o(n);return!(t.endRow<i.startRow||t.startRow>i.endRow||t.endCol<i.startCol||t.startCol>i.endCol)}(t,o(e)))}function y(e,n,t){return t.find(t=>{const i=o(t);return e>=i.startRow&&e<=i.endRow&&n>=i.startCol&&n<=i.endCol})||null}function b(e,n,t,i){if(!function(e,n){return Number.isInteger(e)&&Number.isInteger(n)&&e<=n&&e>=0}(n,t))return i;return i.find(i=>i.type===e&&i.startIndex===n&&i.endIndex===t)?i:[...i,{type:e,startIndex:n,endIndex:t}]}function R(e,n,t,i){return i.filter(i=>!(i.type===e&&i.startIndex===n&&i.endIndex===t))}function k(e,n){return n.some(n=>n.type===e.type&&n.startIndex===e.startIndex&&n.endIndex===e.endIndex)}function S(e){const n=[];for(const t of e||[])t.children&&t.children.length?n.push(...S(t.children)):t.key&&n.push(t);return n}function E(e,n,t,i,o){for(let l=n;l<=t;l++)for(let n=i;n<=o;n++)if(""!==((e[l]||[])[n]||"").toString().trim())return!0;return!1}function _(e,n,t){return String((e[n]||[])[t]||"").trim().replace(/\n/g," ")}function D(){return new Promise(e=>setTimeout(e,0))}async function M(e,n,t={},o){const l=(e,n)=>o&&o(e,n);l(5,"Fayl o'qilmoqda..."),await D();const s=await e.arrayBuffer();let r;l(20,"Excel tahlil qilinmoqda..."),await D();try{r=i.read(s,{type:"array",cellDates:!0})}catch(e){throw new Error("Excel fayl o'qib bo'lmadi: "+e.message)}const{sheetName:a}=t;let c;if(a){if(c=r.Sheets[a],!c)throw new Error(`Sheet "${a}" topilmadi`)}else{const e=r.SheetNames[0];if(!e)throw new Error("Workbook bo'sh");c=r.Sheets[e]}l(40,"Ma'lumotlar tayyorlanmoqda..."),await D();const d=i.utils.sheet_to_json(c,{header:1,raw:!1,defval:""});if(!d||0===d.length)throw new Error("Sheet bo'sh");const h=i.utils.decode_range(c["!ref"]||"A1:A1"),p=h.s.r,u=h.s.c,g=(c["!merges"]||[]).map(e=>({s:{r:e.s.r-p,c:e.s.c-u},e:{r:e.e.r-p,c:e.e.c-u}})),f=function(e,n){if(!e||0===e.length)return{headerRow:0,headerEndRow:0,dataStartRow:1};const t=e.map(e=>e.filter(e=>""!==e).length);n=n||[];let i,o,l,s=-1;for(let n=0;n<Math.min(e.length,20);n++){const t=e[n].filter(e=>""!==e);if(t.length<2)continue;const i=t.map(e=>{const n=parseInt(String(e).trim(),10);return isNaN(n)?null:n});if(!i.some(e=>null===e)&&i.every((e,n)=>e===n+1)){s=n;break}}if(s>=0){for(l=s+1,o=s-1;o>=0&&0===t[o];)o--;for(o<0&&(o=0),i=o;i>0&&t[i-1]>1;)i--}else{i=0;for(let n=0;n<Math.min(e.length,20);n++)if(t[n]>1){i=n;break}let s=1;for(const e of n)e.s.r===i&&e.e.r>e.s.r&&(s=Math.max(s,e.e.r-e.s.r+1));o=i+s-1,l=o+1}for(;l<e.length&&0===t[l];)l++;return{headerRow:i,headerEndRow:o,dataStartRow:l}}(d,g),A=null!=t.headerRow?t.headerRow:f.headerRow,C=null!=t.headerEndRow?t.headerEndRow:f.headerEndRow,w=null!=t.dataStartRow?t.dataStartRow:f.dataStartRow,m=S(n),v=new Set,{columns:x,colMap:y}=function(e,n,t,i,o,l){let s=-1;for(let i=n;i<=t;i++)for(let n=(e[i]||[]).length-1;n>=0;n--)if(""!==((e[i]||[])[n]||"")){s=Math.max(s,n);break}const r=(i||[]).filter(e=>e.s.r>=n&&e.s.r<=t);for(const e of r)s=Math.max(s,e.e.c);if(s<0)return{columns:[],colMap:[]};const a=[];function c(e,n){const t=o.find(n=>n.title&&n.title.toLowerCase()===e.toLowerCase());if(t)return{key:t.key,title:t.title,width:t.width||100};let i=String(e||"").toLowerCase().replace(/\s+/g,"_").replace(/[^a-z0-9_]/g,"").replace(/^_+|_+$/g,"").replace(/_+/g,"_").replace(/^(\d)/,"col_$1")||`col_${n}`,s=i,r=1;for(;l.has(s)||o.some(e=>e.key===s);)s=`${i}_${r++}`;return l.add(s),{key:s,title:e||s,width:100}}const d=function n(t,i,o,l){if(t>i||o>l)return[];const s=[];let d=o;for(;d<=l;){if(r.some(e=>e.s.r<=t&&e.e.r>=t&&e.s.c<d&&e.e.c>=d)){d++;continue}const o=r.find(e=>e.s.r===t&&e.s.c===d),h=o?_(e,o.s.r,o.s.c):_(e,t,d);if(o&&o.e.c>d){const t=Math.min(o.e.c,l),r=o.e.r+1;if(r<=i&&E(e,r,i,d,t)){const e=n(r,i,d,t);s.push({title:h,children:e})}else for(let e=d;e<=t;e++){const n=c(h,e);s.push(n),a.push({key:n.key,colIndex:e})}d=t+1}else if(o&&o.e.r>=i){const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d}),d++}else if(o){const t=o.e.r+1;if(t<=i&&E(e,t,i,d,d)){const e=n(t,i,d,d);s.push({title:h,children:e})}else{const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d})}d++}else if(t===i){const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d}),d++}else{if(E(e,t+1,i,d,d)){const e=n(t+1,i,d,d);s.push({title:h,children:e})}else{const e=c(h,d);s.push(e),a.push({key:e.key,colIndex:d})}d++}}return s}(n,t,0,s);return{columns:d,colMap:a}}(d,A,C,g,m,v),b=Math.max(d.length-w,0);l(55,`Qatorlar qayta ishlanmoqda (0 / ${b})...`),await D();const R=[];for(let e=w;e<d.length;e+=2e3){const n=Math.min(e+2e3,d.length);for(let t=e;t<n;t++){const e=d[t],n={};for(const{key:t,colIndex:i}of y)n[t]=void 0!==e[i]?e[i]:"";R.push(n)}const t=Math.min(n-w,b);l(55+Math.round(40*t/Math.max(b,1)),`Qatorlar: ${t} / ${b}`),await D()}const k=g.filter(e=>e.s.r>=w).map(e=>({startRow:e.s.r-w,startCol:e.s.c,endRow:e.e.r-w,endCol:e.e.c})).filter(e=>e.startRow>=0&&e.startCol>=0);return l(100,"Tayyor!"),{data:R,columns:x,merges:k}}const z={en:{copy:"Copy",cut:"Cut",paste:"Paste",pasteSpecial:"Paste Special",pasteAll:"All (Value + Format)",pasteValues:"Values Only",pasteFormats:"Formats Only",transpose:"Transpose",mergeCells:"Merge Cells",unmergeCells:"Unmerge Cells",hideRows:"Hide Row(s)",hideColumns:"Hide Column(s)",unhideRows:"Unhide All Rows",unhideColumns:"Unhide All Columns",pinLeft:"Pin Left",pinRight:"Pin Right",unpin:"Unpin",invalidValue:"Invalid value",total:"Total"},ru:{copy:"Копировать",cut:"Вырезать",paste:"Вставить",pasteSpecial:"Специальная вставка",pasteAll:"Всё (значение + формат)",pasteValues:"Только значения",pasteFormats:"Только форматы",transpose:"Транспонировать",mergeCells:"Объединить ячейки",unmergeCells:"Разъединить ячейки",hideRows:"Скрыть строку(и)",hideColumns:"Скрыть столбец(цы)",unhideRows:"Показать все строки",unhideColumns:"Показать все столбцы",pinLeft:"Закрепить слева",pinRight:"Закрепить справа",unpin:"Открепить",invalidValue:"Недопустимое значение",total:"Итого"},uz:{copy:"Nusxalash",cut:"Kesib olish",paste:"Joylashtirish",pasteSpecial:"Maxsus joylashtirish",pasteAll:"Hammasi (qiymat + format)",pasteValues:"Faqat qiymatlar",pasteFormats:"Faqat formatlar",transpose:"Transpozitsiya",mergeCells:"Katakchalarni birlashtirish",unmergeCells:"Katakchalarni ajratish",hideRows:"Qatorlarni yashirish",hideColumns:"Ustunlarni yashirish",unhideRows:"Barcha qatorlarni ko'rsatish",unhideColumns:"Barcha ustunlarni ko'rsatish",pinLeft:"Chapga pin qilish",pinRight:"O'ngga pin qilish",unpin:"Pindan olish",invalidValue:"Noto'g'ri qiymat",total:"Jami"},"uz-Cyrl":{copy:"Нусхалаш",cut:"Кесиб олиш",paste:"Жойлаштириш",pasteSpecial:"Махсус жойлаштириш",pasteAll:"Ҳаммаси (қиймат + формат)",pasteValues:"Фақат қийматлар",pasteFormats:"Фақат форматлар",transpose:"Транспозиция",mergeCells:"Катакчаларни бирлаштириш",unmergeCells:"Катакчаларни ажратиш",hideRows:"Қаторларни яшириш",hideColumns:"Устунларни яшириш",unhideRows:"Барча қаторларни кўрсатиш",unhideColumns:"Барча устунларни кўрсатиш",pinLeft:"Чапга пин қилиш",pinRight:"Ўнгга пин қилиш",unpin:"Пиндан олиш",invalidValue:"Нотўғри қиймат",total:"Жами"}};var I={name:"EgCheckbox",model:{prop:"value",event:"input"},props:{value:{default:!1},trueValue:{default:!0},falseValue:{default:!1},disabled:{type:Boolean,default:!1}},computed:{isChecked(){return this.value===this.trueValue}},methods:{onChange(e){const n=e.target.checked?this.trueValue:this.falseValue;this.$emit("input",n),this.$emit("change",n)}}};function H(e,n,t,i,o,l,s,r,a,c){"boolean"!=typeof s&&(a=r,r=s,s=!1);const d="function"==typeof t?t.options:t;let h;if(e&&e.render&&(d.render=e.render,d.staticRenderFns=e.staticRenderFns,d._compiled=!0,o&&(d.functional=!0)),i&&(d._scopeId=i),l?(h=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(l)},d._ssrRegister=h):n&&(h=s?function(e){n.call(this,c(e,this.$root.$options.shadowRoot))}:function(e){n.call(this,r(e))}),h)if(d.functional){const e=d.render;d.render=function(n,t){return h.call(t),e(n,t)}}else{const e=d.beforeCreate;d.beforeCreate=e?[].concat(e,h):[h]}return t}const B="undefined"!=typeof navigator&&/msie [6-9]\\b/.test(navigator.userAgent.toLowerCase());function V(e){return(e,n)=>function(e,n){const t=B?n.media||"default":e,i=O[t]||(O[t]={ids:new Set,styles:[]});if(!i.ids.has(e)){i.ids.add(e);let t=n.source;if(n.map&&(t+="\n/*# sourceURL="+n.map.sources[0]+" */",t+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(n.map))))+" */"),i.element||(i.element=document.createElement("style"),i.element.type="text/css",n.media&&i.element.setAttribute("media",n.media),void 0===L&&(L=document.head||document.getElementsByTagName("head")[0]),L.appendChild(i.element)),"styleSheet"in i.element)i.styles.push(t),i.element.styleSheet.cssText=i.styles.filter(Boolean).join("\n");else{const e=i.ids.size-1,n=document.createTextNode(t),o=i.element.childNodes;o[e]&&i.element.removeChild(o[e]),o.length?i.element.insertBefore(n,o[e]):i.element.appendChild(n)}}}(e,n)}let L;const O={};const $=I;var W=function(){var e=this,n=e.$createElement,t=e._self._c||n;return t("label",{staticClass:"eg-checkbox",class:{"eg-checkbox--disabled":e.disabled}},[t("input",{staticClass:"eg-checkbox-input",attrs:{type:"checkbox",disabled:e.disabled},domProps:{checked:e.isChecked},on:{change:e.onChange}}),e._v(" "),t("span",{staticClass:"eg-checkbox-box"})])};W._withStripped=!0;const F=H({render:W,staticRenderFns:[]},function(e){e&&e("data-v-48d91ddc_0",{source:'\n.eg-checkbox {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n cursor: pointer;\n}\n.eg-checkbox-input {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.eg-checkbox-box {\n position: relative;\n display: block;\n width: 19px;\n height: 19px;\n box-sizing: border-box;\n background-color: #fff;\n background-repeat: no-repeat;\n background-position: center;\n background-size: 0 0;\n border: 1.5px solid #c2c8d1;\n border-radius: 4px;\n transition: background-color .12s ease, border-color .12s ease, background-size .12s ease;\n}\n.eg-checkbox:hover .eg-checkbox-box {\n border-color: #1a73e8;\n}\n.eg-checkbox-input:checked + .eg-checkbox-box {\n background-color: #1a73e8;\n background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTMuNSA4LjNsMyAzIDYtNi42IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==");\n background-size: 70% 70%;\n border-color: #1a73e8;\n}\n.eg-checkbox-input:focus-visible + .eg-checkbox-box {\n box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.2);\n}\n.eg-checkbox--disabled,\n.eg-checkbox--disabled .eg-checkbox-input {\n cursor: default;\n}\n.eg-checkbox-input:disabled + .eg-checkbox-box {\n background-color: #f0f1f3;\n border-color: #dadde2;\n}\n.eg-checkbox-input:disabled:checked + .eg-checkbox-box {\n background-color: #b9c2ce;\n border-color: #b9c2ce;\n}\n',map:{version:3,sources:["/Users/macmini/Projects/Realsoft/vue2-excel-grid/src/components/EgCheckbox.vue"],names:[],mappings:";AAuCA;EACA,kBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,WAAA;EACA,YAAA;EACA,eAAA;AACA;AACA;EACA,kBAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;EACA,UAAA;EACA,eAAA;AACA;AACA;EACA,kBAAA;EACA,cAAA;EACA,WAAA;EACA,YAAA;EACA,sBAAA;EACA,sBAAA;EACA,4BAAA;EACA,2BAAA;EACA,oBAAA;EACA,2BAAA;EACA,kBAAA;EACA,yFAAA;AACA;AACA;EACA,qBAAA;AACA;AACA;EACA,yBAAA;EACA,+SAAA;EACA,wBAAA;EACA,qBAAA;AACA;AACA;EACA,6CAAA;AACA;AACA;;EAEA,eAAA;AACA;AACA;EACA,yBAAA;EACA,qBAAA;AACA;AACA;EACA,yBAAA;EACA,qBAAA;AACA",file:"EgCheckbox.vue",sourcesContent:['<template>\n <label class="eg-checkbox" :class="{ \'eg-checkbox--disabled\': disabled }">\n <input\n type="checkbox"\n class="eg-checkbox-input"\n :checked="isChecked"\n :disabled="disabled"\n @change="onChange"\n />\n <span class="eg-checkbox-box"></span>\n </label>\n</template>\n\n<script>\nexport default {\n name: \'EgCheckbox\',\n model: { prop: \'value\', event: \'input\' },\n props: {\n value: { default: false },\n trueValue: { default: true },\n falseValue: { default: false },\n disabled: { type: Boolean, default: false }\n },\n computed: {\n isChecked() {\n return this.value === this.trueValue\n }\n },\n methods: {\n onChange(e) {\n const val = e.target.checked ? this.trueValue : this.falseValue\n this.$emit(\'input\', val)\n this.$emit(\'change\', val)\n }\n }\n}\n<\/script>\n\n<style>\n.eg-checkbox {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n cursor: pointer;\n}\n.eg-checkbox-input {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.eg-checkbox-box {\n position: relative;\n display: block;\n width: 19px;\n height: 19px;\n box-sizing: border-box;\n background-color: #fff;\n background-repeat: no-repeat;\n background-position: center;\n background-size: 0 0;\n border: 1.5px solid #c2c8d1;\n border-radius: 4px;\n transition: background-color .12s ease, border-color .12s ease, background-size .12s ease;\n}\n.eg-checkbox:hover .eg-checkbox-box {\n border-color: #1a73e8;\n}\n.eg-checkbox-input:checked + .eg-checkbox-box {\n background-color: #1a73e8;\n background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTMuNSA4LjNsMyAzIDYtNi42IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==");\n background-size: 70% 70%;\n border-color: #1a73e8;\n}\n.eg-checkbox-input:focus-visible + .eg-checkbox-box {\n box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.2);\n}\n.eg-checkbox--disabled,\n.eg-checkbox--disabled .eg-checkbox-input {\n cursor: default;\n}\n.eg-checkbox-input:disabled + .eg-checkbox-box {\n background-color: #f0f1f3;\n border-color: #dadde2;\n}\n.eg-checkbox-input:disabled:checked + .eg-checkbox-box {\n background-color: #b9c2ce;\n border-color: #b9c2ce;\n}\n</style>\n']},media:void 0})},$,void 0,false,void 0,!1,V,void 0,void 0);const T={name:"EgSelect",model:{prop:"value",event:"input"},props:{value:{default:null},options:{type:Array,default:()=>[]},enableSearch:{type:Boolean,default:!1},placeholder:{type:String,default:""},disabled:{type:Boolean,default:!1},dropdownOnly:{type:Boolean,default:!1},triggerRect:{type:Object,default:null}},data:()=>({open:!1,searchQuery:"",highlight:-1}),computed:{normalizedOptions(){return(this.options||[]).map(e=>"string"==typeof e?{value:e,label:e}:{value:e.value,label:null!=e.label?e.label:String(e.value),color:e.color})},filteredOptions(){if(!this.enableSearch||!this.searchQuery.trim())return this.normalizedOptions;const e=this.searchQuery.trim().toLowerCase();return this.normalizedOptions.filter(n=>String(n.label||"").toLowerCase().includes(e))},selectedOpt(){const e=String(null==this.value?"":this.value);return this.normalizedOptions.find(n=>String(n.value)===e)||null},overlayStyle(){if(!this.dropdownOnly||!this.triggerRect)return{position:"fixed",zIndex:9999};const e=this.triggerRect,n=this.enableSearch?42:0,t=Math.min(28*this.filteredOptions.length+8+n,320);return{position:"fixed",top:(window.innerHeight-e.bottom>=t?e.bottom:e.top-t)+"px",left:e.left+"px",minWidth:Math.max(e.width,120)+"px",maxWidth:Math.min(360,window.innerWidth-e.left-16)+"px",zIndex:9999}}},watch:{open(e){e&&this.enableSearch&&this.$nextTick(()=>{this.$refs.searchInput&&this.$refs.searchInput.focus()})}},mounted(){if(this.dropdownOnly){this.$el&&this.$el.parentNode&&document.body.appendChild(this.$el);const e=String(null==this.value?"":this.value);this.highlight=this.normalizedOptions.findIndex(n=>String(n.value)===e),this.enableSearch&&this.$nextTick(()=>{this.$refs.searchInput&&this.$refs.searchInput.focus()})}else document.addEventListener("click",this._onDocClick)},beforeDestroy(){this.dropdownOnly||document.removeEventListener("click",this._onDocClick)},methods:{toggle(){if(!this.disabled&&(this.open=!this.open,this.open)){const e=String(null==this.value?"":this.value);this.highlight=this.normalizedOptions.findIndex(n=>String(n.value)===e),this.searchQuery=""}},pickOption(e){this.$emit("input",e.value),this.$emit("select",{value:e.value,label:e.label}),this.dropdownOnly||(this.open=!1,this.searchQuery="",this.highlight=-1)},onSearch(e){this.searchQuery=e.target.value,this.highlight=0},onSearchKey(e){const n=e.key;if("Escape"===n)e.preventDefault(),this.dropdownOnly?this.$emit("close"):(this.open=!1,this.searchQuery="");else if("ArrowDown"===n)e.preventDefault(),this.highlight=Math.min((this.highlight||0)+1,this.filteredOptions.length-1);else if("ArrowUp"===n)e.preventDefault(),this.highlight=Math.max((this.highlight||0)-1,0);else if("Enter"===n){e.preventDefault();const n=this.filteredOptions[this.highlight];n&&this.pickOption(n)}},handleKey(e){const n=e.key;if("Escape"===n)e.preventDefault(),this.$emit("close");else if("Enter"===n||" "===n){e.preventDefault();const n=this.filteredOptions[this.highlight];n?this.pickOption(n):this.$emit("close")}else"ArrowDown"===n?(e.preventDefault(),this.highlight=Math.min((this.highlight||0)+1,this.filteredOptions.length-1)):"ArrowUp"===n&&(e.preventDefault(),this.highlight=Math.max((this.highlight||0)-1,0))},onRootMouseDown(e){this.dropdownOnly&&e.preventDefault()},_onDocClick(e){this.open&&(this.$el&&this.$el.contains(e.target)||(this.open=!1))}}};var P=function(){var e=this,n=e.$createElement,t=e._self._c||n;return t("div",{class:e.dropdownOnly?"eg-select-dropdown":"eg-select-wrapper",style:e.dropdownOnly?e.overlayStyle:null,on:{mousedown:e.onRootMouseDown}},[e.dropdownOnly?e._e():t("div",{staticClass:"eg-select-trigger",class:{"eg-select-trigger--disabled":e.disabled,"eg-select-trigger--open":e.open},on:{click:function(n){return n.stopPropagation(),e.toggle.apply(null,arguments)}}},[e.selectedOpt&&e.selectedOpt.color?t("span",{staticClass:"eg-select-dot",style:{background:e.selectedOpt.color}}):e._e(),e._v(" "),t("span",{staticClass:"eg-select-trigger-label",class:{"eg-select-placeholder":!e.selectedOpt}},[e._v("\n "+e._s(e.selectedOpt?e.selectedOpt.label:e.placeholder)+"\n ")]),e._v(" "),t("span",{staticClass:"eg-select-caret"},[e._v("▾")])]),e._v(" "),e.dropdownOnly?[e.enableSearch?t("div",{staticClass:"eg-select-search",on:{mousedown:function(e){e.stopPropagation()}}},[t("input",{ref:"searchInput",staticClass:"eg-select-search-input",attrs:{placeholder:"Qidirish...",autocomplete:"off",autocorrect:"off",autocapitalize:"off",spellcheck:"false"},domProps:{value:e.searchQuery},on:{input:e.onSearch,keydown:function(n){return n.stopPropagation(),e.onSearchKey.apply(null,arguments)}}})]):e._e(),e._v(" "),e._l(e.filteredOptions,function(n,i){return t("div",{key:String(n.value),staticClass:"eg-select-option",class:{"eg-select-option--active":String(n.value)===String(e.value),"eg-select-option--highlighted":i===e.highlight},on:{mousedown:function(t){return t.preventDefault(),e.pickOption(n)},mouseover:function(n){e.highlight=i}}},[n.color?t("span",{staticClass:"eg-select-dot",style:{background:n.color}}):e._e(),e._v("\n "+e._s(n.label)+"\n ")])})]:e.open?t("div",{staticClass:"eg-select-dropdown eg-select-dropdown--floating",on:{mousedown:function(e){e.stopPropagation(),e.preventDefault()}}},[e.enableSearch?t("div",{staticClass:"eg-select-search",on:{mousedown:function(e){e.stopPropagation()}}},[t("input",{ref:"searchInput",staticClass:"eg-select-search-input",attrs:{placeholder:"Qidirish...",autocomplete:"off",autocorrect:"off",autocapitalize:"off",spellcheck:"false"},domProps:{value:e.searchQuery},on:{input:e.onSearch,keydown:function(n){return n.stopPropagation(),e.onSearchKey.apply(null,arguments)}}})]):e._e(),e._v(" "),e._l(e.filteredOptions,function(n,i){return t("div",{key:String(n.value),staticClass:"eg-select-option",class:{"eg-select-option--active":String(n.value)===String(e.value),"eg-select-option--highlighted":i===e.highlight},on:{mousedown:function(t){return t.preventDefault(),e.pickOption(n)},mouseover:function(n){e.highlight=i}}},[n.color?t("span",{staticClass:"eg-select-dot",style:{background:n.color}}):e._e(),e._v("\n "+e._s(n.label)+"\n ")])})],2):e._e()],2)};P._withStripped=!0;const G=H({render:P,staticRenderFns:[]},function(e){e&&e("data-v-524aca00_0",{source:"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/* Standalone wrapper */\n.eg-select-wrapper {\n position: relative;\n display: inline-block;\n min-width: 120px;\n font-size: 13px;\n font-family: inherit;\n}\n.eg-select-trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-height: 30px;\n padding: 4px 8px;\n box-sizing: border-box;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n background: #fff;\n cursor: pointer;\n user-select: none;\n color: #333;\n transition: border-color .15s ease, box-shadow .15s ease;\n}\n.eg-select-trigger:hover {\n border-color: #1a73e8;\n}\n.eg-select-trigger--open {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26,115,232,0.15);\n}\n.eg-select-trigger--disabled {\n background: #f5f5f5;\n cursor: default;\n color: #aaa;\n}\n.eg-select-trigger-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.eg-select-placeholder {\n color: #aaa;\n}\n.eg-select-caret {\n flex-shrink: 0;\n color: #888;\n font-size: 12px;\n}\n\n/* Dropdown panel */\n.eg-select-dropdown {\n background: #fff;\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.15);\n overflow-y: auto;\n max-height: 320px;\n padding: 4px 0;\n}\n.eg-select-dropdown--floating {\n position: absolute;\n top: 100%;\n left: 0;\n min-width: 100%;\n z-index: 999;\n margin-top: 2px;\n}\n\n/* Search input inside dropdown */\n.eg-select-search {\n padding: 6px 8px;\n border-bottom: 1px solid #ebebeb;\n position: sticky;\n top: 0;\n background: #fff;\n z-index: 1;\n}\n.eg-select-search-input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n padding: 4px 8px;\n font-size: 13px;\n outline: none;\n color: #333;\n background: #fff;\n font-family: inherit;\n}\n.eg-select-search-input:focus {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26,115,232,0.15);\n}\n\n/* Option rows */\n.eg-select-option {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 12px;\n font-size: 13px;\n cursor: pointer;\n white-space: normal;\n word-break: break-word;\n user-select: none;\n color: #333;\n}\n.eg-select-option:hover,\n.eg-select-option--highlighted {\n background: #f0f4ff;\n}\n.eg-select-option--active {\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-select-option--active.eg-select-option--highlighted,\n.eg-select-option--active:hover {\n background: #e8f0fe;\n}\n\n/* Color dot */\n.eg-select-dot {\n width: 9px;\n height: 9px;\n border-radius: 50%;\n flex-shrink: 0;\n display: inline-block;\n margin-top: 3px;\n}\n",map:{version:3,sources:["/Users/macmini/Projects/Realsoft/vue2-excel-grid/src/components/EgSelect.vue"],names:[],mappings:";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqPA,uBAAA;AACA;EACA,kBAAA;EACA,qBAAA;EACA,gBAAA;EACA,eAAA;EACA,oBAAA;AACA;AACA;EACA,aAAA;EACA,mBAAA;EACA,QAAA;EACA,WAAA;EACA,gBAAA;EACA,gBAAA;EACA,sBAAA;EACA,yBAAA;EACA,kBAAA;EACA,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,WAAA;EACA,wDAAA;AACA;AACA;EACA,qBAAA;AACA;AACA;EACA,qBAAA;EACA,2CAAA;AACA;AACA;EACA,mBAAA;EACA,eAAA;EACA,WAAA;AACA;AACA;EACA,OAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;AACA;AACA;EACA,WAAA;AACA;AACA;EACA,cAAA;EACA,WAAA;EACA,eAAA;AACA;;AAEA,mBAAA;AACA;EACA,gBAAA;EACA,yBAAA;EACA,kBAAA;EACA,uCAAA;EACA,gBAAA;EACA,iBAAA;EACA,cAAA;AACA;AACA;EACA,kBAAA;EACA,SAAA;EACA,OAAA;EACA,eAAA;EACA,YAAA;EACA,eAAA;AACA;;AAEA,iCAAA;AACA;EACA,gBAAA;EACA,gCAAA;EACA,gBAAA;EACA,MAAA;EACA,gBAAA;EACA,UAAA;AACA;AACA;EACA,WAAA;EACA,sBAAA;EACA,yBAAA;EACA,kBAAA;EACA,gBAAA;EACA,eAAA;EACA,aAAA;EACA,WAAA;EACA,gBAAA;EACA,oBAAA;AACA;AACA;EACA,qBAAA;EACA,2CAAA;AACA;;AAEA,gBAAA;AACA;EACA,aAAA;EACA,uBAAA;EACA,QAAA;EACA,iBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,sBAAA;EACA,iBAAA;EACA,WAAA;AACA;AACA;;EAEA,mBAAA;AACA;AACA;EACA,gBAAA;EACA,cAAA;AACA;AACA;;EAEA,mBAAA;AACA;;AAEA,cAAA;AACA;EACA,UAAA;EACA,WAAA;EACA,kBAAA;EACA,cAAA;EACA,qBAAA;EACA,eAAA;AACA",file:"EgSelect.vue",sourcesContent:['<template>\n <div\n :class="dropdownOnly ? \'eg-select-dropdown\' : \'eg-select-wrapper\'"\n :style="dropdownOnly ? overlayStyle : null"\n @mousedown="onRootMouseDown"\n >\n \x3c!-- Standalone mode: trigger button --\x3e\n <div\n v-if="!dropdownOnly"\n class="eg-select-trigger"\n :class="{ \'eg-select-trigger--disabled\': disabled, \'eg-select-trigger--open\': open }"\n @click.stop="toggle"\n >\n <span v-if="selectedOpt && selectedOpt.color" class="eg-select-dot" :style="{ background: selectedOpt.color }"></span>\n <span class="eg-select-trigger-label" :class="{ \'eg-select-placeholder\': !selectedOpt }">\n {{ selectedOpt ? selectedOpt.label : placeholder }}\n </span>\n <span class="eg-select-caret">▾</span>\n </div>\n\n \x3c!-- Options panel in grid dropdown-only mode (root IS the dropdown panel) --\x3e\n <template v-if="dropdownOnly">\n <div v-if="enableSearch" class="eg-select-search" @mousedown.stop>\n <input\n ref="searchInput"\n class="eg-select-search-input"\n :value="searchQuery"\n placeholder="Qidirish..."\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n spellcheck="false"\n @input="onSearch"\n @keydown.stop="onSearchKey"\n />\n </div>\n <div\n v-for="(opt, idx) in filteredOptions"\n :key="String(opt.value)"\n class="eg-select-option"\n :class="{\n \'eg-select-option--active\': String(opt.value) === String(value),\n \'eg-select-option--highlighted\': idx === highlight\n }"\n @mousedown.prevent="pickOption(opt)"\n @mouseover="highlight = idx"\n >\n <span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>\n {{ opt.label }}\n </div>\n </template>\n\n \x3c!-- Options panel in standalone mode (floating dropdown below trigger) --\x3e\n <div\n v-else-if="open"\n class="eg-select-dropdown eg-select-dropdown--floating"\n @mousedown.stop.prevent\n >\n <div v-if="enableSearch" class="eg-select-search" @mousedown.stop>\n <input\n ref="searchInput"\n class="eg-select-search-input"\n :value="searchQuery"\n placeholder="Qidirish..."\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n spellcheck="false"\n @input="onSearch"\n @keydown.stop="onSearchKey"\n />\n </div>\n <div\n v-for="(opt, idx) in filteredOptions"\n :key="String(opt.value)"\n class="eg-select-option"\n :class="{\n \'eg-select-option--active\': String(opt.value) === String(value),\n \'eg-select-option--highlighted\': idx === highlight\n }"\n @mousedown.prevent="pickOption(opt)"\n @mouseover="highlight = idx"\n >\n <span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>\n {{ opt.label }}\n </div>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n name: \'EgSelect\',\n model: { prop: \'value\', event: \'input\' },\n props: {\n value: { default: null },\n options: { type: Array, default: () => [] },\n enableSearch: { type: Boolean, default: false },\n placeholder: { type: String, default: \'\' },\n disabled: { type: Boolean, default: false },\n dropdownOnly: { type: Boolean, default: false },\n triggerRect: { type: Object, default: null }\n },\n data() {\n return {\n open: false,\n searchQuery: \'\',\n highlight: -1\n }\n },\n computed: {\n normalizedOptions() {\n return (this.options || []).map(o =>\n typeof o === \'string\'\n ? { value: o, label: o }\n : { value: o.value, label: o.label != null ? o.label : String(o.value), color: o.color }\n )\n },\n filteredOptions() {\n if (!this.enableSearch || !this.searchQuery.trim()) return this.normalizedOptions\n const q = this.searchQuery.trim().toLowerCase()\n return this.normalizedOptions.filter(o => String(o.label || \'\').toLowerCase().includes(q))\n },\n selectedOpt() {\n const v = String(this.value == null ? \'\' : this.value)\n return this.normalizedOptions.find(o => String(o.value) === v) || null\n },\n overlayStyle() {\n if (!this.dropdownOnly || !this.triggerRect) return { position: \'fixed\', zIndex: 9999 }\n const rect = this.triggerRect\n const searchH = this.enableSearch ? 42 : 0\n const approxH = Math.min(this.filteredOptions.length * 28 + 8 + searchH, 320)\n const spaceBelow = window.innerHeight - rect.bottom\n const top = spaceBelow >= approxH ? rect.bottom : rect.top - approxH\n return {\n position: \'fixed\',\n top: top + \'px\',\n left: rect.left + \'px\',\n minWidth: Math.max(rect.width, 120) + \'px\',\n maxWidth: Math.min(360, window.innerWidth - rect.left - 16) + \'px\',\n zIndex: 9999\n }\n }\n },\n watch: {\n open(val) {\n if (val && this.enableSearch) {\n this.$nextTick(() => { this.$refs.searchInput && this.$refs.searchInput.focus() })\n }\n }\n },\n mounted() {\n if (this.dropdownOnly) {\n // Portal: move to document.body so position:fixed is always relative to viewport,\n // regardless of any CSS transform on ancestor elements in the host project.\n // Vue\'s removeNode uses el.parentNode, so cleanup works correctly after the move.\n if (this.$el && this.$el.parentNode) {\n document.body.appendChild(this.$el)\n }\n const v = String(this.value == null ? \'\' : this.value)\n this.highlight = this.normalizedOptions.findIndex(o => String(o.value) === v)\n if (this.enableSearch) {\n this.$nextTick(() => { this.$refs.searchInput && this.$refs.searchInput.focus() })\n }\n } else {\n document.addEventListener(\'click\', this._onDocClick)\n }\n },\n beforeDestroy() {\n if (!this.dropdownOnly) {\n document.removeEventListener(\'click\', this._onDocClick)\n }\n },\n methods: {\n toggle() {\n if (this.disabled) return\n this.open = !this.open\n if (this.open) {\n const v = String(this.value == null ? \'\' : this.value)\n this.highlight = this.normalizedOptions.findIndex(o => String(o.value) === v)\n this.searchQuery = \'\'\n }\n },\n pickOption(opt) {\n this.$emit(\'input\', opt.value)\n this.$emit(\'select\', { value: opt.value, label: opt.label })\n if (!this.dropdownOnly) {\n this.open = false\n this.searchQuery = \'\'\n this.highlight = -1\n }\n },\n onSearch(e) {\n this.searchQuery = e.target.value\n this.highlight = 0\n },\n onSearchKey(e) {\n const key = e.key\n if (key === \'Escape\') {\n e.preventDefault()\n if (this.dropdownOnly) this.$emit(\'close\')\n else { this.open = false; this.searchQuery = \'\' }\n } else if (key === \'ArrowDown\') {\n e.preventDefault()\n this.highlight = Math.min((this.highlight || 0) + 1, this.filteredOptions.length - 1)\n } else if (key === \'ArrowUp\') {\n e.preventDefault()\n this.highlight = Math.max((this.highlight || 0) - 1, 0)\n } else if (key === \'Enter\') {\n e.preventDefault()\n const opt = this.filteredOptions[this.highlight]\n if (opt) this.pickOption(opt)\n }\n },\n handleKey(e) {\n const key = e.key\n if (key === \'Escape\') {\n e.preventDefault()\n this.$emit(\'close\')\n } else if (key === \'Enter\' || key === \' \') {\n e.preventDefault()\n const opt = this.filteredOptions[this.highlight]\n if (opt) this.pickOption(opt)\n else this.$emit(\'close\')\n } else if (key === \'ArrowDown\') {\n e.preventDefault()\n this.highlight = Math.min((this.highlight || 0) + 1, this.filteredOptions.length - 1)\n } else if (key === \'ArrowUp\') {\n e.preventDefault()\n this.highlight = Math.max((this.highlight || 0) - 1, 0)\n }\n },\n onRootMouseDown(e) {\n if (this.dropdownOnly) e.preventDefault()\n },\n _onDocClick(e) {\n if (!this.open) return\n if (this.$el && this.$el.contains(e.target)) return\n this.open = false\n }\n }\n}\n<\/script>\n\n<style>\n/* Standalone wrapper */\n.eg-select-wrapper {\n position: relative;\n display: inline-block;\n min-width: 120px;\n font-size: 13px;\n font-family: inherit;\n}\n.eg-select-trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-height: 30px;\n padding: 4px 8px;\n box-sizing: border-box;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n background: #fff;\n cursor: pointer;\n user-select: none;\n color: #333;\n transition: border-color .15s ease, box-shadow .15s ease;\n}\n.eg-select-trigger:hover {\n border-color: #1a73e8;\n}\n.eg-select-trigger--open {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26,115,232,0.15);\n}\n.eg-select-trigger--disabled {\n background: #f5f5f5;\n cursor: default;\n color: #aaa;\n}\n.eg-select-trigger-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.eg-select-placeholder {\n color: #aaa;\n}\n.eg-select-caret {\n flex-shrink: 0;\n color: #888;\n font-size: 12px;\n}\n\n/* Dropdown panel */\n.eg-select-dropdown {\n background: #fff;\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.15);\n overflow-y: auto;\n max-height: 320px;\n padding: 4px 0;\n}\n.eg-select-dropdown--floating {\n position: absolute;\n top: 100%;\n left: 0;\n min-width: 100%;\n z-index: 999;\n margin-top: 2px;\n}\n\n/* Search input inside dropdown */\n.eg-select-search {\n padding: 6px 8px;\n border-bottom: 1px solid #ebebeb;\n position: sticky;\n top: 0;\n background: #fff;\n z-index: 1;\n}\n.eg-select-search-input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n padding: 4px 8px;\n font-size: 13px;\n outline: none;\n color: #333;\n background: #fff;\n font-family: inherit;\n}\n.eg-select-search-input:focus {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26,115,232,0.15);\n}\n\n/* Option rows */\n.eg-select-option {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 12px;\n font-size: 13px;\n cursor: pointer;\n white-space: normal;\n word-break: break-word;\n user-select: none;\n color: #333;\n}\n.eg-select-option:hover,\n.eg-select-option--highlighted {\n background: #f0f4ff;\n}\n.eg-select-option--active {\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-select-option--active.eg-select-option--highlighted,\n.eg-select-option--active:hover {\n background: #e8f0fe;\n}\n\n/* Color dot */\n.eg-select-dot {\n width: 9px;\n height: 9px;\n border-radius: 50%;\n flex-shrink: 0;\n display: inline-block;\n margin-top: 3px;\n}\n</style>\n']},media:void 0})},T,void 0,false,void 0,!1,V,void 0,void 0);const K={name:"EgTreeSelect",model:{prop:"value",event:"input"},props:{value:{default:null},options:{type:Array,default:()=>[]},valueKey:{type:String,default:"value"},labelKey:{type:String,default:"label"},childrenKey:{type:String,default:"children"},enableSearch:{type:Boolean,default:!1},placeholder:{type:String,default:""},disabled:{type:Boolean,default:!1},dropdownOnly:{type:Boolean,default:!1},triggerRect:{type:Object,default:null}},data:()=>({open:!1,searchQuery:"",highlight:-1,expandedMap:{}}),computed:{keys(){return{valueKey:this.valueKey,labelKey:this.labelKey,childrenKey:this.childrenKey}},flatOptions(){return this._treeFlatten(this.options||[],this.keys,this.expandedMap,0,[])},allFlatOptions(){return this.enableSearch?this._treeFlattenAll(this.options||[],this.keys):null},filteredOptions(){if(!this.enableSearch||!this.searchQuery.trim())return this.flatOptions;const e=this.searchQuery.trim().toLowerCase();return(this.allFlatOptions||this.flatOptions).filter(n=>String(n.label||"").toLowerCase().includes(e))},selectedLabel(){const e=String(null==this.value?"":this.value);if(!e)return null;const n=this._treeFindLabel(this.options||[],this.keys,e);return null!=n?n:null},overlayStyle(){if(!this.dropdownOnly||!this.triggerRect)return{position:"fixed",zIndex:9999};const e=this.triggerRect,n=this.enableSearch?42:0,t=Math.min(28*this.filteredOptions.length+8+n,320);return{position:"fixed",top:(window.innerHeight-e.bottom>=t?e.bottom:e.top-t)+"px",left:e.left+"px",minWidth:Math.max(e.width,120)+"px",maxWidth:Math.min(360,window.innerWidth-e.left-16)+"px",zIndex:9999}}},watch:{open(e){e&&this.enableSearch&&this.$nextTick(()=>{this.$refs.searchInput&&this.$refs.searchInput.focus()})}},mounted(){this.dropdownOnly?(this.$el&&this.$el.parentNode&&document.body.appendChild(this.$el),this._initExpanded(),this.enableSearch&&this.$nextTick(()=>{this.$refs.searchInput&&this.$refs.searchInput.focus()})):document.addEventListener("click",this._onDocClick)},beforeDestroy(){this.dropdownOnly||document.removeEventListener("click",this._onDocClick)},methods:{_initExpanded(){const e=String(null==this.value?"":this.value),n=this._treeFindPath(this.options||[],this.keys,e,[])||[],t={};n.forEach(e=>{t[e]=!0}),this.expandedMap=t;const i=this.flatOptions;this.highlight=i.findIndex(n=>String(n.value)===e)},toggle(){this.disabled||(this.open=!this.open,this.open&&(this.searchQuery="",this._initExpanded()))},pickOption(e){this.$emit("input",e.value),this.$emit("select",{value:e.value,label:e.label}),this.dropdownOnly||(this.open=!1,this.searchQuery="",this.highlight=-1)},toggleNode(e){const n=this.flatOptions[e];if(!n||!n.hasChildren)return;const t={...this.expandedMap};t[n.id]?delete t[n.id]:t[n.id]=!0,this.expandedMap=t;const i=this.flatOptions.findIndex(e=>e.id===n.id);this.highlight=i>=0?i:this.highlight},onSearch(e){this.searchQuery=e.target.value,this.highlight=0},onSearchKey(e){const n=e.key;if("Escape"===n)e.preventDefault(),this.dropdownOnly?this.$emit("close"):(this.open=!1,this.searchQuery="");else if("ArrowDown"===n)e.preventDefault(),this.highlight=Math.min((this.highlight||0)+1,this.filteredOptions.length-1);else if("ArrowUp"===n)e.preventDefault(),this.highlight=Math.max((this.highlight||0)-1,0);else if("Enter"===n){e.preventDefault();const n=this.filteredOptions[this.highlight];n&&this.pickOption(n)}},handleKey(e){const n=e.key;if("Escape"===n)e.preventDefault(),this.$emit("close");else if("Enter"===n||" "===n){e.preventDefault();const n=this.filteredOptions[this.highlight];n?this.pickOption(n):this.$emit("close")}else if("ArrowDown"===n)e.preventDefault(),this.highlight=Math.min((this.highlight||0)+1,this.filteredOptions.length-1);else if("ArrowUp"===n)e.preventDefault(),this.highlight=Math.max((this.highlight||0)-1,0);else if(!this.searchQuery&&("ArrowRight"===n||"ArrowLeft"===n)){e.preventDefault();const t=this.flatOptions[this.highlight];t&&t.hasChildren&&("ArrowRight"!==n||t.expanded||this.toggleNode(this.highlight),"ArrowLeft"===n&&t.expanded&&this.toggleNode(this.highlight))}},onRootMouseDown(e){this.dropdownOnly&&e.preventDefault()},_onDocClick(e){this.open&&(this.$el&&this.$el.contains(e.target)||(this.open=!1))},_treeFlatten(e,n,t,i,o){let l=[];return(e||[]).forEach((e,s)=>{const r=o.concat(s),a=r.join("-"),c=Array.isArray(e[n.childrenKey])?e[n.childrenKey]:[],d=c.length>0,h=d&&!!t[a];l.push({value:e[n.valueKey],label:null!=e[n.labelKey]?e[n.labelKey]:String(e[n.valueKey]),color:e.color,id:a,depth:i,hasChildren:d,expanded:h}),h&&(l=l.concat(this._treeFlatten(c,n,t,i+1,r)))}),l},_treeFlattenAll(e,n){const t=[],i=e=>{(e||[]).forEach(e=>{const o=Array.isArray(e[n.childrenKey])?e[n.childrenKey]:[];t.push({value:e[n.valueKey],label:null!=e[n.labelKey]?e[n.labelKey]:String(e[n.valueKey]),color:e.color,id:String(e[n.valueKey]),depth:0,hasChildren:!1,expanded:!1}),o.length&&i(o)})};return i(e),t},_treeFindLabel(e,n,t){for(const i of e||[]){if(String(i[n.valueKey])===t)return null!=i[n.labelKey]?i[n.labelKey]:String(i[n.valueKey]);const e=this._treeFindLabel(i[n.childrenKey],n,t);if(null!=e)return e}return null},_treeFindPath(e,n,t,i){for(let o=0;o<(e||[]).length;o++){const l=e[o],s=i.concat(o);if(String(l[n.valueKey])===t)return[s.join("-")];const r=l[n.childrenKey];if(Array.isArray(r)){const e=this._treeFindPath(r,n,t,s);if(e)return[s.join("-")].concat(e)}}return null}}};var j=function(){var e=this,n=e.$createElement,t=e._self._c||n;return t("div",{class:e.dropdownOnly?"eg-select-dropdown":"eg-select-wrapper",style:e.dropdownOnly?e.overlayStyle:null,on:{mousedown:e.onRootMouseDown}},[e.dropdownOnly?e._e():t("div",{staticClass:"eg-select-trigger",class:{"eg-select-trigger--disabled":e.disabled,"eg-select-trigger--open":e.open},on:{click:function(n){return n.stopPropagation(),e.toggle.apply(null,arguments)}}},[t("span",{staticClass:"eg-select-trigger-label",class:{"eg-select-placeholder":null==e.selectedLabel}},[e._v("\n "+e._s(null!=e.selectedLabel?e.selectedLabel:e.placeholder)+"\n ")]),e._v(" "),t("span",{staticClass:"eg-select-caret"},[e._v("▾")])]),e._v(" "),e.dropdownOnly?[e.enableSearch?t("div",{staticClass:"eg-select-search",on:{mousedown:function(e){e.stopPropagation()}}},[t("input",{ref:"searchInput",staticClass:"eg-select-search-input",attrs:{placeholder:"Qidirish...",autocomplete:"off",autocorrect:"off",autocapitalize:"off",spellcheck:"false"},domProps:{value:e.searchQuery},on:{input:e.onSearch,keydown:function(n){return n.stopPropagation(),e.onSearchKey.apply(null,arguments)}}})]):e._e(),e._v(" "),e._l(e.filteredOptions,function(n,i){return t("div",{key:null!=n.id?n.id:String(n.value),staticClass:"eg-select-option",class:{"eg-select-option--active":String(n.value)===String(e.value),"eg-select-option--highlighted":i===e.highlight},style:e.searchQuery?null:{paddingLeft:12+16*n.depth+"px"},on:{mousedown:function(t){return t.preventDefault(),e.pickOption(n)},mouseover:function(n){e.highlight=i}}},[e.searchQuery?e._e():t("span",{staticClass:"eg-tree-toggle",class:{"eg-tree-toggle--leaf":!n.hasChildren},on:{mousedown:function(n){return n.preventDefault(),n.stopPropagation(),e.toggleNode(i)}}},[e._v(e._s(n.hasChildren?n.expanded?"▾":"▸":""))]),e._v(" "),n.color?t("span",{staticClass:"eg-select-dot",style:{background:n.color}}):e._e(),e._v("\n "+e._s(n.label)+"\n ")])})]:e.open?t("div",{staticClass:"eg-select-dropdown eg-select-dropdown--floating",on:{mousedown:function(e){e.stopPropagation(),e.preventDefault()}}},[e.enableSearch?t("div",{staticClass:"eg-select-search",on:{mousedown:function(e){e.stopPropagation()}}},[t("input",{ref:"searchInput",staticClass:"eg-select-search-input",attrs:{placeholder:"Qidirish...",autocomplete:"off",autocorrect:"off",autocapitalize:"off",spellcheck:"false"},domProps:{value:e.searchQuery},on:{input:e.onSearch,keydown:function(n){return n.stopPropagation(),e.onSearchKey.apply(null,arguments)}}})]):e._e(),e._v(" "),e._l(e.filteredOptions,function(n,i){return t("div",{key:null!=n.id?n.id:String(n.value),staticClass:"eg-select-option",class:{"eg-select-option--active":String(n.value)===String(e.value),"eg-select-option--highlighted":i===e.highlight},style:e.searchQuery?null:{paddingLeft:12+16*n.depth+"px"},on:{mousedown:function(t){return t.preventDefault(),e.pickOption(n)},mouseover:function(n){e.highlight=i}}},[e.searchQuery?e._e():t("span",{staticClass:"eg-tree-toggle",class:{"eg-tree-toggle--leaf":!n.hasChildren},on:{mousedown:function(n){return n.preventDefault(),n.stopPropagation(),e.toggleNode(i)}}},[e._v(e._s(n.hasChildren?n.expanded?"▾":"▸":""))]),e._v(" "),n.color?t("span",{staticClass:"eg-select-dot",style:{background:n.color}}):e._e(),e._v("\n "+e._s(n.label)+"\n ")])})],2):e._e()],2)};j._withStripped=!0;const U=H({render:j,staticRenderFns:[]},function(e){e&&e("data-v-7628cbd0_0",{source:"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/* Tree toggle */\n.eg-tree-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n font-size: 10px;\n color: #888;\n margin-top: 1px;\n}\n.eg-tree-toggle--leaf {\n visibility: hidden;\n}\n",map:{version:3,sources:["/Users/macmini/Projects/Realsoft/vue2-excel-grid/src/components/EgTreeSelect.vue"],names:[],mappings:";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqVA,gBAAA;AACA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,eAAA;EACA,WAAA;EACA,eAAA;AACA;AACA;EACA,kBAAA;AACA",file:"EgTreeSelect.vue",sourcesContent:['<template>\n <div\n :class="dropdownOnly ? \'eg-select-dropdown\' : \'eg-select-wrapper\'"\n :style="dropdownOnly ? overlayStyle : null"\n @mousedown="onRootMouseDown"\n >\n \x3c!-- Standalone mode: trigger button --\x3e\n <div\n v-if="!dropdownOnly"\n class="eg-select-trigger"\n :class="{ \'eg-select-trigger--disabled\': disabled, \'eg-select-trigger--open\': open }"\n @click.stop="toggle"\n >\n <span class="eg-select-trigger-label" :class="{ \'eg-select-placeholder\': selectedLabel == null }">\n {{ selectedLabel != null ? selectedLabel : placeholder }}\n </span>\n <span class="eg-select-caret">▾</span>\n </div>\n\n \x3c!-- Options panel in grid dropdown-only mode --\x3e\n <template v-if="dropdownOnly">\n <div v-if="enableSearch" class="eg-select-search" @mousedown.stop>\n <input\n ref="searchInput"\n class="eg-select-search-input"\n :value="searchQuery"\n placeholder="Qidirish..."\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n spellcheck="false"\n @input="onSearch"\n @keydown.stop="onSearchKey"\n />\n </div>\n <div\n v-for="(opt, idx) in filteredOptions"\n :key="opt.id != null ? opt.id : String(opt.value)"\n class="eg-select-option"\n :class="{\n \'eg-select-option--active\': String(opt.value) === String(value),\n \'eg-select-option--highlighted\': idx === highlight\n }"\n :style="!searchQuery ? { paddingLeft: (12 + opt.depth * 16) + \'px\' } : null"\n @mousedown.prevent="pickOption(opt)"\n @mouseover="highlight = idx"\n >\n <span\n v-if="!searchQuery"\n class="eg-tree-toggle"\n :class="{ \'eg-tree-toggle--leaf\': !opt.hasChildren }"\n @mousedown.prevent.stop="toggleNode(idx)"\n >{{ opt.hasChildren ? (opt.expanded ? \'▾\' : \'▸\') : \'\' }}</span>\n <span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>\n {{ opt.label }}\n </div>\n </template>\n\n \x3c!-- Options panel in standalone mode --\x3e\n <div\n v-else-if="open"\n class="eg-select-dropdown eg-select-dropdown--floating"\n @mousedown.stop.prevent\n >\n <div v-if="enableSearch" class="eg-select-search" @mousedown.stop>\n <input\n ref="searchInput"\n class="eg-select-search-input"\n :value="searchQuery"\n placeholder="Qidirish..."\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n spellcheck="false"\n @input="onSearch"\n @keydown.stop="onSearchKey"\n />\n </div>\n <div\n v-for="(opt, idx) in filteredOptions"\n :key="opt.id != null ? opt.id : String(opt.value)"\n class="eg-select-option"\n :class="{\n \'eg-select-option--active\': String(opt.value) === String(value),\n \'eg-select-option--highlighted\': idx === highlight\n }"\n :style="!searchQuery ? { paddingLeft: (12 + opt.depth * 16) + \'px\' } : null"\n @mousedown.prevent="pickOption(opt)"\n @mouseover="highlight = idx"\n >\n <span\n v-if="!searchQuery"\n class="eg-tree-toggle"\n :class="{ \'eg-tree-toggle--leaf\': !opt.hasChildren }"\n @mousedown.prevent.stop="toggleNode(idx)"\n >{{ opt.hasChildren ? (opt.expanded ? \'▾\' : \'▸\') : \'\' }}</span>\n <span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>\n {{ opt.label }}\n </div>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n name: \'EgTreeSelect\',\n model: { prop: \'value\', event: \'input\' },\n props: {\n value: { default: null },\n options: { type: Array, default: () => [] },\n valueKey: { type: String, default: \'value\' },\n labelKey: { type: String, default: \'label\' },\n childrenKey: { type: String, default: \'children\' },\n enableSearch: { type: Boolean, default: false },\n placeholder: { type: String, default: \'\' },\n disabled: { type: Boolean, default: false },\n dropdownOnly: { type: Boolean, default: false },\n triggerRect: { type: Object, default: null }\n },\n data() {\n return {\n open: false,\n searchQuery: \'\',\n highlight: -1,\n expandedMap: {}\n }\n },\n computed: {\n keys() {\n return { valueKey: this.valueKey, labelKey: this.labelKey, childrenKey: this.childrenKey }\n },\n flatOptions() {\n return this._treeFlatten(this.options || [], this.keys, this.expandedMap, 0, [])\n },\n allFlatOptions() {\n if (!this.enableSearch) return null\n return this._treeFlattenAll(this.options || [], this.keys)\n },\n filteredOptions() {\n if (!this.enableSearch || !this.searchQuery.trim()) return this.flatOptions\n const q = this.searchQuery.trim().toLowerCase()\n const pool = this.allFlatOptions || this.flatOptions\n return pool.filter(o => String(o.label || \'\').toLowerCase().includes(q))\n },\n selectedLabel() {\n const v = String(this.value == null ? \'\' : this.value)\n if (!v) return null\n const found = this._treeFindLabel(this.options || [], this.keys, v)\n return found != null ? found : null\n },\n overlayStyle() {\n if (!this.dropdownOnly || !this.triggerRect) return { position: \'fixed\', zIndex: 9999 }\n const rect = this.triggerRect\n const searchH = this.enableSearch ? 42 : 0\n const approxH = Math.min(this.filteredOptions.length * 28 + 8 + searchH, 320)\n const spaceBelow = window.innerHeight - rect.bottom\n const top = spaceBelow >= approxH ? rect.bottom : rect.top - approxH\n return {\n position: \'fixed\',\n top: top + \'px\',\n left: rect.left + \'px\',\n minWidth: Math.max(rect.width, 120) + \'px\',\n maxWidth: Math.min(360, window.innerWidth - rect.left - 16) + \'px\',\n zIndex: 9999\n }\n }\n },\n watch: {\n open(val) {\n if (val && this.enableSearch) {\n this.$nextTick(() => { this.$refs.searchInput && this.$refs.searchInput.focus() })\n }\n }\n },\n mounted() {\n if (this.dropdownOnly) {\n // Portal: move to document.body so position:fixed is always relative to viewport,\n // regardless of any CSS transform on ancestor elements in the host project.\n // Vue\'s removeNode uses el.parentNode, so cleanup works correctly after the move.\n if (this.$el && this.$el.parentNode) {\n document.body.appendChild(this.$el)\n }\n this._initExpanded()\n if (this.enableSearch) {\n this.$nextTick(() => { this.$refs.searchInput && this.$refs.searchInput.focus() })\n }\n } else {\n document.addEventListener(\'click\', this._onDocClick)\n }\n },\n beforeDestroy() {\n if (!this.dropdownOnly) {\n document.removeEventListener(\'click\', this._onDocClick)\n }\n },\n methods: {\n _initExpanded() {\n const v = String(this.value == null ? \'\' : this.value)\n const path = this._treeFindPath(this.options || [], this.keys, v, []) || []\n const expandedMap = {}\n path.forEach(id => { expandedMap[id] = true })\n this.expandedMap = expandedMap\n const flat = this.flatOptions\n this.highlight = flat.findIndex(o => String(o.value) === v)\n },\n toggle() {\n if (this.disabled) return\n this.open = !this.open\n if (this.open) {\n this.searchQuery = \'\'\n this._initExpanded()\n }\n },\n pickOption(opt) {\n this.$emit(\'input\', opt.value)\n this.$emit(\'select\', { value: opt.value, label: opt.label })\n if (!this.dropdownOnly) {\n this.open = false\n this.searchQuery = \'\'\n this.highlight = -1\n }\n },\n toggleNode(idx) {\n const opt = this.flatOptions[idx]\n if (!opt || !opt.hasChildren) return\n const next = { ...this.expandedMap }\n if (next[opt.id]) delete next[opt.id]\n else next[opt.id] = true\n this.expandedMap = next\n const newFlat = this.flatOptions\n const newIdx = newFlat.findIndex(o => o.id === opt.id)\n this.highlight = newIdx >= 0 ? newIdx : this.highlight\n },\n onSearch(e) {\n this.searchQuery = e.target.value\n this.highlight = 0\n },\n onSearchKey(e) {\n const key = e.key\n if (key === \'Escape\') {\n e.preventDefault()\n if (this.dropdownOnly) this.$emit(\'close\')\n else { this.open = false; this.searchQuery = \'\' }\n } else if (key === \'ArrowDown\') {\n e.preventDefault()\n this.highlight = Math.min((this.highlight || 0) + 1, this.filteredOptions.length - 1)\n } else if (key === \'ArrowUp\') {\n e.preventDefault()\n this.highlight = Math.max((this.highlight || 0) - 1, 0)\n } else if (key === \'Enter\') {\n e.preventDefault()\n const opt = this.filteredOptions[this.highlight]\n if (opt) this.pickOption(opt)\n }\n },\n handleKey(e) {\n const key = e.key\n if (key === \'Escape\') {\n e.preventDefault()\n this.$emit(\'close\')\n } else if (key === \'Enter\' || key === \' \') {\n e.preventDefault()\n const opt = this.filteredOptions[this.highlight]\n if (opt) this.pickOption(opt)\n else this.$emit(\'close\')\n } else if (key === \'ArrowDown\') {\n e.preventDefault()\n this.highlight = Math.min((this.highlight || 0) + 1, this.filteredOptions.length - 1)\n } else if (key === \'ArrowUp\') {\n e.preventDefault()\n this.highlight = Math.max((this.highlight || 0) - 1, 0)\n } else if (!this.searchQuery && (key === \'ArrowRight\' || key === \'ArrowLeft\')) {\n e.preventDefault()\n const opt = this.flatOptions[this.highlight]\n if (opt && opt.hasChildren) {\n if (key === \'ArrowRight\' && !opt.expanded) this.toggleNode(this.highlight)\n if (key === \'ArrowLeft\' && opt.expanded) this.toggleNode(this.highlight)\n }\n }\n },\n onRootMouseDown(e) {\n if (this.dropdownOnly) e.preventDefault()\n },\n _onDocClick(e) {\n if (!this.open) return\n if (this.$el && this.$el.contains(e.target)) return\n this.open = false\n },\n _treeFlatten(nodes, keys, expandedMap, depth, pathArr) {\n let out = []\n ;(nodes || []).forEach((node, idx) => {\n const path = pathArr.concat(idx)\n const id = path.join(\'-\')\n const children = Array.isArray(node[keys.childrenKey]) ? node[keys.childrenKey] : []\n const hasChildren = children.length > 0\n const expanded = hasChildren && !!expandedMap[id]\n out.push({ value: node[keys.valueKey], label: node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey]), color: node.color, id, depth, hasChildren, expanded })\n if (expanded) out = out.concat(this._treeFlatten(children, keys, expandedMap, depth + 1, path))\n })\n return out\n },\n _treeFlattenAll(nodes, keys) {\n const result = []\n const walk = (list) => {\n ;(list || []).forEach(node => {\n const children = Array.isArray(node[keys.childrenKey]) ? node[keys.childrenKey] : []\n result.push({ value: node[keys.valueKey], label: node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey]), color: node.color, id: String(node[keys.valueKey]), depth: 0, hasChildren: false, expanded: false })\n if (children.length) walk(children)\n })\n }\n walk(nodes)\n return result\n },\n _treeFindLabel(nodes, keys, value) {\n for (const node of (nodes || [])) {\n if (String(node[keys.valueKey]) === value) {\n return node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey])\n }\n const found = this._treeFindLabel(node[keys.childrenKey], keys, value)\n if (found != null) return found\n }\n return null\n },\n _treeFindPath(nodes, keys, value, pathArr) {\n for (let i = 0; i < (nodes || []).length; i++) {\n const node = nodes[i]\n const path = pathArr.concat(i)\n if (String(node[keys.valueKey]) === value) return [path.join(\'-\')]\n const children = node[keys.childrenKey]\n if (Array.isArray(children)) {\n const sub = this._treeFindPath(children, keys, value, path)\n if (sub) return [path.join(\'-\')].concat(sub)\n }\n }\n return null\n }\n }\n}\n<\/script>\n\n<style>\n/* Tree toggle */\n.eg-tree-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n font-size: 10px;\n color: #888;\n margin-top: 1px;\n}\n.eg-tree-toggle--leaf {\n visibility: hidden;\n}\n</style>\n']},media:void 0})},K,void 0,false,void 0,!1,V,void 0,void 0),N={small:{row:22,header:24,footer:24},middle:{row:28,header:28,footer:28},large:{row:36,header:36,footer:36}},Y=50,Q=20;const q={name:"ExcelGrid",components:{EgCheckbox:F,EgSelect:G,EgTreeSelect:U},model:{prop:"data",event:"input"},props:{data:{type:Array,default:()=>[]},columns:{type:Array,default:()=>[]},hiddenColumns:{type:Array,default:()=>[]},hiddenRows:{type:Array,default:()=>[]},merges:{type:Array,default:()=>[]},collapsedGroups:{type:Array,default:()=>[]},height:{type:Number,default:400},width:{type:[Number,String],default:"100%"},beforeDrop:{type:Function,default:null},locale:{type:String,default:"en"},pinnedColumns:{type:Object,default:()=>({})},cellEditable:{type:Function,default:null},cellValidator:{type:Function,default:null},cellValidationMessage:{type:[Function,String],default:null},strictBulkValidation:{type:Boolean,default:!1},size:{type:String,default:"middle"},transitionEnabled:{type:Boolean,default:!1},autoWidth:{type:Boolean,default:!1}},data:()=>({activeCell:{row:0,col:0},selectionRanges:[],isMouseDown:!1,mouseDownRange:null,isCtrlDrag:!1,editingCell:null,editValue:"",editOriginalValue:"",scrollTop:0,scrollLeft:0,headerViewportWidth:null,virtualStartIndex:0,virtualEndIndex:0,virtualOffsetTop:0,totalVirtualHeight:0,colWidths:{},rowHeights:{},importState:{active:!1,percent:0,message:""},exportState:{active:!1},_isScrolling:!1,resizing:{active:!1,type:null,index:-1,startX:0,startY:0,startSize:0,currentPos:0},fillDrag:{active:!1,sourceRange:null,currentRow:-1,currentCol:-1},dragMove:{active:!1,sourceRange:null,startX:0,startY:0,targetRow:-1,targetCol:-1,copyMode:!1},clipboardMatrix:null,clipboardRange:null,clipboardMode:null,contextMenu:{visible:!1,x:0,y:0,row:-1,col:-1},selectDropdown:null,invalidCell:null,internalMerges:[],internalGroups:[],internalCollapsedGroups:[],headerDepth:1,headerRows:[],leafCols:[],columnTotals:{}}),computed:{t(){return e=this.locale,z[e]||z.en;var e},pinnedLeftSet(){const e=new Set;return this.leafCols.forEach(n=>{"left"===this.pinnedColumns[n.key]&&e.add(n._flatIndex)}),e},pinnedRightSet(){const e=new Set;return this.leafCols.forEach(n=>{"right"===this.pinnedColumns[n.key]&&e.add(n._flatIndex)}),e},pinnedLeftCols(){return this.leafCols.filter(e=>this.pinnedLeftSet.has(e._flatIndex))},pinnedRightCols(){return this.leafCols.filter(e=>this.pinnedRightSet.has(e._flatIndex))},hasPinnedLeft(){return this.pinnedLeftSet.size>0},hasPinnedRight(){return this.pinnedRightSet.size>0},hasFooter(){return this.leafCols.some(e=>e.showTotal)},pinnedLeftWidth(){let e=0;for(const n of this.pinnedLeftCols)this.isColHidden(n._flatIndex)||(e+=this.getColW(n._flatIndex));return e},pinnedRightWidth(){let e=0;for(const n of this.pinnedRightCols)this.isColHidden(n._flatIndex)||(e+=this.getColW(n._flatIndex));return e},totalRows(){return this.data.length},_rowTransitionName(){return this.transitionEnabled&&!this._isScrolling?"eg-row":""},_sizeConf(){return N[this.size]||N.middle},_rowH(){return this._sizeConf.row},_headerRowH(){return this._sizeConf.header},_footerRowH(){return this._sizeConf.footer},headerRowHeight(){return this._headerRowH},footerRowHeight(){return this._footerRowH},cumulativeHeights(){return function(e,n,t={},i=new Set){const o=new Array(e+1);o[0]=0;for(let l=0;l<e;l++){const e=i.has(l)?0:void 0!==t[l]?t[l]:n;o[l+1]=o[l]+e}return o}(this.totalRows,this._rowH,this.rowHeights,this.hiddenRowSet)},hiddenColSet(){return new Set(this.hiddenColumns)},hiddenRowSet(){const e=new Set(this.hiddenRows);return function(e,n){const t=new Set;for(const i of e)if("row"===i.type&&k(i,n))for(let e=i.startIndex;e<=i.endIndex;e++)t.add(e);return t}(this.internalGroups,this.internalCollapsedGroups).forEach(n=>e.add(n)),e},hiddenColFromGroupSet(){return function(e,n){const t=new Set;for(const i of e)if("column"===i.type&&k(i,n))for(let e=i.startIndex;e<=i.endIndex;e++)t.add(e);return t}(this.internalGroups,this.internalCollapsedGroups)},effectiveHiddenColSet(){const e=new Set(this.hiddenColSet);return this.hiddenColFromGroupSet.forEach(n=>e.add(n)),e},rowGroups(){return this.internalGroups.filter(e=>"row"===e.type)},colGroups(){return this.internalGroups.filter(e=>"column"===e.type)},visibleRowRange(){const e=[];for(let n=this.virtualStartIndex;n<=this.virtualEndIndex;n++)e.push(n);return e},primaryRange(){return function(e,n){if(!e||0===e.length)return null;return o(e.find(e=>l(n.row,n.col,e))||e[0])}(this.selectionRanges,this.activeCell)},fillHandleVisible(){return this.primaryRange&&!this.editingCell},fillHandleStyle(){if(!this.primaryRange)return{};const e=o(this.primaryRange),n=this.cumulativeHeights[e.endRow+1]||0,t=this.getColLeft(e.endCol+1);return{position:"absolute",width:"6px",height:"6px",background:"#1a73e8",border:"1px solid white",bottom:this.totalVirtualHeight-n+1+"px",right:this.scrollContentWidth-t+1+"px",cursor:"crosshair",zIndex:10}},invalidTooltipStyle(){if(!this.invalidCell||!this.invalidCell.rect)return{};const{rect:e}=this.invalidCell;return{position:"fixed",top:e.bottom+4+"px",left:e.left+"px",zIndex:9999}},dragGhostStyle(){if(!this.dragMove.active||this.dragMove.targetRow<0)return{};const e=o(this.dragMove.sourceRange),n=e.endRow-e.startRow,t=e.endCol-e.startCol;return{position:"absolute",top:(this.cumulativeHeights[this.dragMove.targetRow]||0)+"px",left:this.getColLeft(this.dragMove.targetCol)+"px",width:this.getRangeWidth(this.dragMove.targetCol,this.dragMove.targetCol+t)+"px",height:this.getRangeHeight(this.dragMove.targetRow,this.dragMove.targetRow+n)+"px",border:"2px dashed #1a73e8",pointerEvents:"none",zIndex:20}},wrapperStyle(){return{width:"number"==typeof this.width?this.width+"px":this.width,height:this.height+"px",position:"relative",outline:"none",userSelect:"none"}},headerAreaStyle:()=>({display:"flex",flexDirection:"row",position:"sticky",top:0,zIndex:30,background:"#f8f9fa"}),cornerStyle(){const e=this.rowGroups.length?Q:0;return{width:Y+e+"px",minWidth:Y+e+"px",height:this._headerRowH*this.headerDepth+"px",background:"#f0f0f0",borderRight:"1px solid #ccc",borderBottom:"1px solid #ccc",boxSizing:"border-box",flexShrink:0}},rowOutlineGutterStyle:()=>({width:"20px",flexShrink:0}),frozenColHeaderStyle(){const e=this.getFrozenWidth();return{width:e+"px",minWidth:e+"px",overflow:"hidden",flexShrink:0}},scrollColHeaderStyle(){return null!=this.headerViewportWidth?{flex:"0 0 "+this.headerViewportWidth+"px",overflow:"hidden"}:{flex:1,overflow:"hidden"}},bodyAreaStyle:()=>({display:"flex",flexDirection:"row",flex:1,position:"relative"}),rowHeaderColStyle:()=>({width:"50px",minWidth:"50px",flexShrink:0,display:"flex",flexDirection:"column",position:"sticky",left:0,zIndex:20,background:"#f0f0f0"}),rowOutlineColStyle:()=>({width:"20px",minWidth:"20px",flexShrink:0,position:"relative",background:"#fafafa"}),scrollBodyStyle:()=>({flex:1,overflow:"auto",position:"relative"}),frozenColBodyStyle(){return{position:"sticky",left:0,width:this.getFrozenWidth()+"px",zIndex:10,background:"white",overflow:"hidden"}},mainCellsStyle(){return{position:"absolute",left:this.getFrozenWidth()+"px",top:0,bottom:0}},scrollContentWidth(){let e=0;for(let n=0;n<this.leafCols.length;n++)this.isColHidden(n)||this.pinnedRightSet.has(n)||(e+=this.getColW(n));return this.hasPinnedRight&&(e+=this.pinnedRightWidth),e},scrollableContentWidth(){let e=0;for(let n=0;n<this.leafCols.length;n++)!this.isColHidden(n)&&this.isColumnScrollable(n)&&(e+=this.getColW(n));return e},pinnedRightBodyStyle(){return{position:"sticky",right:0,width:this.pinnedRightWidth+"px",zIndex:10,background:"white",flexShrink:0}},pinnedRightHeaderStyle(){return{width:this.pinnedRightWidth+"px",minWidth:this.pinnedRightWidth+"px",overflow:"hidden",flexShrink:0,borderLeft:"1px solid #ccc"}},scrollRowHeadersStyle:()=>({flex:1,overflow:"hidden",position:"relative"}),footerAreaStyle:()=>({display:"flex",flexDirection:"row",flexShrink:0,position:"sticky",bottom:0,zIndex:30,background:"#f8f9fa",boxShadow:"0 -2px 0 0 #d0d0d0"}),footerLabelStyle(){const e=this.rowGroups.length?Q:0;return{width:Y+e+"px",minWidth:Y+e+"px",height:this._footerRowH+"px",display:"flex",alignItems:"center",justifyContent:"center",background:"#f0f0f0",borderRight:"1px solid #ccc",borderTop:"1px solid #ccc",boxSizing:"border-box",flexShrink:0,fontSize:"12px",fontWeight:600}},frozenColFooterStyle(){const e=this.getFrozenWidth();return{width:e+"px",minWidth:e+"px",height:this._footerRowH+"px",overflow:"hidden",flexShrink:0}},pinnedRightFooterStyle(){return{width:this.pinnedRightWidth+"px",minWidth:this.pinnedRightWidth+"px",height:this._footerRowH+"px",overflow:"hidden",flexShrink:0,borderLeft:"1px solid #ccc"}},colOutlineRowStyle:()=>({position:"absolute",top:"-20px",left:0,right:0,height:"20px"}),resizeLineStyle(){return this.resizing.active?"col"===this.resizing.type?{position:"fixed",top:0,left:this.resizing.currentPos+"px",width:"2px",height:"100%",background:"#1a73e8",pointerEvents:"none",zIndex:9999}:{position:"fixed",left:0,top:this.resizing.currentPos+"px",height:"2px",width:"100%",background:"#1a73e8",pointerEvents:"none",zIndex:9999}:{}}},watch:{size(){this.rowHeights={},this.$nextTick(()=>this.updateVirtualScroll())},autoWidth(e){e&&this._applyAutoWidth()},columns:{immediate:!0,handler(e){this.leafCols=r(e),this.headerDepth=a(e),this.headerRows=c(e,this.headerDepth),this.leafCols.forEach(e=>{void 0===this.colWidths[e._flatIndex]&&(this.colWidths[e._flatIndex]=e.width||100)}),this.computeColumnTotals(),this.updateVirtualScroll(),this._applyAutoWidth()}},merges:{immediate:!0,handler(e){this.internalMerges=e?[...e]:[]}},collapsedGroups:{immediate:!0,handler(e){this.internalCollapsedGroups=e?[...e]:[]}},data:{immediate:!0,handler(e){this._dataRaw=e||[],this.updateVirtualScroll(),this.computeColumnTotals(),this.$forceUpdate(),this._applyAutoWidth()}},totalRows(){this.updateVirtualScroll()},hiddenRowSet(){this.updateVirtualScroll()},totalVirtualHeight(){this.$nextTick(this.syncHeaderViewportWidth)}},created(){this._dataRaw=this.data||[],this._cellStyleCache=Object.create(null),this._rowStyleCache=Object.create(null),this._rowHeaderStyleCache=Object.create(null),this._scrollRAF=null,this._resizeRAF=null,this._history=[],this._historyIndex=-1,this._historySkip=!1},mounted(){this.updateVirtualScroll(),this.$nextTick(this.syncHeaderViewportWidth),document.addEventListener("mousemove",this.onGlobalMouseMove),document.addEventListener("mouseup",this.onGlobalMouseUp),document.addEventListener("keydown",this.onGlobalKeyDown),document.addEventListener("mousedown",this._onDocMouseDownForDropdown,!0),window.addEventListener("resize",this.syncHeaderViewportWidth),window.addEventListener("scroll",this._onWindowScrollForDropdown,!0)},beforeDestroy(){document.removeEventListener("mousemove",this.onGlobalMouseMove),document.removeEventListener("mouseup",this.onGlobalMouseUp),document.removeEventListener("keydown",this.onGlobalKeyDown),document.removeEventListener("mousedown",this._onDocMouseDownForDropdown,!0),window.removeEventListener("resize",this.syncHeaderViewportWidth),window.removeEventListener("scroll",this._onWindowScrollForDropdown,!0),clearTimeout(this._scrollEndTimer)},methods:{updateVirtualScroll(){const e=this.hasFooter?this._footerRowH:0,n=this.height-this._headerRowH*this.headerDepth-e,t=function(e,n,t,i,o=5){if(0===t)return{startIndex:0,endIndex:-1,offsetTop:0,totalHeight:0};const l=i[t],s=e+n;let r=0,a=t-1;for(;r<a;){const n=r+a>>1;i[n+1]<=e?r=n+1:a=n}let c=r,d=c;for(;d<t-1&&i[d+1]<s;)d++;return c=Math.max(0,c-o),d=Math.min(t-1,d+o),{startIndex:c,endIndex:d,offsetTop:i[c],totalHeight:l}}(this.scrollTop,Math.max(n,100),this.totalRows,this.cumulativeHeights);this.virtualStartIndex=t.startIndex,this.virtualEndIndex=t.endIndex,this.virtualOffsetTop=t.offsetTop,this.totalVirtualHeight=t.totalHeight},onBodyScroll(e){const n=e.target;this.$refs.scrollColHeader&&(this.$refs.scrollColHeader.scrollLeft=n.scrollLeft),this.$refs.scrollFooter&&(this.$refs.scrollFooter.scrollLeft=n.scrollLeft),this.$refs.scrollRowHeaders&&(this.$refs.scrollRowHeaders.scrollTop=n.scrollTop),this.selectDropdown&&this.closeSelectDropdown(),this.transitionEnabled&&(this._isScrolling=!0,clearTimeout(this._scrollEndTimer),this._scrollEndTimer=setTimeout(()=>{this._isScrolling=!1},150)),this._scrollRAF||(this._scrollRAF=requestAnimationFrame(()=>{this._scrollRAF=null,this.scrollTop=n.scrollTop,this.scrollLeft=n.scrollLeft,this.updateVirtualScroll()}))},onScroll(e){},syncHeaderViewportWidth(){const e=this.$refs.scrollBody;e&&this.headerViewportWidth!==e.clientWidth&&(this.headerViewportWidth=e.clientWidth)},isRowHidden(e){return this.hiddenRowSet.has(e)},isColHidden(e){return this.effectiveHiddenColSet.has(e)},getColW(e){return void 0!==this.colWidths[e]?this.colWidths[e]:this.leafCols[e]&&this.leafCols[e].width||100},getRowH(e){return this.isRowHidden(e)?0:this.rowHeights[e]||this._rowH},getColLeft(e){let n=0;for(let t=0;t<e&&t<this.leafCols.length;t++)this.isColHidden(t)||(n+=this.getColW(t));return n},getFrozenWidth(){return this.hasPinnedLeft?this.pinnedLeftWidth:0},isColumnPinnedLeft(e){return this.pinnedLeftSet.has(e)},isColumnPinnedRight(e){return this.pinnedRightSet.has(e)},isColumnScrollable(e){return!this.isColumnPinnedLeft(e)&&!this.isColumnPinnedRight(e)},getZoneColLeft(e,n){let t=0;if("left"===n)for(const n of this.pinnedLeftCols){if(n._flatIndex===e)break;this.isColHidden(n._flatIndex)||(t+=this.getColW(n._flatIndex))}else if("right"===n)for(const n of this.pinnedRightCols){if(n._flatIndex===e)break;this.isColHidden(n._flatIndex)||(t+=this.getColW(n._flatIndex))}else for(let n=0;n<this.leafCols.length;n++){const i=this.leafCols[n];if(i._flatIndex===e)break;!this.isColHidden(i._flatIndex)&&this.isColumnScrollable(i._flatIndex)&&(t+=this.getColW(i._flatIndex))}return t},isCellInZone(e,n,t){if(e.isGroup){const n=e._firstLeafFlatIndex,i=n+e.colspan-1;for(let e=n;e<=i;e++){if(this.isColHidden(e))continue;if((this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll")===t)return!0}return!1}const i=e.column._flatIndex;if(this.isColHidden(i))return!1;return(this.isColumnPinnedLeft(i)?"left":this.isColumnPinnedRight(i)?"right":"scroll")===t},getHeaderCellStyleForZone(e,n,t){let i=e._firstLeafFlatIndex;if(e.isGroup){const n=i+e.colspan-1;for(let e=i;e<=n;e++){if(this.isColHidden(e))continue;if((this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll")===t){i=e;break}}}const o=this.getZoneColLeft(i,t);let l=0;if(e.isGroup){const n=e._firstLeafFlatIndex,i=n+e.colspan-1;for(let e=n;e<=i;e++){if(this.isColHidden(e))continue;(this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll")===t&&(l+=this.getColW(e))}}else l=this.getColW(e.column._flatIndex);return{position:"absolute",top:this._headerRowH*n+"px",left:o+"px",width:l+"px",height:this._headerRowH*e.rowspan+"px",display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",borderRight:"1px solid #ccc",borderBottom:"1px solid #ccc",background:"#f0f0f0",fontSize:"12px",fontWeight:600,overflow:"hidden",whiteSpace:"nowrap",userSelect:"none",cursor:"pointer"}},getRangeWidth(e,n){let t=0;for(let i=e;i<=n&&i<this.leafCols.length;i++)this.isColHidden(i)||(t+=this.getColW(i));return t},getRangeHeight(e,n){let t=0;for(let i=e;i<=n&&i<this.totalRows;i++)this.isRowHidden(i)||(t+=this.getRowH(i));return t},getCellValue(e,n){const t=this.leafCols[n];if(!t)return"";const i=this._dataRaw[e];if(!i)return"";const o=i[t.key];return void 0!==o?o:""},isEditing(e,n){return this.editingCell&&this.editingCell.row===e&&this.editingCell.col===n},isMergedAway(e,n){const t=y(e,n,this.internalMerges);if(!t)return!1;const i=o(t);return!(i.startRow===e&&i.startCol===n)},getMergeSpan(e,n){const t=y(e,n,this.internalMerges);if(!t)return{rowspan:1,colspan:1};const i=o(t);return{rowspan:i.endRow-i.startRow+1,colspan:i.endCol-i.startCol+1}},isSelected(e,n){return t=e,i=n,this.selectionRanges.some(e=>l(t,i,e));var t,i},isActiveCell(e,n){return this.activeCell.row===e&&this.activeCell.col===n},isCopyHighlighted(e,n){return!(!this.clipboardRange||"copy"!==this.clipboardMode)&&l(e,n,this.clipboardRange)},getRowStyle(e){const n=this.getRowH(e),t=n;if(this._rowStyleCache[t])return this._rowStyleCache[t];const i={height:n+"px",minHeight:n+"px"};return this._rowStyleCache[t]=i,i},getRowHeaderStyle(e){const n=this.getRowH(e),t=n;if(this._rowHeaderStyleCache[t])return this._rowHeaderStyleCache[t];const i={height:n+"px"};return this._rowHeaderStyleCache[t]=i,i},getCellStyle(e,n){const t=y(e,n,this.internalMerges),i=t?o(t):null;let l=this.getColW(n),s=this.getRowH(e);i&&i.startRow===e&&i.startCol===n&&(l=this.getRangeWidth(i.startCol,i.endCol),s=this.getRangeHeight(i.startRow,i.endRow));const r=this.isColumnPinnedLeft(n)?"left":this.isColumnPinnedRight(n)?"right":"scroll",a=this.getZoneColLeft(n,r),c=l+"_"+s+"_"+a;if(this._cellStyleCache[c])return this._cellStyleCache[c];const d={position:"absolute",left:a+"px",top:"0px",width:l+"px",minWidth:l+"px",height:s+"px"};return this._cellStyleCache[c]=d,d},getCellClasses(e,n){const t=this.leafCols[n];return{"eg-cell-active":this.isActiveCell(e,n),"eg-cell-selected":this.isSelected(e,n)&&!this.isActiveCell(e,n),"eg-cell-fill-preview":this.isInFillPreview(e,n),"eg-cell-copy":this.isCopyHighlighted(e,n),"eg-cell-merged-hidden":this.isMergedAway(e,n),"eg-cell-select":!(!t||!this.isSelectLikeCol(t)),"eg-cell-checkbox":!(!t||"checkbox"!==t.type),"eg-cell-readonly":!this.isCellEditable(e,n),"eg-cell-invalid":!(!this.invalidCell||this.invalidCell.row!==e||this.invalidCell.col!==n)}},isInFillPreview(e,n){if(!this.fillDrag.active||!this.fillDrag.sourceRange)return!1;const t=this.fillDrag.sourceRange,i=this.fillDrag.currentRow,o=this.fillDrag.currentCol;return i>t.endRow?e>t.endRow&&e<=i&&n>=t.startCol&&n<=t.endCol:o>t.endCol&&(n>t.endCol&&n<=o&&e>=t.startRow&&e<=t.endRow)},getHeaderCellStyle(e,n,t){const i=t?"left":"scroll";let o=e._firstLeafFlatIndex;if(e.isGroup){const n=o+e.colspan-1;for(let e=o;e<=n;e++)if(!this.isColHidden(e)&&(t?this.isColumnPinnedLeft(e):this.isColumnScrollable(e))){o=e;break}}const l=this.getZoneColLeft(o,i);let s=0;if(e.isGroup){const n=e._firstLeafFlatIndex,i=n+e.colspan-1;for(let e=n;e<=i;e++){if(this.isColHidden(e))continue;(t?this.isColumnPinnedLeft(e):this.isColumnScrollable(e))&&(s+=this.getColW(e))}}else s=this.getColW(e.column._flatIndex);return{position:"absolute",top:this._headerRowH*n+"px",left:l+"px",width:s+"px",height:this._headerRowH*e.rowspan+"px",display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",borderRight:"1px solid #ccc",borderBottom:"1px solid #ccc",background:"#f0f0f0",fontSize:"12px",fontWeight:600,overflow:"hidden",whiteSpace:"nowrap",userSelect:"none",cursor:"pointer"}},computeColumnTotals(){const e={},n=this._dataRaw||[];this.leafCols.forEach(t=>{if(!t.showTotal)return;let i=0,o=!1;for(let e=0;e<n.length;e++){const l=n[e],s=l?l[t.key]:void 0;"number"==typeof s&&isFinite(s)&&(i+=s,o=!0)}e[t._flatIndex]=o?Math.round(1e6*i)/1e6:null}),this.columnTotals=e},getColumnTotalDisplay(e){const n=this.leafCols[e];if(!n||!n.showTotal)return"";const t=this.columnTotals[e];return null==t?"-":t},getFooterCellStyle(e){const n=this.isColumnPinnedLeft(e)?"left":this.isColumnPinnedRight(e)?"right":"scroll",t=this.getZoneColLeft(e,n),i=this.getColW(e);return{position:"absolute",left:t+"px",top:"0px",width:i+"px",minWidth:i+"px",height:this._footerRowH+"px"}},isCellVisible(e,n,t){if(e.isGroup){const n=e._firstLeafFlatIndex,i=n+e.colspan-1;for(let e=n;e<=i;e++){if(this.isColHidden(e))continue;if(t?this.isColumnPinnedLeft(e):this.isColumnScrollable(e))return!0}return!1}const i=e.column._flatIndex;return!this.isColHidden(i)&&(t?this.isColumnPinnedLeft(i):this.isColumnScrollable(i))},getRowGroupBtnStyle(e){return{position:"absolute",top:h(e.startIndex,this._rowH,this.rowHeights)-this.scrollTop+"px",left:"2px",width:"16px",height:"16px",lineHeight:"16px",textAlign:"center",fontSize:"10px",cursor:"pointer",background:"#e8eaf6",border:"1px solid #9fa8da",borderRadius:"2px",zIndex:5}},getColGroupBtnStyle(e){return{position:"absolute",left:this.getColLeft(e.startIndex)-this.scrollLeft+"px",top:"2px",width:"16px",height:"16px",lineHeight:"16px",textAlign:"center",fontSize:"10px",cursor:"pointer",background:"#e8eaf6",border:"1px solid #9fa8da",borderRadius:"2px"}},selectCell(e,n,t=!1){e<0||e>=this.totalRows||n<0||n>=this.leafCols.length||(this.activeCell={row:e,col:n},t?this.selectionRanges.push(s(e,n)):this.selectionRanges=[s(e,n)],this.emitSelectionChange())},selectRange(e,n,t,i,o=!0){const l={startRow:e,startCol:n,endRow:t,endCol:i};o?this.selectionRanges=[l]:this.selectionRanges.push(l),this.emitSelectionChange()},selectRow(e){this.activeCell={row:e,col:0},this.selectionRanges=[{startRow:e,endRow:e,startCol:0,endCol:this.leafCols.length-1}],this.emitSelectionChange()},selectColumn(e){this.activeCell={row:0,col:e},this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:e,endCol:e}],this.emitSelectionChange()},selectAll(){this.activeCell={row:0,col:0},this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:0,endCol:this.leafCols.length-1}],this.emitSelectionChange()},emitSelectionChange(){this.$emit("selection-change",{activeCell:{...this.activeCell},ranges:this.selectionRanges.map(e=>o(e))})},onCellMouseDown(e,n,t){if(this.invalidCell=null,this.editingCell){const e=this.leafCols[this.editingCell.col];e&&this._hasCustomEdit(e)?this.cancelEdit():this.commitEdit()}if(this.hideContextMenu(),this.$refs.wrapper&&this.$refs.wrapper.focus(),e.ctrlKey||e.metaKey)this.selectDropdown&&this.closeSelectDropdown(),this.activeCell={row:n,col:t},this.mouseDownRange={startRow:n,startCol:t,endRow:n,endCol:t},this.selectionRanges.push(this.mouseDownRange),this.isCtrlDrag=!0;else if(e.shiftKey&&this.selectionRanges.length>0)this.selectDropdown&&this.closeSelectDropdown(),this.selectionRanges=[(i=this.activeCell,o={row:n,col:t},{startRow:i.row,startCol:i.col,endRow:o.row,endCol:o.col})];else{const i=this.activeCell.row===n&&this.activeCell.col===t;this.activeCell={row:n,col:t},this.mouseDownRange={startRow:n,startCol:t,endRow:n,endCol:t},this.selectionRanges=[this.mouseDownRange],this.isCtrlDrag=!1;const o=this.leafCols[t];if(i&&o&&this.isSelectLikeCol(o)&&!this._hasCustomEdit(o)&&this.isCellEditable(n,t))return this.openSelectDropdown(n,t,e),e.preventDefault(),this.isMouseDown=!0,void this.emitSelectionChange();this.selectDropdown&&this.closeSelectDropdown()}var i,o;this.isMouseDown=!0,this.emitSelectionChange(),e.preventDefault()},onCellMouseOver(e,n,t){if(this.isMouseDown&&!this.fillDrag.active)return this.dragMove.active?(this.dragMove.targetRow=n,void(this.dragMove.targetCol=t)):void(this.mouseDownRange&&(this.mouseDownRange.endRow=n,this.mouseDownRange.endCol=t,this.emitSelectionChange()))},onCellMouseUp(){this.isMouseDown=!1},onCellDblClick(e,n){const t=this.leafCols[n];this.isCellEditable(e,n)&&(t&&this.isSelectLikeCol(t)&&!this._hasCustomEdit(t)?this.openSelectDropdownForActive():t&&"checkbox"===t.type||this.startEditing(e,n,null,!0))},onRowHeaderMouseDown(e,n){const t=this.leafCols.length-1,i={startRow:n,endRow:n,startCol:0,endCol:t};if(e.ctrlKey||e.metaKey)if(e.shiftKey&&this.selectionRanges.length>0){const e=Math.min(this.activeCell.row,n),i=Math.max(this.activeCell.row,n);this.selectionRanges=[...this.selectionRanges,{startRow:e,endRow:i,startCol:0,endCol:t}]}else{const e=this.selectionRanges.findIndex(e=>e.startRow===n&&e.endRow===n&&0===e.startCol&&e.endCol===t);if(e>=0){const n=this.selectionRanges.filter((n,t)=>t!==e);this.selectionRanges=n.length?n:[i]}else this.activeCell={row:n,col:0},this.selectionRanges=[...this.selectionRanges,i]}else if(e.shiftKey&&this.selectionRanges.length>0){const e=Math.min(this.activeCell.row,n),i=Math.max(this.activeCell.row,n);this.selectionRanges=[{startRow:e,endRow:i,startCol:0,endCol:t}]}else this.activeCell={row:n,col:0},this.selectionRanges=[i];this.mouseDownRange=this.selectionRanges[this.selectionRanges.length-1],this.isMouseDown=!0,this.emitSelectionChange(),e.preventDefault()},onColHeaderMouseDown(e,n){if(n.isGroup)return;const t=n.column._flatIndex;if(e.shiftKey&&this.selectionRanges.length>0){const e=Math.min(this.activeCell.col,t),n=Math.max(this.activeCell.col,t);this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:e,endCol:n}]}else this.activeCell={row:0,col:t},this.selectionRanges=[{startRow:0,endRow:this.totalRows-1,startCol:t,endCol:t}];this.isMouseDown=!0,this.mouseDownRange=this.selectionRanges[0],this.emitSelectionChange(),e.preventDefault()},onColHeaderDblClick(e,n){n.isGroup||this.autoFitColumn(n.column._flatIndex)},onColHeaderContextMenu(e,n){n.isGroup||(this.contextMenu={visible:!0,x:e.clientX,y:e.clientY,row:-1,col:n.column._flatIndex})},onRowContextMenu(e,n){this.contextMenu={visible:!0,x:e.clientX,y:e.clientY,row:n,col:-1}},onCellContextMenu(e,n,t){this.isSelected(n,t)||this.selectCell(n,t),this.contextMenu={visible:!0,x:e.clientX,y:e.clientY,row:n,col:t}},hideContextMenu(){this.contextMenu.visible=!1},onKeyDown(e){if(this.selectDropdown){const n=this.$refs.selectDropdownComp;return void(n&&n.handleKey&&n.handleKey(e))}if(this.editingCell)return;const n=e.key;if((e.ctrlKey||e.metaKey)&&"z"===n&&!e.shiftKey)return e.preventDefault(),void this.undo();if((e.ctrlKey||e.metaKey)&&("y"===n||"z"===n&&e.shiftKey))return e.preventDefault(),void this.redo();if("a"===n&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.selectAll();if("c"===n&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.copySelection();if("x"===n&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.cutSelection();if("v"===n&&(e.ctrlKey||e.metaKey))return e.preventDefault(),void this.pasteAt(this.activeCell.row,this.activeCell.col);if("Delete"===n||"Backspace"===n)return e.preventDefault(),void this.clearSelection();if("F2"===n){e.preventDefault();const n=this.leafCols[this.activeCell.col];return void(n&&this.isSelectLikeCol(n)&&!this._hasCustomEdit(n)?this.openSelectDropdownForActive():n&&"checkbox"===n.type?this.toggleCheckbox(this.activeCell.row,this.activeCell.col):this.startEditing(this.activeCell.row,this.activeCell.col,null,!0))}if(" "===n){const n=this.leafCols[this.activeCell.col];if(n&&"checkbox"===n.type)return e.preventDefault(),void this.toggleCheckbox(this.activeCell.row,this.activeCell.col)}if("Escape"===n)return this.clipboardRange=null,this.clipboardMatrix=null,void(this.clipboardMode=null);if("Tab"===n)return e.preventDefault(),void this.moveActive(0,e.shiftKey?-1:1,e);if("Enter"===n)return e.preventDefault(),void this.moveActive(e.shiftKey?-1:1,0,e);const t={ArrowUp:[-1,0],ArrowDown:[1,0],ArrowLeft:[0,-1],ArrowRight:[0,1]};if(t[n]){e.preventDefault();const[i,l]=t[n];if(e.shiftKey){const e=this.primaryRange||s(this.activeCell.row,this.activeCell.col),n=o(e);-1===i&&(e.startRow=Math.max(0,n.startRow-1)),1===i&&(e.endRow=Math.min(this.totalRows-1,n.endRow+1)),-1===l&&(e.startCol=Math.max(0,n.startCol-1)),1===l&&(e.endCol=Math.min(this.leafCols.length-1,n.endCol+1)),this.selectionRanges=[e],this.emitSelectionChange()}else this.moveActive(i,l,e);return}if("Home"===n)return e.preventDefault(),void(e.ctrlKey?this.selectCell(0,0):this.selectCell(this.activeCell.row,0));if("End"===n)return e.preventDefault(),void(e.ctrlKey?this.selectCell(this.totalRows-1,this.leafCols.length-1):this.selectCell(this.activeCell.row,this.leafCols.length-1));if("PageUp"===n){e.preventDefault();const n=Math.floor((this.height-this._headerRowH*this.headerDepth)/this._rowH);return void this.moveActive(-n,0,e)}if("PageDown"===n){e.preventDefault();const n=Math.floor((this.height-this._headerRowH*this.headerDepth)/this._rowH);return void this.moveActive(n,0,e)}},onKeyPress(e){if(!this.editingCell&&!this.selectDropdown&&!(e.ctrlKey||e.metaKey||e.altKey)&&e.key&&1===e.key.length){const n=this.leafCols[this.activeCell.col];if(n&&this.isSelectLikeCol(n)&&!this._hasCustomEdit(n))return e.preventDefault(),void this.openSelectDropdownForActive();if(n&&"checkbox"===n.type)return void e.preventDefault();this.startEditing(this.activeCell.row,this.activeCell.col,e.key,!1)}},onGlobalKeyDown(e){},moveActive(e,n,t){let i=this.activeCell.row+e,l=this.activeCell.col+n;for(t&&"Tab"===t.key&&(l>=this.leafCols.length&&(l=0,i++),l<0&&(l=this.leafCols.length-1,i--)),i=Math.max(0,Math.min(this.totalRows-1,i)),l=Math.max(0,Math.min(this.leafCols.length-1,l));this.isColHidden(l)&&l>0&&l<this.leafCols.length-1;)l+=n>=0?1:-1;for(;this.isRowHidden(i)&&i>0&&i<this.totalRows-1;)i+=e>=0?1:-1;const r=function(e,n,t){const i=y(e,n,t);if(!i)return{row:e,col:n};const l=o(i);return{row:l.startRow,col:l.startCol}}(i,l,this.internalMerges);i=r.row,l=r.col,this.activeCell={row:i,col:l},this.selectionRanges=[s(i,l)],this.emitSelectionChange(),this.scrollToCell(i,l)},scrollToCell(e,n){const t=this.$refs.scrollBody;if(!t)return;const i=h(e,this._rowH,this.rowHeights),o=i+this.getRowH(e),l=this.getColLeft(n)-this.getFrozenWidth(),s=l+this.getColW(n);i<t.scrollTop&&(t.scrollTop=i),o>t.scrollTop+t.clientHeight&&(t.scrollTop=o-t.clientHeight),l<t.scrollLeft&&(t.scrollLeft=l),s>t.scrollLeft+t.clientWidth&&(t.scrollLeft=s-t.clientWidth)},startEditing(e,n,t=null,i=!1){const o=this.leafCols[n];if(o&&this.isCellEditable(e,n)&&!(e<0||e>=this.totalRows)){if(!this._hasCustomEdit(o)){if(this.isSelectLikeCol(o))return void this.openSelectDropdownForActive();if("checkbox"===o.type)return}this.invalidCell=null,this.editingCell={row:e,col:n},this.editValue=null!==t?t:i?String(this.getCellValue(e,n)||""):"",this.editOriginalValue=String(this.getCellValue(e,n)||""),this.$nextTick(()=>{const e=this.$refs.editInput;if(e){const n=Array.isArray(e)?e[0]:e;n&&(n.focus(),i&&n.setSelectionRange(n.value.length,n.value.length))}})}},commitEdit(){if(!this.editingCell)return;const{row:e,col:n}=this.editingCell,t=this.leafCols[n];if(!t)return void(this.editingCell=null);if(!this.isCellValueValid(e,n,this.editValue))return this._showInvalidCell(e,n,this.editValue),this.editingCell=null,void(this.$refs.wrapper&&this.$refs.wrapper.focus());this.invalidCell=null;const i=this.data,o=i.slice();o[e]={...i[e],[t.key]:this.editValue},this._emitDataChange(i,o),this.editingCell=null,this.$refs.wrapper&&this.$refs.wrapper.focus()},cancelEdit(){this.editingCell=null,this.$refs.wrapper&&this.$refs.wrapper.focus()},hasCellSlot(e){return!!(e&&e.key&&this.$scopedSlots&&this.$scopedSlots["cell-"+e.key])},hasLegacyEditSlot(e){return!!(e&&e.key&&this.$scopedSlots&&this.$scopedSlots["edit-"+e.key])},_hasCustomEdit(e){return"select"===e.type||"treeSelect"===e.type?this.hasLegacyEditSlot(e):this.hasCellSlot(e)||this.hasLegacyEditSlot(e)},commitEditSlot(e){if(!this.editingCell)return;const{row:n,col:t}=this.editingCell,i=this.leafCols[t];if(!i)return void(this.editingCell=null);if(!this.isCellValueValid(n,t,e))return this._showInvalidCell(n,t,e),this.editingCell=null,void(this.$refs.wrapper&&this.$refs.wrapper.focus());this.invalidCell=null;const o=this.data,l=o.slice();l[n]={...o[n],[i.key]:e},this._emitDataChange(o,l),this.editingCell=null,this.$refs.wrapper&&this.$refs.wrapper.focus()},isCheckboxChecked(e,n){const t=this.getCellValue(e,n);return!0===t||1===t||"string"==typeof t&&["true","1"].includes(t.trim().toLowerCase())},setCheckboxValue(e,n,t){const i=this.leafCols[n];if(!i||!this.isCellEditable(e,n))return;if(!this.isCellValueValid(e,n,t))return void this._showInvalidCell(e,n,t);this.invalidCell=null;const o=this.data,l=o.slice();l[e]={...o[e],[i.key]:t},this._emitDataChange(o,l)},toggleCheckbox(e,n){this.setCheckboxValue(e,n,!this.isCheckboxChecked(e,n))},isCellEditable(e,n){const t=this.leafCols[n];if(!t)return!1;const i=this._dataRaw[e]||{};return"function"==typeof this.cellEditable?!!this.cellEditable(i,e,t,n):"function"==typeof t.editable?!!t.editable(i,e):!1!==t.editable},isCellValueValid(e,n,t){if("function"!=typeof this.cellValidator)return!0;const i=this.leafCols[n];if(!i)return!0;const o=this._dataRaw[e]||{};return!!this.cellValidator(t,o,e,i,n)},_resolveValidationMessage(e,n,t){const i=this.leafCols[n],o=this._dataRaw[e]||{};return"function"==typeof this.cellValidationMessage?this.cellValidationMessage(t,o,e,i,n):"string"==typeof this.cellValidationMessage&&this.cellValidationMessage?this.cellValidationMessage:this.t.invalidValue},_invalidCellRect(){const e=this.$refs.editInput,n=Array.isArray(e)?e[0]:e;if(n)return n.getBoundingClientRect();const t=this.$el?this.$el.querySelector(".eg-cell-active"):null;return t?t.getBoundingClientRect():null},_showInvalidCell(e,n,t){const i=this._resolveValidationMessage(e,n,t);this.invalidCell={row:e,col:n,message:i,rect:this._invalidCellRect()},this.$emit("cell-validation-error",{row:e,col:n,value:t,message:i})},_emitBulkValidationError(e,n){const t=e.map(e=>({row:e.row,col:e.col,value:e.value,message:this._resolveValidationMessage(e.row,e.col,e.value)}));this.$emit("bulk-validation-error",{blocked:n,cells:t})},isSelectLikeCol:e=>!(!e||"select"!==e.type&&"treeSelect"!==e.type),treeOptionKeys:e=>({valueKey:e.optionValueKey||"value",labelKey:e.optionLabelKey||"label",childrenKey:e.optionChildrenKey||"children"}),_treeFindLabel(e,n,t){for(const i of e||[]){if(String(i[n.valueKey])===t)return null!=i[n.labelKey]?i[n.labelKey]:String(i[n.valueKey]);const e=this._treeFindLabel(i[n.childrenKey],n,t);if(null!=e)return e}return null},colOptions(e){const n=this.leafCols[e];return n&&Array.isArray(n.options)?n.options.map(e=>"string"==typeof e?{value:e,label:e}:{value:e.value,label:null!=e.label?e.label:String(e.value),color:e.color}):[]},getSelectLabel(e,n){const t=this.leafCols[e],i=null==n?"":String(n);if(t&&"treeSelect"===t.type){if(!Array.isArray(t.options))return i;const e=this._treeFindLabel(t.options,this.treeOptionKeys(t),i);return null!=e?e:i}const o=this.colOptions(e);if(!o.length)return i;const l=o.find(e=>String(e.value)===i);return l?l.label:i},_buildSelectDropdownState(e,n,t,i,o){const l={top:o.top,bottom:o.bottom,left:o.left,width:o.width};return{row:e,col:n,tree:"treeSelect"===t.type,keys:"treeSelect"===t.type?this.treeOptionKeys(t):null,rawOptions:t.options||[],currentValue:i,rect:l,enableSearch:!!t.enableSearch}},openSelectDropdown(e,n,t){const i=this.leafCols[n];if(!i||!Array.isArray(i.options))return;const o=t.currentTarget.getBoundingClientRect(),l=String(null!=this.getCellValue(e,n)?this.getCellValue(e,n):"");this.selectDropdown=this._buildSelectDropdownState(e,n,i,l,o)},openSelectDropdownForActive(){const{row:e,col:n}=this.activeCell,t=this.leafCols[n];t&&Array.isArray(t.options)&&this.$nextTick(()=>{const i=this.$el?this.$el.querySelector(".eg-cell-active"):null;if(!i)return;const o=i.getBoundingClientRect(),l=String(null!=this.getCellValue(e,n)?this.getCellValue(e,n):"");this.selectDropdown=this._buildSelectDropdownState(e,n,t,l,o)})},onComponentSelect({value:e,label:n}){if(!this.selectDropdown)return;const{row:t,col:i}=this.selectDropdown,o=this.leafCols[i];if(o){if(!this.isCellValueValid(t,i,e))return this._showInvalidCell(t,i,e),this.closeSelectDropdown(),void(this.$refs.wrapper&&this.$refs.wrapper.focus());this.invalidCell=null;const l=this.data,s=l.slice();s[t]={...l[t],[o.key]:e},this._emitDataChange(l,s),"function"==typeof o.onSelect&&o.onSelect({value:e,label:n},s[t],t,o)}this.closeSelectDropdown(),this.$refs.wrapper&&this.$refs.wrapper.focus()},closeSelectDropdown(){this.selectDropdown=null},_onWindowScrollForDropdown(e){if(!this.selectDropdown)return;const n=this.$refs.selectDropdownComp,t=n&&n.$el?n.$el:null;t&&t.contains(e.target)||this.closeSelectDropdown()},_onDocMouseDownForDropdown(e){if(!this.selectDropdown)return;const n=this.$refs.selectDropdownComp,t=n&&n.$el?n.$el:null;t&&t.contains(e.target)||this.closeSelectDropdown()},onEditKeyDown(e){"Escape"===e.key?(e.preventDefault(),this.cancelEdit()):"Enter"!==e.key||e.shiftKey?"Tab"===e.key&&(e.preventDefault(),this.commitEdit(),this.moveActive(0,e.shiftKey?-1:1,e)):(e.preventDefault(),this.commitEdit(),this.moveActive(1,0,e))},clearSelection(){if(!this.selectionRanges.length)return;const e=this.data,n=e.slice();let t=!1;const i=[];for(const e of this.selectionRanges){const o={startRow:Math.min(e.startRow,e.endRow),endRow:Math.max(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endCol:Math.max(e.startCol,e.endCol)};for(let e=o.startRow;e<=o.endRow&&e<n.length;e++){let l=null;for(let s=o.startCol;s<=o.endCol;s++){const o=this.leafCols[s];if(o&&this.isCellEditable(e,s)){if(!this.isCellValueValid(e,s,"")){i.push({row:e,col:s,value:""});continue}l||(l={...n[e]}),l[o.key]="",t=!0}}l&&(n[e]=l)}}this.strictBulkValidation&&i.length>0?this._emitBulkValidationError(i,!0):(t&&this._emitDataChange(e,n),i.length>0&&this._emitBulkValidationError(i,!1))},startFillDrag(e){const n=this.primaryRange;n&&(this.fillDrag.active=!0,this.fillDrag.sourceRange=o(n),this.fillDrag.currentRow=n.endRow,this.fillDrag.currentCol=n.endCol,e.preventDefault())},onGlobalMouseMove(e){this.resizing.active?this.handleResizeMove(e):this.fillDrag.active?this.handleFillDragMove(e):this.dragMove.active&&this.handleDragMoveMove(e)},onGlobalMouseUp(e){this.resizing.active?this.commitResize():this.fillDrag.active?this.commitFillDrag():this.dragMove.active?this.commitDragMove(e):this.isMouseDown=!1},handleFillDragMove(e){const n=this.$refs.scrollBody;if(!n)return;const t=n.getBoundingClientRect(),i=e.clientY-t.top+n.scrollTop,o=e.clientX-t.left+n.scrollLeft,l=this.cumulativeHeights,s=this.totalRows;let r=0,a=s-1;for(;r<a;){const e=r+a>>1;l[e+1]<=i?r=e+1:a=e}const c=Math.min(r,s-1);let d=this.getFrozenWidth(),h=0;for(let e=0;e<this.leafCols.length;e++)if(this.isColumnScrollable(e)){if(d+=this.getColW(e),o<d){h=e;break}h=e}this.fillDrag.currentRow=c,this.fillDrag.currentCol=h},commitFillDrag(){const e=this.fillDrag.sourceRange;if(!e)return void(this.fillDrag.active=!1);const n=this.fillDrag.currentRow,t=this.fillDrag.currentCol,i=this.data,o=i.slice();let l;const s=[];if(n>e.endRow){const t=n-e.endRow;for(let n=e.startCol;n<=e.endCol;n++){const l=this.leafCols[n];if(!l)continue;const r=[];for(let n=e.startRow;n<=e.endRow;n++)r.push(i[n]?i[n][l.key]:"");const a=w(r,t);for(let r=0;r<t;r++){const t=e.endRow+1+r;if(t<this.totalRows&&this.isCellEditable(t,n)){if(!this.isCellValueValid(t,n,a[r])){s.push({row:t,col:n,value:a[r]});continue}o[t]===i[t]&&(o[t]={...i[t]}),o[t][l.key]=a[r]}}}l={startRow:e.endRow+1,endRow:n,startCol:e.startCol,endCol:e.endCol}}else if(t>e.endCol){const n=t-e.endCol;for(let t=e.startRow;t<=e.endRow;t++){const l=[];for(let n=e.startCol;n<=e.endCol;n++){const e=this.leafCols[n];l.push(e&&i[t]?i[t][e.key]:"")}const r=w(l,n);for(let l=0;l<n;l++){const n=e.endCol+1+l;if(n<this.leafCols.length){const e=this.leafCols[n];if(e&&this.isCellEditable(t,n)){if(!this.isCellValueValid(t,n,r[l])){s.push({row:t,col:n,value:r[l]});continue}o[t]===i[t]&&(o[t]={...i[t]}),o[t][e.key]=r[l]}}}}l={startRow:e.startRow,endRow:e.endRow,startCol:e.endCol+1,endCol:t}}l&&(this.strictBulkValidation&&s.length>0?this._emitBulkValidationError(s,!0):(this._emitDataChange(i,o),this.$emit("fill",{sourceRange:e,targetRange:l,values:o}),s.length>0&&this._emitBulkValidationError(s,!1))),this.fillDrag.active=!1,this.fillDrag.sourceRange=null},doubleFillDown(){const e=this.primaryRange;if(!e)return;const n=o(e),t=n.startCol>0?n.startCol-1:n.endCol+1;if(t<0||t>=this.leafCols.length)return;const i=this.leafCols[t];if(!i)return;let l=n.endRow;for(let e=n.endRow+1;e<this.totalRows&&(this.data[e]&&""!==this.data[e][i.key]&&void 0!==this.data[e][i.key]);e++)l=e;if(l<=n.endRow)return;const s=l-n.endRow,r=this.data,a=r.slice(),c=[];for(let e=n.startCol;e<=n.endCol;e++){const t=this.leafCols[e];if(!t)continue;const i=[];for(let e=n.startRow;e<=n.endRow;e++)i.push(r[e]?r[e][t.key]:"");const o=w(i,s);for(let i=0;i<s;i++){const l=n.endRow+1+i;if(l<this.totalRows&&this.isCellEditable(l,e)){if(!this.isCellValueValid(l,e,o[i])){c.push({row:l,col:e,value:o[i]});continue}a[l]===r[l]&&(a[l]={...r[l]}),a[l][t.key]=o[i]}}}this.strictBulkValidation&&c.length>0?this._emitBulkValidationError(c,!0):(this._emitDataChange(r,a),c.length>0&&this._emitBulkValidationError(c,!1))},copySelection(){const e=this.primaryRange;if(!e)return;const n=o(e);this.clipboardMatrix=m(this.data,this.leafCols,n),this.clipboardRange=n,this.clipboardMode="copy";const t=this.clipboardMatrix.map(e=>e.map(e=>(null==e?"":String(e)).replace(/\t/g," ").replace(/\r?\n/g," ")).join("\t")).join("\n");navigator.clipboard&&navigator.clipboard.writeText(t).catch(()=>{})},cutSelection(){this.copySelection(),this.clipboardMode="cut"},async pasteAt(e,n,t={}){const{mode:i="all",transpose:l=!1}=t;let s=null;if(navigator.clipboard)try{const e=await navigator.clipboard.readText();e&&(s=(r=e)?r.split("\n").map(e=>e.split("\t")):[[""]])}catch(e){}var r;if(!s&&this.clipboardMatrix&&(s=this.clipboardMatrix),!s)return;l&&(s=function(e){if(!e||0===e.length)return[];const n=e.length,t=e[0].length;return Array.from({length:t},(t,i)=>Array.from({length:n},(n,t)=>e[t][i]))}(s));if(!this.clipboardMode||"copy"===this.clipboardMode){const t=this.primaryRange;if(t){const i=o(t),l=i.endRow-i.startRow+1,r=i.endCol-i.startCol+1,a=s.length,c=s[0]?s[0].length:0;(l>a||r>c)&&l%a===0&&r%c===0&&(s=function(e,n,t){const i=e.length,o=e[0]?e[0].length:0;if(!i||!o)return e;if(n%i!==0||t%o!==0)return e;const l=[];for(let s=0;s<n;s++){const n=[];for(let l=0;l<t;l++)n.push(e[s%i][l%o]);l.push(n)}return l}(s,l,r),e=i.startRow,n=i.startCol)}}const a=this.data,{newData:c,affectedRange:d,skipped:h}=v(a,this.leafCols,e,n,s,i,(e,n)=>this.isCellEditable(e,n),(e,n,t)=>this.isCellValueValid(e,n,t));if(this.strictBulkValidation&&h.length>0)this._emitBulkValidationError(h,!0);else{if("cut"===this.clipboardMode&&this.clipboardRange){const e=this.clipboardRange;for(let n=e.startRow;n<=e.endRow;n++){c[n]===a[n]&&(c[n]={...a[n]});for(let t=e.startCol;t<=e.endCol;t++){const e=this.leafCols[t];e&&this.isCellEditable(n,t)&&this.isCellValueValid(n,t,"")&&(c[n][e.key]="")}}this.clipboardRange=null,this.clipboardMode=null}this._emitDataChange(a,c),h.length>0&&this._emitBulkValidationError(h,!1)}},ctxCopy(){this.hideContextMenu(),this.copySelection()},ctxCut(){this.hideContextMenu(),this.cutSelection()},ctxPaste(){this.hideContextMenu(),this.pasteAt(this.activeCell.row,this.activeCell.col)},ctxPasteSpecial(e,n=!1){this.hideContextMenu(),this.pasteAt(this.activeCell.row,this.activeCell.col,{mode:e,transpose:n})},startColResize(e,n){this.$refs.wrapper.getBoundingClientRect(),this.resizing={active:!0,type:"col",index:n,startX:e.clientX,startY:e.clientY,startSize:this.getColW(n),currentPos:e.clientX},e.preventDefault()},startRowResize(e,n){this.resizing={active:!0,type:"row",index:n,startX:e.clientX,startY:e.clientY,startSize:this.getRowH(n),currentPos:e.clientY},e.preventDefault()},handleResizeMove(e){if("col"===this.resizing.type){this.resizing.currentPos=e.clientX;const n=Math.max(20,this.resizing.startSize+(e.clientX-this.resizing.startX));this.$set(this.colWidths,this.resizing.index,n),this._cellStyleCache=Object.create(null)}else this.resizing.currentPos=e.clientY,this._resizePreviewHeight=Math.max(10,this.resizing.startSize+(e.clientY-this.resizing.startY)),this._resizeRAF||(this._resizeRAF=requestAnimationFrame(()=>{this._resizeRAF=null,this._rowStyleCache=Object.create(null),this._rowHeaderStyleCache=Object.create(null),this._cellStyleCache=Object.create(null),this.$set(this.rowHeights,this.resizing.index,this._resizePreviewHeight),this.updateVirtualScroll()}))},commitResize(){if(this._resizeRAF&&(cancelAnimationFrame(this._resizeRAF),this._resizeRAF=null,"row"===this.resizing.type&&void 0!==this._resizePreviewHeight&&this.$set(this.rowHeights,this.resizing.index,this._resizePreviewHeight)),"col"===this.resizing.type){this._cellStyleCache=Object.create(null);const e=this.colWidths[this.resizing.index];this.$emit("column-resize",{index:this.resizing.index,newSize:e})}else{this._rowStyleCache=Object.create(null),this._rowHeaderStyleCache=Object.create(null),this._cellStyleCache=Object.create(null);const e=this.rowHeights[this.resizing.index];this.$emit("row-resize",{index:this.resizing.index,newSize:e}),this.updateVirtualScroll()}this.resizing.active=!1},autoFitColumn(e){const n=this.leafCols[e];if(!n)return;let t=(n.title||"").length;for(let e=0;e<Math.min(this.totalRows,500);e++){const i=this.data[e]?String(this.data[e][n.key]||""):"";i.length>t&&(t=i.length)}const i=Math.max(50,8*t+16);this.$set(this.colWidths,e,i),this._cellStyleCache=Object.create(null),this.$emit("column-resize",{index:e,newSize:i})},_applyAutoWidth(){this.autoWidth&&this.leafCols.length&&(this.leafCols.forEach((e,n)=>{let t=(e.title||"").length;for(let n=0;n<Math.min(this.totalRows,500);n++){const i=this.data[n]?String(this.data[n][e.key]||""):"";i.length>t&&(t=i.length)}this.$set(this.colWidths,n,Math.max(50,8*t+16))}),this._cellStyleCache=Object.create(null))},ctxHideRow(){this.hideContextMenu();const e=this.primaryRange;if(!e)return;const n=o(e),t=[...this.hiddenRows];for(let e=n.startRow;e<=n.endRow;e++)t.includes(e)||t.push(e);this.$emit("update:hiddenRows",t)},ctxHideColumn(){this.hideContextMenu();const e=this.primaryRange;if(!e)return;const n=o(e),t=[...this.hiddenColumns];for(let e=n.startCol;e<=n.endCol;e++)t.includes(e)||t.push(e);this.$emit("update:hiddenColumns",t)},ctxUnhideRows(){this.hideContextMenu(),this.$emit("update:hiddenRows",[])},ctxUnhideColumns(){this.hideContextMenu(),this.$emit("update:hiddenColumns",[])},ctxPinLeft(){const e=this.leafCols[this.contextMenu.col];e?(this.$emit("update:pinnedColumns",{...this.pinnedColumns,[e.key]:"left"}),this.hideContextMenu()):this.hideContextMenu()},ctxPinRight(){const e=this.leafCols[this.contextMenu.col];e?(this.$emit("update:pinnedColumns",{...this.pinnedColumns,[e.key]:"right"}),this.hideContextMenu()):this.hideContextMenu()},ctxUnpin(){const e=this.leafCols[this.contextMenu.col];if(!e)return void this.hideContextMenu();const n={...this.pinnedColumns};delete n[e.key],this.$emit("update:pinnedColumns",n),this.hideContextMenu()},isColPinned(e){const n=this.leafCols[e];return!(!n||!this.pinnedColumns[n.key])},mergeCells(e){const n=this.internalMerges.slice(),{merges:t,conflict:i}=function(e,n){const t=o(e);return t.startRow===t.endRow&&t.startCol===t.endCol?{merges:n,conflict:!1}:x(t,n)?{merges:n,conflict:!0}:{merges:[...n,t],conflict:!1}}(e,this.internalMerges);return i?(this.$emit("merge-error",{range:e,reason:"Overlaps existing merge"}),!1):(this.internalMerges=t,this._emitMergeChange(n,[...t]),!0)},unmergeCells(e,n){const t=this.internalMerges.slice(),i=function(e,n,t){return t.filter(t=>{const i=o(t);return!(e>=i.startRow&&e<=i.endRow&&n>=i.startCol&&n<=i.endCol)})}(e,n,this.internalMerges);this.internalMerges=i,this._emitMergeChange(t,[...i])},ctxMerge(){this.hideContextMenu();const e=this.primaryRange;e&&this.mergeCells(o(e))},ctxUnmerge(){this.hideContextMenu(),this.unmergeCells(this.activeCell.row,this.activeCell.col)},groupRows(e,n){this.internalGroups=b("row",e,n,this.internalGroups)},groupColumns(e,n){this.internalGroups=b("column",e,n,this.internalGroups)},ungroupRows(e,n){this.internalGroups=R("row",e,n,this.internalGroups)},ungroupColumns(e,n){this.internalGroups=R("column",e,n,this.internalGroups)},toggleGroup(e){this.internalCollapsedGroups=function(e,n){return n.find(n=>n.type===e.type&&n.startIndex===e.startIndex&&n.endIndex===e.endIndex)?n.filter(n=>!(n.type===e.type&&n.startIndex===e.startIndex&&n.endIndex===e.endIndex)):[...n,{type:e.type,startIndex:e.startIndex,endIndex:e.endIndex}]}(e,this.internalCollapsedGroups),this.$emit("update:collapsedGroups",[...this.internalCollapsedGroups]),this.$emit("group-toggle",{group:e,collapsed:k(e,this.internalCollapsedGroups)}),this.updateVirtualScroll()},isGroupCollapsed(e){return k(e,this.internalCollapsedGroups)},expandAll(){this.internalCollapsedGroups=[],this.$emit("update:collapsedGroups",[]),this.updateVirtualScroll()},collapseAll(){this.internalCollapsedGroups=[...this.internalGroups],this.$emit("update:collapsedGroups",[...this.internalGroups]),this.updateVirtualScroll()},startDragMove(e,n,t){const i=this.primaryRange;i&&(this.dragMove={active:!0,sourceRange:o(i),startX:e.clientX,startY:e.clientY,targetRow:n,targetCol:t,copyMode:e.ctrlKey||e.metaKey},this.$emit("cell-drag-start",{sourceRange:this.dragMove.sourceRange}),e.preventDefault())},handleDragMoveMove(e){this.dragMove.copyMode=e.ctrlKey||e.metaKey,this.$emit("cell-drag-move",{sourceRange:this.dragMove.sourceRange,targetRow:this.dragMove.targetRow,targetCol:this.dragMove.targetCol})},async commitDragMove(e){if(!this.dragMove.active)return;const{sourceRange:n,targetRow:t,targetCol:i,copyMode:o}=this.dragMove,l={startRow:t,startCol:i,endRow:t+(n.endRow-n.startRow),endCol:i+(n.endCol-n.startCol)};let s=!0;if(this.beforeDrop&&(s=await Promise.resolve(this.beforeDrop(n,l))),s){const e=this.data,s=m(e,this.leafCols,n),{newData:r}=v(e,this.leafCols,t,i,s,"all",(e,n)=>this.isCellEditable(e,n));if(!o)for(let t=n.startRow;t<=n.endRow;t++){r[t]===e[t]&&(r[t]={...e[t]});for(let e=n.startCol;e<=n.endCol;e++){const n=this.leafCols[e];n&&this.isCellEditable(t,e)&&(r[t][n.key]="")}}this._emitDataChange(e,r),this.$emit("cell-drop",{sourceRange:n,targetRange:l})}this.dragMove={active:!1,sourceRange:null,startX:0,startY:0,targetRow:-1,targetCol:-1,copyMode:!1},this.isMouseDown=!1},async exportToExcel(e={}){const{filename:n="export.xlsx",sheetName:t="Sheet1",scope:l="all",includeHidden:s=!1}=e;this.$emit("export-start"),this.exportState={active:!0};const d={data:this.data,columns:this.columns,leafCols:this.leafCols,merges:this.internalMerges,hiddenCols:[...this.effectiveHiddenColSet],hiddenRows:[...this.hiddenRowSet],range:"selection"===l?this.primaryRange:null,filename:n,sheetName:t,scope:l,includeHidden:s};try{null||function(e){const{data:n,columns:t,leafCols:l,merges:s=[],hiddenColSet:d=new Set,hiddenRowSet:h=new Set,range:p=null,filename:u="export.xlsx",sheetName:g="Sheet1",scope:f="all",includeHidden:A=!1}=e,C=l.filter((e,n)=>{if(!A&&d.has(n))return!1;if("selection"===f&&p){const e=o(p);if(n<e.startCol||n>e.endCol)return!1}return!0});let w=0,m=n.length-1;if("selection"===f&&p){const e=o(p);w=e.startRow,m=e.endRow}const v=[];for(let e=w;e<=m;e++)!A&&h.has(e)||v.push({rowIndex:e,data:n[e]});const x=a(t),y=c(t,x),b=i.utils.book_new(),R=[];for(let e=0;e<y.length;e++){const n=[];for(const t of y[e])if(t.isGroup){const e=r(t.column.children||[]).filter(e=>C.some(n=>n.key===e.key));if(0===e.length)continue;n.push(t.column.title||"");for(let t=1;t<e.length;t++)n.push(null)}else{if(!C.some(e=>e.key===t.column.key))continue;t.rowspan,n.push(t.column.title||"")}R.push(n)}for(const{data:e}of v){const n=C.map(n=>{const t=e?e[n.key]:"";return void 0!==t?t:""});R.push(n)}const k=i.utils.aoa_to_sheet(R);k["!cols"]=C.map(e=>({wch:Math.round((e.width||100)/7)}));const S=[];for(const e of s){const n=o(e),t="selection"===f&&p?o(p):{startRow:0,startCol:0};if("selection"===f){if(n.startRow<t.startRow||n.endRow>t.endRow)continue;if(n.startCol<t.startCol||n.endCol>t.endCol)continue}const i=C.findIndex(e=>l.indexOf(e)===n.startCol),s=C.findIndex(e=>l.indexOf(e)===n.endCol);if(i<0||s<0)continue;const r=v.filter(e=>e.rowIndex<n.startRow).length,a=v.filter(e=>e.rowIndex<=n.endRow).length;S.push({s:{r:x+r,c:i},e:{r:x+a-1,c:s}})}S.length&&(k["!merges"]=S),i.utils.book_append_sheet(b,k,g),i.writeFile(b,u)}({...d,hiddenColSet:this.effectiveHiddenColSet,hiddenRowSet:this.hiddenRowSet}),this.$emit("export-success")}catch(e){this.$emit("export-error",e)}finally{this.exportState={active:!1}}},_exportViaWorker:(e,n)=>new Promise((t,i)=>{let o=!1;const l=(n,t)=>{o||(o=!0,e.terminate(),n(t))};e.onmessage=e=>{const{type:n,buffer:o,filename:s,message:r}=e.data;if("result"===n){const e=new Blob([new Uint8Array(o)],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}),n=URL.createObjectURL(e),i=document.createElement("a");i.href=n,i.download=s,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(n),l(t)}else"error"===n&&l(i,new Error(r))},e.onerror=e=>l(i,new Error(e.message||"Export worker xatosi")),e.postMessage(n)}),async importFromFile(e,n={}){this.importState={active:!0,percent:5,message:"Fayl o'qilmoqda..."};try{let t;null||(t=await M(e,this.columns,n,(e,n)=>{this.importState={active:!0,percent:e,message:n}}));const{columns:i,merges:o}=t,l=t.data.map(e=>Object.freeze(e));this.$emit("input",l),this.$emit("update:columns",i);const s=o&&o.length>0;this.internalMerges=s?o:[],this.$emit("update:merges",s?o:[]),this.$emit("update:collapsedGroups",[]),this.$emit("update:hiddenRows",[]),this.$emit("import-success",{data:l,columns:i,merges:o}),this.rowHeights={},this.scrollTop=0,this.scrollLeft=0,this.$nextTick(()=>{this.$refs.scrollBody&&(this.$refs.scrollBody.scrollTop=0,this.$refs.scrollBody.scrollLeft=0),this.$refs.scrollColHeader&&(this.$refs.scrollColHeader.scrollLeft=0),this.$refs.scrollFooter&&(this.$refs.scrollFooter.scrollLeft=0),this.$refs.scrollRowHeaders&&(this.$refs.scrollRowHeaders.scrollTop=0),this.updateVirtualScroll()})}catch(e){this.$emit("import-error",e)}finally{this.importState={active:!1,percent:0,message:""}}},_importViaWorker(e,n,t){return new Promise(async(i,o)=>{let l=!1;const s=(n,t)=>{l||(l=!0,e.terminate(),n(t))};e.onmessage=e=>{const{type:n,percent:t,message:l,data:r,columns:a,merges:c}=e.data;"progress"===n?this.importState={active:!0,percent:t,message:l}:"result"===n?s(i,{data:r,columns:a,merges:c}):"error"===n&&s(o,new Error(l))},e.onerror=e=>s(o,new Error(e.message||"Import worker xatosi"));try{const i=await n.arrayBuffer();e.postMessage({buffer:i,existingColumns:this.columns,options:t},[i])}catch(e){s(o,e)}})},_pushHistory(e){this._historyIndex<this._history.length-1&&this._history.splice(this._historyIndex+1),this._history.push(e),this._history.length>100?this._history.shift():this._historyIndex++},_emitDataChange(e,n){if(!this._historySkip){const t=[],i=[],o=[],l=Math.max(e.length,n.length);for(let s=0;s<l;s++)e[s]!==n[s]&&(t.push(s),i.push(e[s]),o.push(n[s]));t.length>0&&this._pushHistory({type:"data",before:{indices:t,rows:i},after:{indices:t,rows:o}})}this.$emit("input",n)},_emitMergeChange(e,n){this._historySkip||this._pushHistory({type:"merges",before:e,after:n}),this.$emit("update:merges",n)},undo(){if(this._historyIndex<0)return;const e=this._history[this._historyIndex--];this._historySkip=!0;try{if("data"===e.type){const n=this.data.slice();for(let t=0;t<e.before.indices.length;t++)n[e.before.indices[t]]=e.before.rows[t];this.$emit("input",n)}else"merges"===e.type&&(this.internalMerges=e.before.slice(),this.$emit("update:merges",e.before.slice()))}finally{this._historySkip=!1}},redo(){if(this._historyIndex>=this._history.length-1)return;const e=this._history[++this._historyIndex];this._historySkip=!0;try{if("data"===e.type){const n=this.data.slice();for(let t=0;t<e.after.indices.length;t++)n[e.after.indices[t]]=e.after.rows[t];this.$emit("input",n)}else"merges"===e.type&&(this.internalMerges=e.after.slice(),this.$emit("update:merges",e.after.slice()))}finally{this._historySkip=!1}}}};var X=function(){var e=this,n=e.$createElement,t=e._self._c||n;return t("div",{ref:"wrapper",staticClass:"eg-wrapper",class:"eg-size-"+e.size,style:e.wrapperStyle,attrs:{tabindex:"0"},on:{keydown:e.onKeyDown,keypress:e.onKeyPress}},[e.contextMenu.visible?t("div",{staticClass:"eg-context-menu",style:{top:e.contextMenu.y+"px",left:e.contextMenu.x+"px"},on:{click:function(e){e.stopPropagation()}}},[t("div",{staticClass:"eg-context-item",on:{click:e.ctxCopy}},[e._v(e._s(e.t.copy))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxCut}},[e._v(e._s(e.t.cut))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxPaste}},[e._v(e._s(e.t.paste))]),e._v(" "),t("div",{staticClass:"eg-context-separator"}),e._v(" "),t("div",{staticClass:"eg-context-item eg-context-submenu-parent"},[e._v("\n "+e._s(e.t.pasteSpecial)+"\n "),t("div",{staticClass:"eg-context-submenu"},[t("div",{staticClass:"eg-context-item",on:{click:function(n){return e.ctxPasteSpecial("all")}}},[e._v(e._s(e.t.pasteAll))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:function(n){return e.ctxPasteSpecial("values")}}},[e._v(e._s(e.t.pasteValues))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:function(n){return e.ctxPasteSpecial("formats")}}},[e._v(e._s(e.t.pasteFormats))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:function(n){return e.ctxPasteSpecial("all",!0)}}},[e._v(e._s(e.t.transpose))])])]),e._v(" "),t("div",{staticClass:"eg-context-separator"}),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxMerge}},[e._v(e._s(e.t.mergeCells))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxUnmerge}},[e._v(e._s(e.t.unmergeCells))]),e._v(" "),t("div",{staticClass:"eg-context-separator"}),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxHideRow}},[e._v(e._s(e.t.hideRows))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxHideColumn}},[e._v(e._s(e.t.hideColumns))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxUnhideRows}},[e._v(e._s(e.t.unhideRows))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxUnhideColumns}},[e._v(e._s(e.t.unhideColumns))]),e._v(" "),e.contextMenu.row<0&&e.contextMenu.col>=0?[t("div",{staticClass:"eg-context-separator"}),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxPinLeft}},[e._v(e._s(e.t.pinLeft))]),e._v(" "),t("div",{staticClass:"eg-context-item",on:{click:e.ctxPinRight}},[e._v(e._s(e.t.pinRight))]),e._v(" "),e.isColPinned(e.contextMenu.col)?t("div",{staticClass:"eg-context-item",on:{click:e.ctxUnpin}},[e._v(e._s(e.t.unpin))]):e._e()]:e._e()],2):e._e(),e._v(" "),t("div",{ref:"container",staticClass:"eg-container",on:{scroll:e.onScroll,click:e.hideContextMenu}},[t("div",{staticClass:"eg-header-area",style:e.headerAreaStyle},[t("div",{staticClass:"eg-corner",style:e.cornerStyle,on:{click:e.selectAll}}),e._v(" "),e.rowGroups.length?t("div",{staticClass:"eg-row-outline-gutter",style:e.rowOutlineGutterStyle}):e._e(),e._v(" "),e.getFrozenWidth()>0?t("div",{staticClass:"eg-frozen-col-header",style:e.frozenColHeaderStyle},[t("div",{style:{position:"relative",height:e.headerRowHeight*e.headerDepth+"px"}},[e._l(e.headerRows,function(n,i){return[e._l(n,function(n){return[e.isCellVisible(n,i,!0)?[t("div",{key:"fh-"+i+"-"+(n.column.key||n.column.title),staticClass:"eg-header-cell",class:{"eg-header-group":n.isGroup},style:e.getHeaderCellStyle(n,i,!0),on:{mousedown:function(t){return e.onColHeaderMouseDown(t,n)},dblclick:function(t){return e.onColHeaderDblClick(t,n)},contextmenu:function(t){return t.preventDefault(),e.onColHeaderContextMenu(t,n)}}},[n.isGroup?t("span",[e._v(e._s(n.column.title))]):e._t("header-"+n.column.key,function(){return[e._v("\n "+e._s(n.column.title)+"\n ")]},{column:n.column}),e._v(" "),n.isGroup?e._e():t("div",{staticClass:"eg-col-resize-handle",on:{mousedown:function(t){return t.stopPropagation(),e.startColResize(t,n.column._flatIndex)}}})],2)]:e._e()]})]})],2)]):e._e(),e._v(" "),t("div",{ref:"scrollColHeader",staticClass:"eg-scroll-col-header",style:e.scrollColHeaderStyle},[t("div",{staticClass:"eg-scroll-col-header-inner",style:{width:e.scrollContentWidth+"px",height:e.headerRowHeight*e.headerDepth+"px",position:"relative"}},[e._l(e.headerRows,function(n,i){return[e._l(n,function(n){return[e.isCellVisible(n,i,!1)?[t("div",{key:"sh-"+i+"-"+(n.column.key||n.column.title),staticClass:"eg-header-cell",class:{"eg-header-group":n.isGroup},style:e.getHeaderCellStyle(n,i,!1),on:{mousedown:function(t){return e.onColHeaderMouseDown(t,n)},dblclick:function(t){return e.onColHeaderDblClick(t,n)},contextmenu:function(t){return t.preventDefault(),e.onColHeaderContextMenu(t,n)}}},[n.isGroup?t("span",[e._v(e._s(n.column.title))]):e._t("header-"+n.column.key,function(){return[e._v("\n "+e._s(n.column.title)+"\n ")]},{column:n.column}),e._v(" "),n.isGroup?e._e():t("div",{staticClass:"eg-col-resize-handle",on:{mousedown:function(t){return t.stopPropagation(),e.startColResize(t,n.column._flatIndex)}}})],2)]:e._e()]})]})],2)]),e._v(" "),e.hasPinnedRight?t("div",{staticClass:"eg-pinned-right-header",style:e.pinnedRightHeaderStyle},[t("div",{style:{position:"relative",height:e.headerRowHeight*e.headerDepth+"px"}},[e._l(e.headerRows,function(n,i){return[e._l(n,function(n){return[e.isCellInZone(n,i,"right")?[t("div",{key:"prh-"+i+"-"+(n.column.key||n.column.title),staticClass:"eg-header-cell",class:{"eg-header-group":n.isGroup},style:e.getHeaderCellStyleForZone(n,i,"right"),on:{mousedown:function(t){return e.onColHeaderMouseDown(t,n)},dblclick:function(t){return e.onColHeaderDblClick(t,n)},contextmenu:function(t){return t.preventDefault(),e.onColHeaderContextMenu(t,n)}}},[n.isGroup?t("span",[e._v(e._s(n.column.title))]):e._t("header-"+n.column.key,function(){return[e._v("\n "+e._s(n.column.title)+"\n ")]},{column:n.column}),e._v(" "),n.isGroup?e._e():t("div",{staticClass:"eg-col-resize-handle",on:{mousedown:function(t){return t.stopPropagation(),e.startColResize(t,n.column._flatIndex)}}})],2)]:e._e()]})]})],2)]):e._e()]),e._v(" "),t("div",{staticClass:"eg-body-area",style:e.bodyAreaStyle},[t("div",{staticClass:"eg-row-header-col",style:e.rowHeaderColStyle},[t("div",{ref:"scrollRowHeaders",staticClass:"eg-scroll-row-headers",style:e.scrollRowHeadersStyle},[t("div",{style:{height:e.totalVirtualHeight+"px",position:"relative"}},[t("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(n){return[e.isRowHidden(n)?e._e():t("div",{key:"srh-"+n,staticClass:"eg-row-header-cell",style:e.getRowHeaderStyle(n),on:{mousedown:function(t){return e.onRowHeaderMouseDown(t,n)},contextmenu:function(t){return t.preventDefault(),e.onRowContextMenu(t,n)}}},[e._v("\n "+e._s(n+1)+"\n "),t("div",{staticClass:"eg-row-resize-handle",on:{mousedown:function(t){return t.stopPropagation(),e.startRowResize(t,n)}}})])]})],2)])])]),e._v(" "),e.rowGroups.length?t("div",{staticClass:"eg-row-outline-col",style:e.rowOutlineColStyle},[e._l(e.rowGroups,function(n){return[t("div",{key:"rg-"+n.startIndex+"-"+n.endIndex,staticClass:"eg-group-btn",class:{collapsed:e.isGroupCollapsed(n)},style:e.getRowGroupBtnStyle(n),on:{click:function(t){return e.toggleGroup(n)}}},[e._v(e._s(e.isGroupCollapsed(n)?"+":"−"))])]})],2):e._e(),e._v(" "),t("div",{ref:"scrollBody",staticClass:"eg-scroll-body",style:e.scrollBodyStyle,on:{scroll:e.onBodyScroll}},[t("div",{staticClass:"eg-scroll-body-inner",style:{width:e.scrollContentWidth+"px",height:e.totalVirtualHeight+"px",position:"relative"}},[e.getFrozenWidth()>0?t("div",{staticClass:"eg-frozen-col-body",style:e.frozenColBodyStyle},[t("div",{style:{height:e.totalVirtualHeight+"px",position:"relative"}},[t("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(n){return[t("transition",{key:"tr-fc-"+n,attrs:{name:e._rowTransitionName}},[e.isRowHidden(n)?e._e():t("div",{key:"fc-row-"+n,staticClass:"eg-row",style:e.getRowStyle(n)},[e._l(e.leafCols,function(i,o){return[e.isColumnPinnedLeft(o)&&!e.isColHidden(o)?[t("div",{key:"fc-cell-"+n+"-"+o,staticClass:"eg-cell",class:e.getCellClasses(n,o),style:e.getCellStyle(n,o),on:{mousedown:function(t){return e.onCellMouseDown(t,n,o)},mouseover:function(t){return e.onCellMouseOver(t,n,o)},mouseup:e.onCellMouseUp,dblclick:function(t){return e.onCellDblClick(n,o)},contextmenu:function(t){return t.preventDefault(),e.onCellContextMenu(t,n,o)}}},[e.isMergedAway(n,o)?t("div",{staticStyle:{display:"none"}}):e.isEditing(n,o)?[e.hasCellSlot(i)?e._t("cell-"+i.key,null,{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,o),isEditing:!0,done:function(n){return e.commitEditSlot(n)},cancel:e.cancelEdit}):e.hasLegacyEditSlot(i)?e._t("edit-"+i.key,null,{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,o),done:function(n){return e.commitEditSlot(n)},cancel:e.cancelEdit}):t("input",{directives:[{name:"model",rawName:"v-model",value:e.editValue,expression:"editValue"}],ref:"editInput",refInFor:!0,staticClass:"eg-cell-input",domProps:{value:e.editValue},on:{blur:e.commitEdit,keydown:function(n){return n.stopPropagation(),e.onEditKeyDown.apply(null,arguments)},input:function(n){n.target.composing||(e.editValue=n.target.value)}}})]:"checkbox"===i.type?[t("eg-checkbox",{attrs:{value:e.isCheckboxChecked(n,o),disabled:!e.isCellEditable(n,o)},on:{change:function(t){return e.setCheckboxValue(n,o,t)}}})]:[e._t("cell-"+i.key,function(){return[e._v("\n "+e._s(e.isSelectLikeCol(i)?e.getSelectLabel(o,e.getCellValue(n,o)):e.getCellValue(n,o))+"\n ")]},{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,o),isEditing:!1,done:null,cancel:null})]],2)]:e._e()]})],2)])]})],2)])]):e._e(),e._v(" "),t("div",{staticClass:"eg-main-cells",style:e.mainCellsStyle},[t("div",{style:{height:e.totalVirtualHeight+"px",position:"relative",width:e.scrollableContentWidth+"px"}},[t("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(n){return[t("transition",{key:"tr-m-"+n,attrs:{name:e._rowTransitionName}},[e.isRowHidden(n)?e._e():t("div",{key:"row-"+n,staticClass:"eg-row",style:e.getRowStyle(n),attrs:{"data-row":n}},[e._l(e.leafCols,function(i,o){return[e.isColumnScrollable(o)&&!e.isColHidden(o)?[t("div",{key:"cell-"+n+"-"+o,staticClass:"eg-cell",class:e.getCellClasses(n,o),style:e.getCellStyle(n,o),on:{mousedown:function(t){return e.onCellMouseDown(t,n,o)},mouseover:function(t){return e.onCellMouseOver(t,n,o)},mouseup:e.onCellMouseUp,dblclick:function(t){return e.onCellDblClick(n,o)},contextmenu:function(t){return t.preventDefault(),e.onCellContextMenu(t,n,o)}}},[e.isMergedAway(n,o)?t("div",{staticStyle:{display:"none"}}):e.isEditing(n,o)?[e.hasCellSlot(i)?e._t("cell-"+i.key,null,{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,o),isEditing:!0,done:function(n){return e.commitEditSlot(n)},cancel:e.cancelEdit}):e.hasLegacyEditSlot(i)?e._t("edit-"+i.key,null,{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,o),done:function(n){return e.commitEditSlot(n)},cancel:e.cancelEdit}):t("input",{directives:[{name:"model",rawName:"v-model",value:e.editValue,expression:"editValue"}],ref:"editInput",refInFor:!0,staticClass:"eg-cell-input",domProps:{value:e.editValue},on:{blur:e.commitEdit,keydown:function(n){return n.stopPropagation(),e.onEditKeyDown.apply(null,arguments)},input:function(n){n.target.composing||(e.editValue=n.target.value)}}})]:"checkbox"===i.type?[t("eg-checkbox",{attrs:{value:e.isCheckboxChecked(n,o),disabled:!e.isCellEditable(n,o)},on:{change:function(t){return e.setCheckboxValue(n,o,t)}}})]:[e._t("cell-"+i.key,function(){return[e._v("\n "+e._s(e.isSelectLikeCol(i)?e.getSelectLabel(o,e.getCellValue(n,o)):e.getCellValue(n,o))+"\n ")]},{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,o),isEditing:!1,done:null,cancel:null})]],2)]:e._e()]})],2)])]})],2)])]),e._v(" "),e.hasPinnedRight?t("div",{staticClass:"eg-pinned-right-body",style:e.pinnedRightBodyStyle},[t("div",{style:{height:e.totalVirtualHeight+"px",position:"relative"}},[t("div",{style:{transform:"translateY("+e.virtualOffsetTop+"px)"}},[e._l(e.visibleRowRange,function(n){return[t("transition",{key:"tr-pr-"+n,attrs:{name:e._rowTransitionName}},[e.isRowHidden(n)?e._e():t("div",{key:"pr-row-"+n,staticClass:"eg-row",style:e.getRowStyle(n)},[e._l(e.pinnedRightCols,function(i){return[t("div",{key:"pr-cell-"+n+"-"+i._flatIndex,staticClass:"eg-cell",class:e.getCellClasses(n,i._flatIndex),style:e.getCellStyle(n,i._flatIndex),on:{mousedown:function(t){return e.onCellMouseDown(t,n,i._flatIndex)},mouseover:function(t){return e.onCellMouseOver(t,n,i._flatIndex)},mouseup:e.onCellMouseUp,dblclick:function(t){return e.onCellDblClick(n,i._flatIndex)},contextmenu:function(t){return t.preventDefault(),e.onCellContextMenu(t,n,i._flatIndex)}}},[e.isMergedAway(n,i._flatIndex)?t("div",{staticStyle:{display:"none"}}):e.isEditing(n,i._flatIndex)?[e.hasCellSlot(i)?e._t("cell-"+i.key,null,{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,i._flatIndex),isEditing:!0,done:function(n){return e.commitEditSlot(n)},cancel:e.cancelEdit}):e.hasLegacyEditSlot(i)?e._t("edit-"+i.key,null,{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,i._flatIndex),done:function(n){return e.commitEditSlot(n)},cancel:e.cancelEdit}):t("input",{directives:[{name:"model",rawName:"v-model",value:e.editValue,expression:"editValue"}],ref:"editInput",refInFor:!0,staticClass:"eg-cell-input",domProps:{value:e.editValue},on:{blur:e.commitEdit,keydown:function(n){return n.stopPropagation(),e.onEditKeyDown.apply(null,arguments)},input:function(n){n.target.composing||(e.editValue=n.target.value)}}})]:"checkbox"===i.type?[t("eg-checkbox",{attrs:{value:e.isCheckboxChecked(n,i._flatIndex),disabled:!e.isCellEditable(n,i._flatIndex)},on:{change:function(t){return e.setCheckboxValue(n,i._flatIndex,t)}}})]:[e._t("cell-"+i.key,function(){return[e._v("\n "+e._s(e.isSelectLikeCol(i)?e.getSelectLabel(i._flatIndex,e.getCellValue(n,i._flatIndex)):e.getCellValue(n,i._flatIndex))+"\n ")]},{row:e._dataRaw[n],rowIndex:n,column:i,value:e.getCellValue(n,i._flatIndex),isEditing:!1,done:null,cancel:null})]],2)]})],2)])]})],2)])]):e._e(),e._v(" "),e.fillHandleVisible?t("div",{staticClass:"eg-fill-handle",style:e.fillHandleStyle,on:{mousedown:function(n){return n.stopPropagation(),e.startFillDrag.apply(null,arguments)}}}):e._e(),e._v(" "),e.dragMove.active?t("div",{staticClass:"eg-drag-ghost",style:e.dragGhostStyle}):e._e()])]),e._v(" "),e.colGroups.length?t("div",{staticClass:"eg-col-outline-row",style:e.colOutlineRowStyle},[e._l(e.colGroups,function(n){return[t("div",{key:"cg-"+n.startIndex+"-"+n.endIndex,staticClass:"eg-group-btn",class:{collapsed:e.isGroupCollapsed(n)},style:e.getColGroupBtnStyle(n),on:{click:function(t){return e.toggleGroup(n)}}},[e._v(e._s(e.isGroupCollapsed(n)?"+":"−"))])]})],2):e._e()]),e._v(" "),e.hasFooter?t("div",{staticClass:"eg-footer-area",style:e.footerAreaStyle},[t("div",{staticClass:"eg-footer-label",style:e.footerLabelStyle},[e._v(e._s(e.t.total))]),e._v(" "),e.getFrozenWidth()>0?t("div",{staticClass:"eg-frozen-col-footer",style:e.frozenColFooterStyle},[t("div",{style:{position:"relative",height:e.footerRowHeight+"px"}},[e._l(e.leafCols,function(n,i){return[e.isColumnPinnedLeft(i)&&!e.isColHidden(i)?[t("div",{key:"ff-"+i,staticClass:"eg-footer-cell",style:e.getFooterCellStyle(i)},[e._v("\n "+e._s(e.getColumnTotalDisplay(i))+"\n ")])]:e._e()]})],2)]):e._e(),e._v(" "),t("div",{ref:"scrollFooter",staticClass:"eg-scroll-col-footer",style:e.scrollColHeaderStyle},[t("div",{staticClass:"eg-scroll-col-footer-inner",style:{width:e.scrollContentWidth+"px",height:e.footerRowHeight+"px",position:"relative"}},[e._l(e.leafCols,function(n,i){return[e.isColumnScrollable(i)&&!e.isColHidden(i)?[t("div",{key:"sf-"+i,staticClass:"eg-footer-cell",style:e.getFooterCellStyle(i)},[e._v("\n "+e._s(e.getColumnTotalDisplay(i))+"\n ")])]:e._e()]})],2)]),e._v(" "),e.hasPinnedRight?t("div",{staticClass:"eg-pinned-right-footer",style:e.pinnedRightFooterStyle},[t("div",{style:{position:"relative",height:e.footerRowHeight+"px"}},e._l(e.pinnedRightCols,function(n){return t("div",{key:"prf-"+n._flatIndex,staticClass:"eg-footer-cell",style:e.getFooterCellStyle(n._flatIndex)},[e._v(e._s(e.getColumnTotalDisplay(n._flatIndex)))])}),0)]):e._e()]):e._e(),e._v(" "),e.resizing.active?t("div",{staticClass:"eg-resize-line",style:e.resizeLineStyle}):e._e()]),e._v(" "),e.importState.active?t("div",{staticClass:"eg-import-overlay"},[t("div",{staticClass:"eg-import-dialog"},[t("div",{staticClass:"eg-import-title"},[e._v("Fayl import qilinmoqda")]),e._v(" "),t("div",{staticClass:"eg-import-bar-wrap"},[t("div",{staticClass:"eg-import-bar",style:{width:e.importState.percent+"%"}})]),e._v(" "),t("div",{staticClass:"eg-import-msg"},[e._v(e._s(e.importState.message))]),e._v(" "),t("div",{staticClass:"eg-import-pct"},[e._v(e._s(e.importState.percent)+"%")])])]):e._e(),e._v(" "),e.exportState.active?t("div",{staticClass:"eg-import-overlay"},[e._m(0)]):e._e(),e._v(" "),e.selectDropdown&&!e.selectDropdown.tree?t("eg-select",{ref:"selectDropdownComp",attrs:{value:e.selectDropdown.currentValue,options:e.selectDropdown.rawOptions,"enable-search":e.selectDropdown.enableSearch,"trigger-rect":e.selectDropdown.rect,"dropdown-only":!0},on:{select:e.onComponentSelect,close:e.closeSelectDropdown}}):e._e(),e._v(" "),e.selectDropdown&&e.selectDropdown.tree?t("eg-tree-select",{ref:"selectDropdownComp",attrs:{value:e.selectDropdown.currentValue,options:e.selectDropdown.rawOptions,"value-key":e.selectDropdown.keys.valueKey,"label-key":e.selectDropdown.keys.labelKey,"children-key":e.selectDropdown.keys.childrenKey,"enable-search":e.selectDropdown.enableSearch,"trigger-rect":e.selectDropdown.rect,"dropdown-only":!0},on:{select:e.onComponentSelect,close:e.closeSelectDropdown}}):e._e(),e._v(" "),e.invalidCell&&e.invalidCell.rect?t("div",{staticClass:"eg-validation-tooltip",style:e.invalidTooltipStyle},[e._v(e._s(e.invalidCell.message))]):e._e()],1)};X._withStripped=!0;const Z=H({render:X,staticRenderFns:[function(){var e=this,n=e.$createElement,t=e._self._c||n;return t("div",{staticClass:"eg-import-dialog"},[t("div",{staticClass:"eg-import-title"},[e._v("Fayl export qilinmoqda...")]),e._v(" "),t("div",{staticClass:"eg-export-spinner"})])}]},function(e){e&&e("data-v-5b531cb4_0",{source:"\n.eg-wrapper[data-v-5b531cb4] {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-sizing: border-box;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n}\n.eg-container[data-v-5b531cb4] {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: auto;\n position: relative;\n}\n.eg-header-area[data-v-5b531cb4] {\n display: flex;\n flex-shrink: 0;\n box-shadow: 0 2px 0 0 #d0d0d0;\n position: relative;\n z-index: 31;\n}\n.eg-header-cell[data-v-5b531cb4] {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n border-right: 1px solid #ccc;\n border-bottom: 1px solid #ccc;\n background: #f0f0f0;\n font-size: 12px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n position: absolute;\n user-select: none;\n cursor: pointer;\n}\n.eg-header-cell[data-v-5b531cb4]:hover { background: #e0e0e0;\n}\n.eg-header-group[data-v-5b531cb4] { background: #e8eaf6;\n}\n.eg-col-resize-handle[data-v-5b531cb4] {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n width: 5px;\n cursor: col-resize;\n z-index: 5;\n}\n.eg-body-area[data-v-5b531cb4] {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n/* ===== FOOTER AREA (column totals) ===== */\n.eg-footer-area[data-v-5b531cb4] {\n border-top: 1px solid #ccc;\n}\n.eg-frozen-col-footer[data-v-5b531cb4],\n.eg-scroll-col-footer[data-v-5b531cb4],\n.eg-pinned-right-footer[data-v-5b531cb4] {\n position: relative;\n}\n.eg-footer-cell[data-v-5b531cb4] {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n font-weight: 600;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: #f8f9fa;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n.eg-row[data-v-5b531cb4] {\n position: relative;\n}\n.eg-cell[data-v-5b531cb4] {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n border-bottom: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: white;\n cursor: default;\n display: flex;\n align-items: center;\n}\n.eg-cell-active[data-v-5b531cb4] {\n outline: 2px solid #1a73e8 !important;\n outline-offset: -2px;\n z-index: 3;\n}\n.eg-cell-selected[data-v-5b531cb4] {\n background: #e3f2fd !important;\n}\n.eg-cell-fill-preview[data-v-5b531cb4] {\n background: #bbdefb !important;\n outline: 1px dashed #1565c0;\n outline-offset: -1px;\n}\n.eg-cell-copy[data-v-5b531cb4] {\n outline: 1px dashed #1a73e8;\n outline-offset: -1px;\n}\n.eg-cell-merged-hidden[data-v-5b531cb4] {\n display: none !important;\n}\n.eg-cell-invalid[data-v-5b531cb4] {\n outline: 2px solid #e53935 !important;\n outline-offset: -2px;\n z-index: 4;\n}\n.eg-cell-input[data-v-5b531cb4] {\n width: 100%;\n height: 100%;\n border: none;\n outline: none;\n background: white;\n font-size: 13px;\n font-family: inherit;\n padding: 0;\n}\n.eg-row-header-cell[data-v-5b531cb4] {\n box-sizing: border-box;\n background: #f0f0f0;\n border-bottom: 1px solid #e0e0e0;\n border-right: 2px solid #bbb;\n text-align: center;\n font-size: 11px;\n color: #666;\n cursor: pointer;\n user-select: none;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.eg-row-resize-handle[data-v-5b531cb4] {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 4px;\n cursor: row-resize;\n z-index: 5;\n}\n.eg-fill-handle[data-v-5b531cb4] {\n position: absolute;\n width: 6px;\n height: 6px;\n background: #1a73e8;\n border: 1px solid white;\n cursor: crosshair;\n z-index: 10;\n}\n.eg-context-menu[data-v-5b531cb4] {\n position: fixed;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n z-index: 9999;\n min-width: 160px;\n font-size: 13px;\n padding: 4px 0;\n}\n.eg-context-item[data-v-5b531cb4] {\n padding: 6px 16px;\n cursor: pointer;\n position: relative;\n}\n.eg-context-item[data-v-5b531cb4]:hover { background: #f0f0f0;\n}\n.eg-context-separator[data-v-5b531cb4] {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n.eg-context-submenu-parent[data-v-5b531cb4]::after { content: ' ▶'; float: right;\n}\n.eg-context-submenu[data-v-5b531cb4] {\n display: none;\n position: absolute;\n left: 100%;\n top: -4px;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n min-width: 160px;\n padding: 4px 0;\n z-index: 10000;\n}\n.eg-context-submenu-parent:hover .eg-context-submenu[data-v-5b531cb4] { display: block;\n}\n.eg-group-btn[data-v-5b531cb4] {\n position: absolute;\n width: 16px;\n height: 16px;\n line-height: 14px;\n text-align: center;\n font-size: 10px;\n cursor: pointer;\n background: #e8eaf6;\n border: 1px solid #9fa8da;\n border-radius: 2px;\n z-index: 5;\n user-select: none;\n}\n.eg-group-btn.collapsed[data-v-5b531cb4] { background: #c5cae9;\n}\n.eg-drag-ghost[data-v-5b531cb4] {\n position: absolute;\n border: 2px dashed #1a73e8;\n pointer-events: none;\n z-index: 20;\n}\n.eg-resize-line[data-v-5b531cb4] {\n pointer-events: none;\n z-index: 9999;\n}\n\n/* Pin left separator shadow */\n.eg-frozen-col-body[data-v-5b531cb4]::after {\n content: '';\n position: absolute;\n top: 0;\n right: -4px;\n width: 4px;\n height: 100%;\n background: linear-gradient(to right, rgba(0,0,0,0.1), transparent);\n pointer-events: none;\n}\n.eg-pinned-right-body[data-v-5b531cb4] {\n position: sticky;\n right: 0;\n z-index: 10;\n background: white;\n flex-shrink: 0;\n}\n.eg-pinned-right-body[data-v-5b531cb4]::before {\n content: '';\n position: absolute;\n top: 0;\n left: -4px;\n bottom: 0;\n width: 4px;\n background: linear-gradient(to left, rgba(0,0,0,0.10), transparent);\n pointer-events: none;\n}\n.eg-pinned-right-header[data-v-5b531cb4] {\n overflow: hidden;\n flex-shrink: 0;\n border-left: 2px solid #bbb;\n background: #f0f0f0;\n}\n.eg-import-overlay[data-v-5b531cb4] {\n position: absolute;\n inset: 0;\n background: rgba(255, 255, 255, 0.85);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n border-radius: 4px;\n}\n.eg-import-dialog[data-v-5b531cb4] {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 24px 32px;\n min-width: 280px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.15);\n text-align: center;\n}\n.eg-import-title[data-v-5b531cb4] {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n margin-bottom: 16px;\n}\n.eg-import-bar-wrap[data-v-5b531cb4] {\n height: 8px;\n background: #e0e0e0;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 10px;\n}\n.eg-import-bar[data-v-5b531cb4] {\n height: 100%;\n background: linear-gradient(to right, #1a73e8, #4caf50);\n border-radius: 4px;\n transition: width 0.15s ease;\n}\n.eg-import-msg[data-v-5b531cb4] {\n font-size: 12px;\n color: #666;\n margin-bottom: 4px;\n}\n.eg-import-pct[data-v-5b531cb4] {\n font-size: 13px;\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-export-spinner[data-v-5b531cb4] {\n width: 32px;\n height: 32px;\n margin: 12px auto 0;\n border: 3px solid #e0e0e0;\n border-top-color: #1a73e8;\n border-radius: 50%;\n animation: eg-spin-data-v-5b531cb4 0.8s linear infinite;\n}\n@keyframes eg-spin-data-v-5b531cb4 {\nto { transform: rotate(360deg);\n}\n}\n\n/* ===== READONLY CELL ===== */\n.eg-cell-readonly[data-v-5b531cb4] {\n background: #f8f8f8;\n cursor: default;\n}\n.eg-cell-readonly.eg-cell-selected[data-v-5b531cb4] {\n background: #dce8f5;\n}\n.eg-cell-readonly.eg-cell-active[data-v-5b531cb4] {\n outline-color: #aaa;\n}\n\n/* ===== SELECT DROPDOWN ===== */\n.eg-cell-select[data-v-5b531cb4] {\n cursor: pointer;\n}\n.eg-cell-select[data-v-5b531cb4]::after {\n content: '▾';\n position: absolute;\n right: 4px;\n top: 50%;\n transform: translateY(-50%);\n color: #aaa;\n font-size: 11px;\n pointer-events: none;\n line-height: 1;\n}\n\n/* ===== CHECKBOX CELL ===== */\n.eg-cell-checkbox[data-v-5b531cb4] {\n justify-content: center;\n padding: 0 2px;\n}\n.eg-validation-tooltip[data-v-5b531cb4] {\n background: #fff0f0;\n border: 1px solid #e53935;\n color: #c62828;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n white-space: nowrap;\n pointer-events: none;\n}\n\n/* ===== ROW TRANSITION ===== */\n.eg-row-enter-active[data-v-5b531cb4],\n.eg-row-leave-active[data-v-5b531cb4] {\n transition: opacity 0.18s ease, transform 0.18s ease;\n}\n.eg-row-enter[data-v-5b531cb4],\n.eg-row-leave-to[data-v-5b531cb4] {\n opacity: 0;\n transform: translateY(-4px);\n}\n\n/* ===== SIZE VARIANTS ===== */\n\n/* small */\n.eg-size-small .eg-cell[data-v-5b531cb4] {\n font-size: 12px;\n padding: 0 3px;\n}\n.eg-size-small .eg-cell-input[data-v-5b531cb4] {\n font-size: 12px;\n}\n.eg-size-small .eg-header-cell[data-v-5b531cb4] {\n font-size: 11px;\n}\n.eg-size-small .eg-footer-cell[data-v-5b531cb4] {\n font-size: 12px;\n}\n.eg-size-small .eg-row-header-cell[data-v-5b531cb4] {\n font-size: 10px;\n}\n.eg-size-small .eg-select-option[data-v-5b531cb4] {\n font-size: 12px;\n padding: 4px 10px;\n}\n.eg-size-small .eg-context-menu[data-v-5b531cb4],\n.eg-size-small .eg-context-item[data-v-5b531cb4] {\n font-size: 12px;\n}\n.eg-size-small .eg-context-item[data-v-5b531cb4] {\n padding: 4px 14px;\n}\n\n/* large */\n.eg-size-large .eg-cell[data-v-5b531cb4] {\n font-size: 14px;\n padding: 0 6px;\n}\n.eg-size-large .eg-cell-input[data-v-5b531cb4] {\n font-size: 14px;\n}\n.eg-size-large .eg-header-cell[data-v-5b531cb4] {\n font-size: 13px;\n}\n.eg-size-large .eg-footer-cell[data-v-5b531cb4] {\n font-size: 14px;\n}\n.eg-size-large .eg-row-header-cell[data-v-5b531cb4] {\n font-size: 12px;\n}\n.eg-size-large .eg-select-option[data-v-5b531cb4] {\n font-size: 14px;\n padding: 8px 14px;\n}\n.eg-size-large .eg-context-menu[data-v-5b531cb4],\n.eg-size-large .eg-context-item[data-v-5b531cb4] {\n font-size: 14px;\n}\n.eg-size-large .eg-context-item[data-v-5b531cb4] {\n padding: 8px 18px;\n}\n",map:{version:3,sources:["/Users/macmini/Projects/Realsoft/vue2-excel-grid/src/components/ExcelGrid.vue"],names:[],mappings:";AAwzGA;EACA,aAAA;EACA,sBAAA;EACA,gBAAA;EACA,sBAAA;EACA,sBAAA;EACA,kBAAA;EACA,6EAAA;AACA;AAEA;EACA,aAAA;EACA,sBAAA;EACA,OAAA;EACA,cAAA;EACA,kBAAA;AACA;AAEA;EACA,aAAA;EACA,cAAA;EACA,6BAAA;EACA,kBAAA;EACA,WAAA;AACA;AAEA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,sBAAA;EACA,4BAAA;EACA,6BAAA;EACA,mBAAA;EACA,eAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;EACA,iBAAA;EACA,eAAA;AACA;AAEA,yCAAA,mBAAA;AAAA;AACA,oCAAA,mBAAA;AAAA;AAEA;EACA,kBAAA;EACA,QAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,kBAAA;EACA,UAAA;AACA;AAEA;EACA,aAAA;EACA,OAAA;EACA,gBAAA;AACA;;AAEA,4CAAA;AACA;EACA,0BAAA;AACA;AAEA;;;EAGA,kBAAA;AACA;AAEA;EACA,sBAAA;EACA,+BAAA;EACA,gBAAA;EACA,mBAAA;EACA,eAAA;EACA,gBAAA;EACA,cAAA;EACA,kBAAA;EACA,MAAA;EACA,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;AACA;AAEA;EACA,kBAAA;AACA;AAEA;EACA,sBAAA;EACA,+BAAA;EACA,gCAAA;EACA,gBAAA;EACA,mBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,MAAA;EACA,iBAAA;EACA,eAAA;EACA,aAAA;EACA,mBAAA;AACA;AAEA;EACA,qCAAA;EACA,oBAAA;EACA,UAAA;AACA;AAEA;EACA,8BAAA;AACA;AAEA;EACA,8BAAA;EACA,2BAAA;EACA,oBAAA;AACA;AAEA;EACA,2BAAA;EACA,oBAAA;AACA;AAEA;EACA,wBAAA;AACA;AAEA;EACA,qCAAA;EACA,oBAAA;EACA,UAAA;AACA;AAEA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;EACA,eAAA;EACA,oBAAA;EACA,UAAA;AACA;AAEA;EACA,sBAAA;EACA,mBAAA;EACA,gCAAA;EACA,4BAAA;EACA,kBAAA;EACA,eAAA;EACA,WAAA;EACA,eAAA;EACA,iBAAA;EACA,kBAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;AACA;AAEA;EACA,kBAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,WAAA;EACA,kBAAA;EACA,UAAA;AACA;AAEA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,mBAAA;EACA,uBAAA;EACA,iBAAA;EACA,WAAA;AACA;AAEA;EACA,eAAA;EACA,iBAAA;EACA,sBAAA;EACA,kBAAA;EACA,yCAAA;EACA,aAAA;EACA,gBAAA;EACA,eAAA;EACA,cAAA;AACA;AAEA;EACA,iBAAA;EACA,eAAA;EACA,kBAAA;AACA;AAEA,0CAAA,mBAAA;AAAA;AAEA;EACA,WAAA;EACA,mBAAA;EACA,aAAA;AACA;AAEA,qDAAA,aAAA,EAAA,YAAA;AAAA;AAEA;EACA,aAAA;EACA,kBAAA;EACA,UAAA;EACA,SAAA;EACA,iBAAA;EACA,sBAAA;EACA,kBAAA;EACA,yCAAA;EACA,gBAAA;EACA,cAAA;EACA,cAAA;AACA;AAEA,wEAAA,cAAA;AAAA;AAEA;EACA,kBAAA;EACA,WAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EACA,UAAA;EACA,iBAAA;AACA;AAEA,2CAAA,mBAAA;AAAA;AAEA;EACA,kBAAA;EACA,0BAAA;EACA,oBAAA;EACA,WAAA;AACA;AAEA;EACA,oBAAA;EACA,aAAA;AACA;;AAEA,8BAAA;AACA;EACA,WAAA;EACA,kBAAA;EACA,MAAA;EACA,WAAA;EACA,UAAA;EACA,YAAA;EACA,mEAAA;EACA,oBAAA;AACA;AAEA;EACA,gBAAA;EACA,QAAA;EACA,WAAA;EACA,iBAAA;EACA,cAAA;AACA;AAEA;EACA,WAAA;EACA,kBAAA;EACA,MAAA;EACA,UAAA;EACA,SAAA;EACA,UAAA;EACA,mEAAA;EACA,oBAAA;AACA;AAEA;EACA,gBAAA;EACA,cAAA;EACA,2BAAA;EACA,mBAAA;AACA;AAEA;EACA,kBAAA;EACA,QAAA;EACA,qCAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,YAAA;EACA,kBAAA;AACA;AAEA;EACA,iBAAA;EACA,yBAAA;EACA,kBAAA;EACA,kBAAA;EACA,gBAAA;EACA,uCAAA;EACA,kBAAA;AACA;AAEA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,mBAAA;AACA;AAEA;EACA,WAAA;EACA,mBAAA;EACA,kBAAA;EACA,gBAAA;EACA,mBAAA;AACA;AAEA;EACA,YAAA;EACA,uDAAA;EACA,kBAAA;EACA,4BAAA;AACA;AAEA;EACA,eAAA;EACA,WAAA;EACA,kBAAA;AACA;AAEA;EACA,eAAA;EACA,gBAAA;EACA,cAAA;AACA;AACA;EACA,WAAA;EACA,YAAA;EACA,mBAAA;EACA,yBAAA;EACA,yBAAA;EACA,kBAAA;EACA,uDAAA;AACA;AACA;AACA,KAAA,yBAAA;AAAA;AACA;;AAEA,8BAAA;AACA;EACA,mBAAA;EACA,eAAA;AACA;AACA;EACA,mBAAA;AACA;AACA;EACA,mBAAA;AACA;;AAEA,gCAAA;AACA;EACA,eAAA;AACA;AACA;EACA,YAAA;EACA,kBAAA;EACA,UAAA;EACA,QAAA;EACA,2BAAA;EACA,WAAA;EACA,eAAA;EACA,oBAAA;EACA,cAAA;AACA;;AAEA,8BAAA;AACA;EACA,uBAAA;EACA,cAAA;AACA;AACA;EACA,mBAAA;EACA,yBAAA;EACA,cAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;EACA,yCAAA;EACA,mBAAA;EACA,oBAAA;AACA;;AAEA,+BAAA;AACA;;EAEA,oDAAA;AACA;AACA;;EAEA,UAAA;EACA,2BAAA;AACA;;AAEA,8BAAA;;AAEA,UAAA;AACA;EACA,eAAA;EACA,cAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;EACA,iBAAA;AACA;AACA;;EAEA,eAAA;AACA;AACA;EACA,iBAAA;AACA;;AAEA,UAAA;AACA;EACA,eAAA;EACA,cAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,eAAA;EACA,iBAAA;AACA;AACA;;EAEA,eAAA;AACA;AACA;EACA,iBAAA;AACA",file:"ExcelGrid.vue",sourcesContent:["<template>\n <div\n class=\"eg-wrapper\"\n :class=\"'eg-size-' + size\"\n :style=\"wrapperStyle\"\n tabindex=\"0\"\n @keydown=\"onKeyDown\"\n @keypress=\"onKeyPress\"\n ref=\"wrapper\"\n >\n \x3c!-- Context menu --\x3e\n <div\n v-if=\"contextMenu.visible\"\n class=\"eg-context-menu\"\n :style=\"{ top: contextMenu.y + 'px', left: contextMenu.x + 'px' }\"\n @click.stop\n >\n <div class=\"eg-context-item\" @click=\"ctxCopy\">{{ t.copy }}</div>\n <div class=\"eg-context-item\" @click=\"ctxCut\">{{ t.cut }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPaste\">{{ t.paste }}</div>\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item eg-context-submenu-parent\">\n {{ t.pasteSpecial }}\n <div class=\"eg-context-submenu\">\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('all')\">{{ t.pasteAll }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('values')\">{{ t.pasteValues }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('formats')\">{{ t.pasteFormats }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPasteSpecial('all', true)\">{{ t.transpose }}</div>\n </div>\n </div>\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item\" @click=\"ctxMerge\">{{ t.mergeCells }}</div>\n <div class=\"eg-context-item\" @click=\"ctxUnmerge\">{{ t.unmergeCells }}</div>\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item\" @click=\"ctxHideRow\">{{ t.hideRows }}</div>\n <div class=\"eg-context-item\" @click=\"ctxHideColumn\">{{ t.hideColumns }}</div>\n <div class=\"eg-context-item\" @click=\"ctxUnhideRows\">{{ t.unhideRows }}</div>\n <div class=\"eg-context-item\" @click=\"ctxUnhideColumns\">{{ t.unhideColumns }}</div>\n <template v-if=\"contextMenu.row < 0 && contextMenu.col >= 0\">\n <div class=\"eg-context-separator\"></div>\n <div class=\"eg-context-item\" @click=\"ctxPinLeft\">{{ t.pinLeft }}</div>\n <div class=\"eg-context-item\" @click=\"ctxPinRight\">{{ t.pinRight }}</div>\n <div class=\"eg-context-item\" v-if=\"isColPinned(contextMenu.col)\" @click=\"ctxUnpin\">{{ t.unpin }}</div>\n </template>\n </div>\n\n \x3c!-- Scroll container --\x3e\n <div class=\"eg-container\" ref=\"container\" @scroll=\"onScroll\" @click=\"hideContextMenu\">\n\n \x3c!-- ===== HEADER AREA ===== --\x3e\n <div class=\"eg-header-area\" :style=\"headerAreaStyle\">\n\n \x3c!-- Corner cell --\x3e\n <div\n class=\"eg-corner\"\n :style=\"cornerStyle\"\n @click=\"selectAll\"\n ></div>\n\n \x3c!-- Row group outline gutter (left) --\x3e\n <div v-if=\"rowGroups.length\" class=\"eg-row-outline-gutter\" :style=\"rowOutlineGutterStyle\"></div>\n\n \x3c!-- Frozen columns header --\x3e\n <div v-if=\"getFrozenWidth() > 0\" class=\"eg-frozen-col-header\" :style=\"frozenColHeaderStyle\">\n <div :style=\"{ position: 'relative', height: (headerRowHeight * headerDepth) + 'px' }\">\n <template v-for=\"(row, rowIdx) in headerRows\">\n <template v-for=\"cell in row\">\n <template v-if=\"isCellVisible(cell, rowIdx, true)\">\n <div\n :key=\"'fh-' + rowIdx + '-' + (cell.column.key || cell.column.title)\"\n class=\"eg-header-cell\"\n :class=\"{ 'eg-header-group': cell.isGroup }\"\n :style=\"getHeaderCellStyle(cell, rowIdx, true)\"\n @mousedown=\"onColHeaderMouseDown($event, cell)\"\n @dblclick=\"onColHeaderDblClick($event, cell)\"\n @contextmenu.prevent=\"onColHeaderContextMenu($event, cell)\"\n >\n <slot v-if=\"!cell.isGroup\" :name=\"'header-' + cell.column.key\" :column=\"cell.column\">\n {{ cell.column.title }}\n </slot>\n <span v-else>{{ cell.column.title }}</span>\n <div\n v-if=\"!cell.isGroup\"\n class=\"eg-col-resize-handle\"\n @mousedown.stop=\"startColResize($event, cell.column._flatIndex)\"\n ></div>\n </div>\n </template>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Scrollable columns header --\x3e\n <div class=\"eg-scroll-col-header\" ref=\"scrollColHeader\" :style=\"scrollColHeaderStyle\">\n <div class=\"eg-scroll-col-header-inner\" :style=\"{ width: scrollContentWidth + 'px', height: (headerRowHeight * headerDepth) + 'px', position: 'relative' }\">\n <template v-for=\"(row, rowIdx) in headerRows\">\n <template v-for=\"cell in row\">\n <template v-if=\"isCellVisible(cell, rowIdx, false)\">\n <div\n :key=\"'sh-' + rowIdx + '-' + (cell.column.key || cell.column.title)\"\n class=\"eg-header-cell\"\n :class=\"{ 'eg-header-group': cell.isGroup }\"\n :style=\"getHeaderCellStyle(cell, rowIdx, false)\"\n @mousedown=\"onColHeaderMouseDown($event, cell)\"\n @dblclick=\"onColHeaderDblClick($event, cell)\"\n @contextmenu.prevent=\"onColHeaderContextMenu($event, cell)\"\n >\n <slot v-if=\"!cell.isGroup\" :name=\"'header-' + cell.column.key\" :column=\"cell.column\">\n {{ cell.column.title }}\n </slot>\n <span v-else>{{ cell.column.title }}</span>\n <div\n v-if=\"!cell.isGroup\"\n class=\"eg-col-resize-handle\"\n @mousedown.stop=\"startColResize($event, cell.column._flatIndex)\"\n ></div>\n </div>\n </template>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Pinned Right columns header --\x3e\n <div v-if=\"hasPinnedRight\" class=\"eg-pinned-right-header\" :style=\"pinnedRightHeaderStyle\">\n <div :style=\"{ position: 'relative', height: (headerRowHeight * headerDepth) + 'px' }\">\n <template v-for=\"(row, rowIdx) in headerRows\">\n <template v-for=\"cell in row\">\n <template v-if=\"isCellInZone(cell, rowIdx, 'right')\">\n <div\n :key=\"'prh-' + rowIdx + '-' + (cell.column.key || cell.column.title)\"\n class=\"eg-header-cell\"\n :class=\"{ 'eg-header-group': cell.isGroup }\"\n :style=\"getHeaderCellStyleForZone(cell, rowIdx, 'right')\"\n @mousedown=\"onColHeaderMouseDown($event, cell)\"\n @dblclick=\"onColHeaderDblClick($event, cell)\"\n @contextmenu.prevent=\"onColHeaderContextMenu($event, cell)\"\n >\n <slot v-if=\"!cell.isGroup\" :name=\"'header-' + cell.column.key\" :column=\"cell.column\">\n {{ cell.column.title }}\n </slot>\n <span v-else>{{ cell.column.title }}</span>\n <div\n v-if=\"!cell.isGroup\"\n class=\"eg-col-resize-handle\"\n @mousedown.stop=\"startColResize($event, cell.column._flatIndex)\"\n ></div>\n </div>\n </template>\n </template>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- ===== BODY AREA ===== --\x3e\n <div class=\"eg-body-area\" :style=\"bodyAreaStyle\">\n\n \x3c!-- Row number header column --\x3e\n <div class=\"eg-row-header-col\" :style=\"rowHeaderColStyle\">\n \x3c!-- Scrollable row headers --\x3e\n <div class=\"eg-scroll-row-headers\" :style=\"scrollRowHeadersStyle\" ref=\"scrollRowHeaders\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <div\n v-if=\"!isRowHidden(ri)\"\n :key=\"'srh-' + ri\"\n class=\"eg-row-header-cell\"\n :style=\"getRowHeaderStyle(ri)\"\n @mousedown=\"onRowHeaderMouseDown($event, ri)\"\n @contextmenu.prevent=\"onRowContextMenu($event, ri)\"\n >\n {{ ri + 1 }}\n <div class=\"eg-row-resize-handle\" @mousedown.stop=\"startRowResize($event, ri)\"></div>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Row group outline gutter (left side of rows) --\x3e\n <div v-if=\"rowGroups.length\" class=\"eg-row-outline-col\" :style=\"rowOutlineColStyle\">\n <template v-for=\"group in rowGroups\">\n <div\n :key=\"'rg-' + group.startIndex + '-' + group.endIndex\"\n class=\"eg-group-btn\"\n :class=\"{ collapsed: isGroupCollapsed(group) }\"\n :style=\"getRowGroupBtnStyle(group)\"\n @click=\"toggleGroup(group)\"\n >{{ isGroupCollapsed(group) ? '+' : '−' }}</div>\n </template>\n </div>\n\n \x3c!-- Main scroll body --\x3e\n <div class=\"eg-scroll-body\" ref=\"scrollBody\" :style=\"scrollBodyStyle\" @scroll=\"onBodyScroll\">\n <div class=\"eg-scroll-body-inner\" :style=\"{ width: scrollContentWidth + 'px', height: totalVirtualHeight + 'px', position: 'relative' }\">\n\n \x3c!-- Pinned left column body --\x3e\n <div v-if=\"getFrozenWidth() > 0\" class=\"eg-frozen-col-body\" :style=\"frozenColBodyStyle\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <transition :key=\"'tr-fc-' + ri\" :name=\"_rowTransitionName\">\n <div v-if=\"!isRowHidden(ri)\" :key=\"'fc-row-' + ri\" class=\"eg-row\" :style=\"getRowStyle(ri)\">\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnPinnedLeft(ci) && !isColHidden(ci)\">\n <div\n :key=\"'fc-cell-' + ri + '-' + ci\"\n class=\"eg-cell\"\n :class=\"getCellClasses(ri, ci)\"\n :style=\"getCellStyle(ri, ci)\"\n @mousedown=\"onCellMouseDown($event, ri, ci)\"\n @mouseover=\"onCellMouseOver($event, ri, ci)\"\n @mouseup=\"onCellMouseUp\"\n @dblclick=\"onCellDblClick(ri, ci)\"\n @contextmenu.prevent=\"onCellContextMenu($event, ri, ci)\"\n >\n <div v-if=\"isMergedAway(ri, ci)\" style=\"display:none\"></div>\n <template v-else-if=\"isEditing(ri, ci)\">\n <slot v-if=\"hasCellSlot(col)\" :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :isEditing=\"true\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <slot v-else-if=\"hasLegacyEditSlot(col)\" :name=\"'edit-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <input v-else ref=\"editInput\" class=\"eg-cell-input\" v-model=\"editValue\" @blur=\"commitEdit\" @keydown.stop=\"onEditKeyDown\" />\n </template>\n <template v-else-if=\"col.type === 'checkbox'\">\n <eg-checkbox\n :value=\"isCheckboxChecked(ri, ci)\"\n :disabled=\"!isCellEditable(ri, ci)\"\n @change=\"(v) => setCheckboxValue(ri, ci, v)\"\n />\n </template>\n <template v-else>\n <slot :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :isEditing=\"false\" :done=\"null\" :cancel=\"null\">\n {{ isSelectLikeCol(col) ? getSelectLabel(ci, getCellValue(ri, ci)) : getCellValue(ri, ci) }}\n </slot>\n </template>\n </div>\n </template>\n </template>\n </div>\n </transition>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- Main data cells --\x3e\n <div class=\"eg-main-cells\" :style=\"mainCellsStyle\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative', width: scrollableContentWidth + 'px' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <transition :key=\"'tr-m-' + ri\" :name=\"_rowTransitionName\">\n <div\n v-if=\"!isRowHidden(ri)\"\n :key=\"'row-' + ri\"\n class=\"eg-row\"\n :style=\"getRowStyle(ri)\"\n :data-row=\"ri\"\n >\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnScrollable(ci) && !isColHidden(ci)\">\n <div\n :key=\"'cell-' + ri + '-' + ci\"\n class=\"eg-cell\"\n :class=\"getCellClasses(ri, ci)\"\n :style=\"getCellStyle(ri, ci)\"\n @mousedown=\"onCellMouseDown($event, ri, ci)\"\n @mouseover=\"onCellMouseOver($event, ri, ci)\"\n @mouseup=\"onCellMouseUp\"\n @dblclick=\"onCellDblClick(ri, ci)\"\n @contextmenu.prevent=\"onCellContextMenu($event, ri, ci)\"\n >\n <div v-if=\"isMergedAway(ri, ci)\" style=\"display:none\"></div>\n <template v-else-if=\"isEditing(ri, ci)\">\n <slot v-if=\"hasCellSlot(col)\" :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :isEditing=\"true\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <slot v-else-if=\"hasLegacyEditSlot(col)\" :name=\"'edit-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <input\n v-else\n ref=\"editInput\"\n class=\"eg-cell-input\"\n v-model=\"editValue\"\n @blur=\"commitEdit\"\n @keydown.stop=\"onEditKeyDown\"\n />\n </template>\n <template v-else-if=\"col.type === 'checkbox'\">\n <eg-checkbox\n :value=\"isCheckboxChecked(ri, ci)\"\n :disabled=\"!isCellEditable(ri, ci)\"\n @change=\"(v) => setCheckboxValue(ri, ci, v)\"\n />\n </template>\n <template v-else>\n <slot :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, ci)\" :isEditing=\"false\" :done=\"null\" :cancel=\"null\">\n {{ isSelectLikeCol(col) ? getSelectLabel(ci, getCellValue(ri, ci)) : getCellValue(ri, ci) }}\n </slot>\n </template>\n </div>\n </template>\n </template>\n </div>\n </transition>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- Pinned Right column body --\x3e\n <div v-if=\"hasPinnedRight\" class=\"eg-pinned-right-body\" :style=\"pinnedRightBodyStyle\">\n <div :style=\"{ height: totalVirtualHeight + 'px', position: 'relative' }\">\n <div :style=\"{ transform: 'translateY(' + virtualOffsetTop + 'px)' }\">\n <template v-for=\"ri in visibleRowRange\">\n <transition :key=\"'tr-pr-' + ri\" :name=\"_rowTransitionName\">\n <div v-if=\"!isRowHidden(ri)\" :key=\"'pr-row-' + ri\" class=\"eg-row\" :style=\"getRowStyle(ri)\">\n <template v-for=\"col in pinnedRightCols\">\n <div\n :key=\"'pr-cell-' + ri + '-' + col._flatIndex\"\n class=\"eg-cell\"\n :class=\"getCellClasses(ri, col._flatIndex)\"\n :style=\"getCellStyle(ri, col._flatIndex)\"\n @mousedown=\"onCellMouseDown($event, ri, col._flatIndex)\"\n @mouseover=\"onCellMouseOver($event, ri, col._flatIndex)\"\n @mouseup=\"onCellMouseUp\"\n @dblclick=\"onCellDblClick(ri, col._flatIndex)\"\n @contextmenu.prevent=\"onCellContextMenu($event, ri, col._flatIndex)\"\n >\n <div v-if=\"isMergedAway(ri, col._flatIndex)\" style=\"display:none\"></div>\n <template v-else-if=\"isEditing(ri, col._flatIndex)\">\n <slot v-if=\"hasCellSlot(col)\" :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, col._flatIndex)\" :isEditing=\"true\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <slot v-else-if=\"hasLegacyEditSlot(col)\" :name=\"'edit-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, col._flatIndex)\" :done=\"(v) => commitEditSlot(v)\" :cancel=\"cancelEdit\" />\n <input v-else ref=\"editInput\" class=\"eg-cell-input\" v-model=\"editValue\" @blur=\"commitEdit\" @keydown.stop=\"onEditKeyDown\" />\n </template>\n <template v-else-if=\"col.type === 'checkbox'\">\n <eg-checkbox\n :value=\"isCheckboxChecked(ri, col._flatIndex)\"\n :disabled=\"!isCellEditable(ri, col._flatIndex)\"\n @change=\"(v) => setCheckboxValue(ri, col._flatIndex, v)\"\n />\n </template>\n <template v-else>\n <slot :name=\"'cell-' + col.key\" :row=\"_dataRaw[ri]\" :rowIndex=\"ri\" :column=\"col\" :value=\"getCellValue(ri, col._flatIndex)\" :isEditing=\"false\" :done=\"null\" :cancel=\"null\">\n {{ isSelectLikeCol(col) ? getSelectLabel(col._flatIndex, getCellValue(ri, col._flatIndex)) : getCellValue(ri, col._flatIndex) }}\n </slot>\n </template>\n </div>\n </template>\n </div>\n </transition>\n </template>\n </div>\n </div>\n </div>\n\n \x3c!-- Fill handle (shown on primary range) --\x3e\n <div\n v-if=\"fillHandleVisible\"\n class=\"eg-fill-handle\"\n :style=\"fillHandleStyle\"\n @mousedown.stop=\"startFillDrag\"\n ></div>\n\n \x3c!-- Drag move ghost border --\x3e\n <div\n v-if=\"dragMove.active\"\n class=\"eg-drag-ghost\"\n :style=\"dragGhostStyle\"\n ></div>\n </div>\n </div>\n\n \x3c!-- Column group outline row (top, above body) --\x3e\n <div v-if=\"colGroups.length\" class=\"eg-col-outline-row\" :style=\"colOutlineRowStyle\">\n <template v-for=\"group in colGroups\">\n <div\n :key=\"'cg-' + group.startIndex + '-' + group.endIndex\"\n class=\"eg-group-btn\"\n :class=\"{ collapsed: isGroupCollapsed(group) }\"\n :style=\"getColGroupBtnStyle(group)\"\n @click=\"toggleGroup(group)\"\n >{{ isGroupCollapsed(group) ? '+' : '−' }}</div>\n </template>\n </div>\n\n </div>\x3c!-- end body-area --\x3e\n\n \x3c!-- ===== FOOTER AREA (column totals) ===== --\x3e\n <div v-if=\"hasFooter\" class=\"eg-footer-area\" :style=\"footerAreaStyle\">\n\n \x3c!-- \"Jami\" label cell (mirrors corner cell) --\x3e\n <div class=\"eg-footer-label\" :style=\"footerLabelStyle\">{{ t.total }}</div>\n\n \x3c!-- Frozen columns footer --\x3e\n <div v-if=\"getFrozenWidth() > 0\" class=\"eg-frozen-col-footer\" :style=\"frozenColFooterStyle\">\n <div :style=\"{ position: 'relative', height: footerRowHeight + 'px' }\">\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnPinnedLeft(ci) && !isColHidden(ci)\">\n <div :key=\"'ff-' + ci\" class=\"eg-footer-cell\" :style=\"getFooterCellStyle(ci)\">\n {{ getColumnTotalDisplay(ci) }}\n </div>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Scrollable footer (horizontally synced with body via scrollFooter ref) --\x3e\n <div class=\"eg-scroll-col-footer\" ref=\"scrollFooter\" :style=\"scrollColHeaderStyle\">\n <div class=\"eg-scroll-col-footer-inner\" :style=\"{ width: scrollContentWidth + 'px', height: footerRowHeight + 'px', position: 'relative' }\">\n <template v-for=\"(col, ci) in leafCols\">\n <template v-if=\"isColumnScrollable(ci) && !isColHidden(ci)\">\n <div :key=\"'sf-' + ci\" class=\"eg-footer-cell\" :style=\"getFooterCellStyle(ci)\">\n {{ getColumnTotalDisplay(ci) }}\n </div>\n </template>\n </template>\n </div>\n </div>\n\n \x3c!-- Pinned Right columns footer --\x3e\n <div v-if=\"hasPinnedRight\" class=\"eg-pinned-right-footer\" :style=\"pinnedRightFooterStyle\">\n <div :style=\"{ position: 'relative', height: footerRowHeight + 'px' }\">\n <div\n v-for=\"col in pinnedRightCols\"\n :key=\"'prf-' + col._flatIndex\"\n class=\"eg-footer-cell\"\n :style=\"getFooterCellStyle(col._flatIndex)\"\n >{{ getColumnTotalDisplay(col._flatIndex) }}</div>\n </div>\n </div>\n </div>\x3c!-- end footer-area --\x3e\n\n \x3c!-- Resize indicator line --\x3e\n <div v-if=\"resizing.active\" class=\"eg-resize-line\" :style=\"resizeLineStyle\"></div>\n </div>\x3c!-- end container --\x3e\n\n \x3c!-- Import progress overlay --\x3e\n <div v-if=\"importState.active\" class=\"eg-import-overlay\">\n <div class=\"eg-import-dialog\">\n <div class=\"eg-import-title\">Fayl import qilinmoqda</div>\n <div class=\"eg-import-bar-wrap\">\n <div class=\"eg-import-bar\" :style=\"{ width: importState.percent + '%' }\"></div>\n </div>\n <div class=\"eg-import-msg\">{{ importState.message }}</div>\n <div class=\"eg-import-pct\">{{ importState.percent }}%</div>\n </div>\n </div>\n\n \x3c!-- Export progress overlay --\x3e\n <div v-if=\"exportState.active\" class=\"eg-import-overlay\">\n <div class=\"eg-import-dialog\">\n <div class=\"eg-import-title\">Fayl export qilinmoqda...</div>\n <div class=\"eg-export-spinner\"></div>\n </div>\n </div>\n\n \x3c!-- Select dropdown overlay (flat select) --\x3e\n <eg-select\n v-if=\"selectDropdown && !selectDropdown.tree\"\n ref=\"selectDropdownComp\"\n :value=\"selectDropdown.currentValue\"\n :options=\"selectDropdown.rawOptions\"\n :enable-search=\"selectDropdown.enableSearch\"\n :trigger-rect=\"selectDropdown.rect\"\n :dropdown-only=\"true\"\n @select=\"onComponentSelect\"\n @close=\"closeSelectDropdown\"\n />\n \x3c!-- Tree select dropdown overlay --\x3e\n <eg-tree-select\n v-if=\"selectDropdown && selectDropdown.tree\"\n ref=\"selectDropdownComp\"\n :value=\"selectDropdown.currentValue\"\n :options=\"selectDropdown.rawOptions\"\n :value-key=\"selectDropdown.keys.valueKey\"\n :label-key=\"selectDropdown.keys.labelKey\"\n :children-key=\"selectDropdown.keys.childrenKey\"\n :enable-search=\"selectDropdown.enableSearch\"\n :trigger-rect=\"selectDropdown.rect\"\n :dropdown-only=\"true\"\n @select=\"onComponentSelect\"\n @close=\"closeSelectDropdown\"\n />\n\n \x3c!-- Validation error tooltip (cellValidator) --\x3e\n <div\n v-if=\"invalidCell && invalidCell.rect\"\n class=\"eg-validation-tooltip\"\n :style=\"invalidTooltipStyle\"\n >{{ invalidCell.message }}</div>\n </div>\n</template>\n\n<script>\nimport {\n normalizeRange, isCellInRange, isCellInAnyRange,\n buildRange, singleCellRange, getPrimaryRange\n} from '../utils/selection.js'\nimport { flattenColumns, buildHeaderRows, columnTreeDepth } from '../utils/headerTree.js'\nimport { calcVisibleRows, buildCumulativeHeights, findVisibleRange, getRowTop, getRowHeight } from '../utils/virtualScroll.js'\nimport { generateFillValues } from '../utils/fillHandle.js'\nimport {\n matrixToTSV, tsvToMatrix, extractMatrix, transposeMatrix, applyMatrix, tileMatrix\n} from '../utils/clipboard.js'\nimport {\n mergeHasConflict, findMergeAtCell, isMergeAnchor, addMerge,\n removeMergeAt, resolveMergeAnchor\n} from '../utils/merge.js'\nimport {\n addGroup, removeGroup, toggleGroupCollapse, isGroupCollapsed as _isGroupCollapsed,\n getCollapsedRowIndices, getCollapsedColIndices\n} from '../utils/outline.js'\nimport { exportToExcel as _exportToExcel } from '../utils/excelExport.js'\nimport { importFromFile as _importFromFile } from '../utils/excelImport.js'\nimport { createExcelImportWorker, createExcelExportWorker } from '../utils/workerFactory'\nimport { getMessages } from '../utils/i18n.js'\nimport EgCheckbox from './EgCheckbox.vue'\nimport EgSelect from './EgSelect.vue'\nimport EgTreeSelect from './EgTreeSelect.vue'\n\nconst SIZE_CONFIG = {\n small: { row: 22, header: 24, footer: 24 },\n middle: { row: 28, header: 28, footer: 28 },\n large: { row: 36, header: 36, footer: 36 }\n}\nconst ROW_HEADER_WIDTH = 50\nconst DEFAULT_COL_WIDTH = 100\nconst OUTLINE_GUTTER = 20\n\nexport default {\n name: 'ExcelGrid',\n\n components: { EgCheckbox, EgSelect, EgTreeSelect },\n\n // v-model → :data + @input\n model: { prop: 'data', event: 'input' },\n\n props: {\n /** Array of row objects keyed by columns[].key */\n data: { type: Array, default: () => [] },\n\n /** Column definitions (may be nested via children[]) */\n columns: { type: Array, default: () => [] },\n\n /** Indices of hidden leaf columns */\n hiddenColumns: { type: Array, default: () => [] },\n\n /** Indices of hidden rows */\n hiddenRows: { type: Array, default: () => [] },\n\n /** Initial merge descriptors [{ startRow, startCol, endRow, endCol }] */\n merges: { type: Array, default: () => [] },\n\n /** Collapsed group descriptors [{ type, startIndex, endIndex }] */\n collapsedGroups: { type: Array, default: () => [] },\n\n /** Height of the grid container in px */\n height: { type: Number, default: 400 },\n\n /** Width of the grid container (px or 'auto') */\n width: { type: [Number, String], default: '100%' },\n\n /**\n * Called before drag-drop: (sourceRange, targetRange) => bool\n * Return false to cancel the drop.\n */\n beforeDrop: { type: Function, default: null },\n\n /**\n * UI locale for the context menu.\n * Supported: 'en' | 'ru' | 'uz' | 'uz-Cyrl'\n */\n locale: { type: String, default: 'en' },\n\n /**\n * Pinned columns map: { [colKey]: 'left' | 'right' }\n * Pinned-left columns render as a fixed zone on the left.\n * Pinned-right columns render as a fixed zone on the right.\n * Emit 'update:pinnedColumns' to support .sync modifier.\n */\n pinnedColumns: { type: Object, default: () => ({}) },\n\n /**\n * Dynamic cell editability: (row, rowIndex, col, colIndex) => boolean\n * Overrides column-level editable when provided.\n * row = plain data object, col = column definition.\n */\n cellEditable: { type: Function, default: null },\n\n /**\n * Cell value validation: (value, row, rowIndex, col, colIndex) => boolean\n * Return false to reject the write — the cell keeps its old value and\n * an error tooltip is shown anchored to that cell.\n */\n cellValidator: { type: Function, default: null },\n\n /**\n * Error tooltip text for a rejected value.\n * Function: (value, row, rowIndex, col, colIndex) => string\n * String: same message for every rejected cell.\n * Default: locale's \"invalidValue\" message.\n */\n cellValidationMessage: { type: [Function, String], default: null },\n\n /**\n * Bulk-write strictness (paste / fill handle / Delete).\n * false (default): invalid cells in the affected range are skipped,\n * the rest of the operation still applies.\n * true: if ANY cell in the affected range fails validation, the whole\n * bulk operation is rejected — nothing is written.\n */\n strictBulkValidation: { type: Boolean, default: false },\n\n /** Visual size of the grid. Affects row/header height and font sizes. */\n size: { type: String, default: 'middle' },\n\n /** Enable CSS list transitions on row enter/leave (data changes). Disabled during scroll. */\n transitionEnabled: { type: Boolean, default: false },\n\n /** Auto-fit all column widths to content (same as double-clicking each resize handle). Re-applies on data/columns change. */\n autoWidth: { type: Boolean, default: false }\n },\n\n data() {\n return {\n // Selection\n activeCell: { row: 0, col: 0 },\n selectionRanges: [], // [{ startRow, startCol, endRow, endCol }]\n isMouseDown: false,\n mouseDownRange: null,\n isCtrlDrag: false,\n\n // Edit\n editingCell: null, // { row, col }\n editValue: '',\n editOriginalValue: '',\n\n // Scroll\n scrollTop: 0,\n scrollLeft: 0,\n headerViewportWidth: null,\n\n // Virtual scroll\n virtualStartIndex: 0,\n virtualEndIndex: 0,\n virtualOffsetTop: 0,\n totalVirtualHeight: 0,\n\n // Column widths (flat index → px)\n colWidths: {},\n\n // Row heights (row index → px)\n rowHeights: {},\n\n // Import/export progress\n importState: { active: false, percent: 0, message: '' },\n exportState: { active: false },\n\n // Resize state\n _isScrolling: false,\n\n resizing: {\n active: false,\n type: null, // 'col' | 'row'\n index: -1,\n startX: 0,\n startY: 0,\n startSize: 0,\n currentPos: 0\n },\n\n // Fill handle\n fillDrag: {\n active: false,\n sourceRange: null,\n currentRow: -1,\n currentCol: -1\n },\n\n // Drag-move\n dragMove: {\n active: false,\n sourceRange: null,\n startX: 0,\n startY: 0,\n targetRow: -1,\n targetCol: -1,\n copyMode: false\n },\n\n // Clipboard (internal)\n clipboardMatrix: null,\n clipboardRange: null,\n clipboardMode: null, // 'copy' | 'cut'\n\n // Context menu\n contextMenu: { visible: false, x: 0, y: 0, row: -1, col: -1 },\n\n // Select dropdown\n selectDropdown: null, // { row, col, options, currentValue, rect }\n\n // Cell value validation (cellValidator)\n invalidCell: null, // { row, col, message, rect }\n\n // Merges (controlled via prop + update:merges)\n internalMerges: [],\n\n // Groups\n internalGroups: [], // [{ type, startIndex, endIndex }]\n internalCollapsedGroups: [],\n\n // Header\n headerDepth: 1,\n headerRows: [],\n\n // Leaf columns cache\n leafCols: [],\n\n // Footer column totals (flat col index → number | null)\n columnTotals: {}\n }\n },\n\n computed: {\n t() { return getMessages(this.locale) },\n\n pinnedLeftSet() {\n const s = new Set()\n this.leafCols.forEach(col => { if (this.pinnedColumns[col.key] === 'left') s.add(col._flatIndex) })\n return s\n },\n\n pinnedRightSet() {\n const s = new Set()\n this.leafCols.forEach(col => { if (this.pinnedColumns[col.key] === 'right') s.add(col._flatIndex) })\n return s\n },\n\n pinnedLeftCols() { return this.leafCols.filter(col => this.pinnedLeftSet.has(col._flatIndex)) },\n pinnedRightCols() { return this.leafCols.filter(col => this.pinnedRightSet.has(col._flatIndex)) },\n\n hasPinnedLeft() { return this.pinnedLeftSet.size > 0 },\n hasPinnedRight() { return this.pinnedRightSet.size > 0 },\n\n hasFooter() { return this.leafCols.some(col => col.showTotal) },\n\n pinnedLeftWidth() {\n let w = 0\n for (const col of this.pinnedLeftCols) {\n if (!this.isColHidden(col._flatIndex)) w += this.getColW(col._flatIndex)\n }\n return w\n },\n\n pinnedRightWidth() {\n let w = 0\n for (const col of this.pinnedRightCols) {\n if (!this.isColHidden(col._flatIndex)) w += this.getColW(col._flatIndex)\n }\n return w\n },\n\n totalRows() { return this.data.length },\n\n _rowTransitionName() {\n return this.transitionEnabled && !this._isScrolling ? 'eg-row' : ''\n },\n\n _sizeConf() { return SIZE_CONFIG[this.size] || SIZE_CONFIG.middle },\n _rowH() { return this._sizeConf.row },\n _headerRowH() { return this._sizeConf.header },\n _footerRowH() { return this._sizeConf.footer },\n\n headerRowHeight() { return this._headerRowH },\n footerRowHeight() { return this._footerRowH },\n\n // Pre-built cumulative height array — O(n) once, then cached by Vue.\n // findVisibleRange() uses binary search against this cached array → O(log n) per scroll.\n // Invalidated only when row count, custom heights, or hidden rows change.\n cumulativeHeights() {\n return buildCumulativeHeights(this.totalRows, this._rowH, this.rowHeights, this.hiddenRowSet)\n },\n\n hiddenColSet() { return new Set(this.hiddenColumns) },\n\n hiddenRowSet() {\n const s = new Set(this.hiddenRows)\n // Also add rows hidden by collapsed row groups\n const collapsed = getCollapsedRowIndices(this.internalGroups, this.internalCollapsedGroups)\n collapsed.forEach(i => s.add(i))\n return s\n },\n\n hiddenColFromGroupSet() {\n return getCollapsedColIndices(this.internalGroups, this.internalCollapsedGroups)\n },\n\n effectiveHiddenColSet() {\n const s = new Set(this.hiddenColSet)\n this.hiddenColFromGroupSet.forEach(i => s.add(i))\n return s\n },\n\n rowGroups() { return this.internalGroups.filter(g => g.type === 'row') },\n colGroups() { return this.internalGroups.filter(g => g.type === 'column') },\n\n visibleRowRange() {\n const rows = []\n for (let i = this.virtualStartIndex; i <= this.virtualEndIndex; i++) rows.push(i)\n return rows\n },\n\n primaryRange() {\n return getPrimaryRange(this.selectionRanges, this.activeCell)\n },\n\n fillHandleVisible() {\n return this.primaryRange && !this.editingCell\n },\n\n fillHandleStyle() {\n if (!this.primaryRange) return {}\n const r = normalizeRange(this.primaryRange)\n // O(1) lookup via cumulativeHeights instead of O(endRow) getRowTop loop\n const bottom = this.cumulativeHeights[r.endRow + 1] || 0\n const right = this.getColLeft(r.endCol + 1)\n return {\n position: 'absolute',\n width: '6px',\n height: '6px',\n background: '#1a73e8',\n border: '1px solid white',\n bottom: (this.totalVirtualHeight - bottom + 1) + 'px',\n right: (this.scrollContentWidth - right + 1) + 'px',\n cursor: 'crosshair',\n zIndex: 10\n }\n },\n\n\n invalidTooltipStyle() {\n if (!this.invalidCell || !this.invalidCell.rect) return {}\n const { rect } = this.invalidCell\n return {\n position: 'fixed',\n top: (rect.bottom + 4) + 'px',\n left: rect.left + 'px',\n zIndex: 9999\n }\n },\n\n dragGhostStyle() {\n if (!this.dragMove.active || this.dragMove.targetRow < 0) return {}\n const r = normalizeRange(this.dragMove.sourceRange)\n const rows = r.endRow - r.startRow\n const cols = r.endCol - r.startCol\n // O(1) lookup via cumulativeHeights instead of O(targetRow) getRowTop loop\n const top = this.cumulativeHeights[this.dragMove.targetRow] || 0\n const left = this.getColLeft(this.dragMove.targetCol)\n const width = this.getRangeWidth(this.dragMove.targetCol, this.dragMove.targetCol + cols)\n const height = this.getRangeHeight(this.dragMove.targetRow, this.dragMove.targetRow + rows)\n return {\n position: 'absolute',\n top: top + 'px',\n left: left + 'px',\n width: width + 'px',\n height: height + 'px',\n border: '2px dashed #1a73e8',\n pointerEvents: 'none',\n zIndex: 20\n }\n },\n\n wrapperStyle() {\n return {\n width: typeof this.width === 'number' ? this.width + 'px' : this.width,\n height: this.height + 'px',\n position: 'relative',\n outline: 'none',\n userSelect: 'none'\n }\n },\n\n headerAreaStyle() {\n return {\n display: 'flex',\n flexDirection: 'row',\n position: 'sticky',\n top: 0,\n zIndex: 30,\n background: '#f8f9fa'\n }\n },\n\n cornerStyle() {\n const outlineW = this.rowGroups.length ? OUTLINE_GUTTER : 0\n return {\n width: (ROW_HEADER_WIDTH + outlineW) + 'px',\n minWidth: (ROW_HEADER_WIDTH + outlineW) + 'px',\n height: (this._headerRowH * this.headerDepth) + 'px',\n background: '#f0f0f0',\n borderRight: '1px solid #ccc',\n borderBottom: '1px solid #ccc',\n boxSizing: 'border-box',\n flexShrink: 0\n }\n },\n\n rowOutlineGutterStyle() {\n return { width: OUTLINE_GUTTER + 'px', flexShrink: 0 }\n },\n\n frozenColHeaderStyle() {\n const w = this.getFrozenWidth()\n return {\n width: w + 'px',\n minWidth: w + 'px',\n overflow: 'hidden',\n flexShrink: 0\n }\n },\n\n scrollColHeaderStyle() {\n // Once measured, lock the header to the body's exact clientWidth (flex: 0 0 <px>)\n // instead of growing independently via flex:1 — this guarantees identical\n // scrollWidth-clientWidth (max scrollLeft) on both, so syncing scrollLeft never clamps\n // one before the other. (padding-right can't do this: clientWidth always includes\n // padding, so padding never actually shrinks it.)\n if (this.headerViewportWidth != null) {\n return { flex: '0 0 ' + this.headerViewportWidth + 'px', overflow: 'hidden' }\n }\n return { flex: 1, overflow: 'hidden' }\n },\n\n bodyAreaStyle() {\n return {\n display: 'flex',\n flexDirection: 'row',\n flex: 1,\n position: 'relative'\n }\n },\n\n rowHeaderColStyle() {\n return {\n width: ROW_HEADER_WIDTH + 'px',\n minWidth: ROW_HEADER_WIDTH + 'px',\n flexShrink: 0,\n display: 'flex',\n flexDirection: 'column',\n position: 'sticky',\n left: 0,\n zIndex: 20,\n background: '#f0f0f0'\n }\n },\n\n rowOutlineColStyle() {\n return {\n width: OUTLINE_GUTTER + 'px',\n minWidth: OUTLINE_GUTTER + 'px',\n flexShrink: 0,\n position: 'relative',\n background: '#fafafa'\n }\n },\n\n scrollBodyStyle() {\n return {\n flex: 1,\n overflow: 'auto',\n position: 'relative'\n }\n },\n\n frozenColBodyStyle() {\n const w = this.getFrozenWidth()\n return {\n position: 'sticky',\n left: 0,\n width: w + 'px',\n zIndex: 10,\n background: 'white',\n overflow: 'hidden'\n }\n },\n\n mainCellsStyle() {\n const left = this.getFrozenWidth()\n return {\n position: 'absolute',\n left: left + 'px',\n top: 0,\n bottom: 0\n }\n },\n\n scrollContentWidth() {\n let w = 0\n for (let i = 0; i < this.leafCols.length; i++) {\n if (!this.isColHidden(i) && !this.pinnedRightSet.has(i)) w += this.getColW(i)\n }\n // Extra right padding so pinned-right zone doesn't cover the last scrollable column\n if (this.hasPinnedRight) w += this.pinnedRightWidth\n return w\n },\n\n scrollableContentWidth() {\n let w = 0\n for (let i = 0; i < this.leafCols.length; i++) {\n if (!this.isColHidden(i) && this.isColumnScrollable(i)) w += this.getColW(i)\n }\n return w\n },\n\n pinnedRightBodyStyle() {\n return {\n position: 'sticky',\n right: 0,\n width: this.pinnedRightWidth + 'px',\n zIndex: 10,\n background: 'white',\n flexShrink: 0\n }\n },\n\n pinnedRightHeaderStyle() {\n return {\n width: this.pinnedRightWidth + 'px',\n minWidth: this.pinnedRightWidth + 'px',\n overflow: 'hidden',\n flexShrink: 0,\n borderLeft: '1px solid #ccc'\n }\n },\n\n scrollRowHeadersStyle() {\n return { flex: 1, overflow: 'hidden', position: 'relative' }\n },\n\n footerAreaStyle() {\n return {\n display: 'flex',\n flexDirection: 'row',\n flexShrink: 0,\n position: 'sticky',\n bottom: 0,\n zIndex: 30,\n background: '#f8f9fa',\n boxShadow: '0 -2px 0 0 #d0d0d0'\n }\n },\n\n footerLabelStyle() {\n const outlineW = this.rowGroups.length ? OUTLINE_GUTTER : 0\n return {\n width: (ROW_HEADER_WIDTH + outlineW) + 'px',\n minWidth: (ROW_HEADER_WIDTH + outlineW) + 'px',\n height: this._footerRowH + 'px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: '#f0f0f0',\n borderRight: '1px solid #ccc',\n borderTop: '1px solid #ccc',\n boxSizing: 'border-box',\n flexShrink: 0,\n fontSize: '12px',\n fontWeight: 600\n }\n },\n\n frozenColFooterStyle() {\n const w = this.getFrozenWidth()\n return {\n width: w + 'px',\n minWidth: w + 'px',\n height: this._footerRowH + 'px',\n overflow: 'hidden',\n flexShrink: 0\n }\n },\n\n pinnedRightFooterStyle() {\n return {\n width: this.pinnedRightWidth + 'px',\n minWidth: this.pinnedRightWidth + 'px',\n height: this._footerRowH + 'px',\n overflow: 'hidden',\n flexShrink: 0,\n borderLeft: '1px solid #ccc'\n }\n },\n\n colOutlineRowStyle() {\n return {\n position: 'absolute',\n top: -OUTLINE_GUTTER + 'px',\n left: 0,\n right: 0,\n height: OUTLINE_GUTTER + 'px'\n }\n },\n\n resizeLineStyle() {\n if (!this.resizing.active) return {}\n if (this.resizing.type === 'col') {\n return {\n position: 'fixed',\n top: 0,\n left: this.resizing.currentPos + 'px',\n width: '2px',\n height: '100%',\n background: '#1a73e8',\n pointerEvents: 'none',\n zIndex: 9999\n }\n } else {\n return {\n position: 'fixed',\n left: 0,\n top: this.resizing.currentPos + 'px',\n height: '2px',\n width: '100%',\n background: '#1a73e8',\n pointerEvents: 'none',\n zIndex: 9999\n }\n }\n }\n },\n\n watch: {\n size() {\n this.rowHeights = {}\n this.$nextTick(() => this.updateVirtualScroll())\n },\n\n autoWidth(v) {\n if (v) this._applyAutoWidth()\n },\n\n columns: {\n immediate: true,\n handler(cols) {\n this.leafCols = flattenColumns(cols)\n this.headerDepth = columnTreeDepth(cols)\n this.headerRows = buildHeaderRows(cols, this.headerDepth)\n // Init colWidths\n this.leafCols.forEach(col => {\n if (this.colWidths[col._flatIndex] === undefined) {\n this.colWidths[col._flatIndex] = col.width || DEFAULT_COL_WIDTH\n }\n })\n this.computeColumnTotals()\n this.updateVirtualScroll()\n this._applyAutoWidth()\n }\n },\n merges: {\n immediate: true,\n handler(v) { this.internalMerges = v ? [...v] : [] }\n },\n collapsedGroups: {\n immediate: true,\n handler(v) { this.internalCollapsedGroups = v ? [...v] : [] }\n },\n data: {\n immediate: true,\n handler(newData) {\n // Keep _dataRaw in sync so template uses fresh data without triggering dependArray\n this._dataRaw = newData || []\n this.updateVirtualScroll()\n this.computeColumnTotals()\n // Force re-render so cell values refresh even when virtualScroll indices don't change\n // (e.g. editing a cell: same row count, same scroll position, only content changed)\n this.$forceUpdate()\n this._applyAutoWidth()\n }\n },\n totalRows() { this.updateVirtualScroll() },\n hiddenRowSet() { this.updateVirtualScroll() },\n totalVirtualHeight() { this.$nextTick(this.syncHeaderViewportWidth) }\n },\n\n created() {\n // Non-reactive row data reference — avoids Vue 2 dependArray(data) O(n) loop.\n // Vue 2 calls dependArray() for EVERY `data[ri]` access in the template,\n // iterating all 50,000 rows each time. With 1200 cells per render that's\n // 60 million iterations per render (~600ms). _dataRaw bypasses this.\n this._dataRaw = this.data || []\n\n // Non-reactive caches: same (w,h) → same style object → Vue 2 skips DOM style writes\n this._cellStyleCache = Object.create(null)\n this._rowStyleCache = Object.create(null)\n this._rowHeaderStyleCache = Object.create(null)\n // rAF handles for scroll/resize throttle\n this._scrollRAF = null\n this._resizeRAF = null\n\n // Undo/redo history — non-reactive to avoid Vue overhead.\n // Each entry: { type: 'data'|'merges', before: {...}, after: {...} }\n // 'data' stores only diff rows: { indices: number[], rows: object[] }\n this._history = []\n this._historyIndex = -1\n this._historySkip = false // true while undo/redo is in progress\n\n },\n\n mounted() {\n this.updateVirtualScroll()\n this.$nextTick(this.syncHeaderViewportWidth)\n document.addEventListener('mousemove', this.onGlobalMouseMove)\n document.addEventListener('mouseup', this.onGlobalMouseUp)\n document.addEventListener('keydown', this.onGlobalKeyDown)\n document.addEventListener('mousedown', this._onDocMouseDownForDropdown, true)\n window.addEventListener('resize', this.syncHeaderViewportWidth)\n window.addEventListener('scroll', this._onWindowScrollForDropdown, true)\n },\n\n beforeDestroy() {\n document.removeEventListener('mousemove', this.onGlobalMouseMove)\n document.removeEventListener('mouseup', this.onGlobalMouseUp)\n document.removeEventListener('keydown', this.onGlobalKeyDown)\n document.removeEventListener('mousedown', this._onDocMouseDownForDropdown, true)\n window.removeEventListener('resize', this.syncHeaderViewportWidth)\n window.removeEventListener('scroll', this._onWindowScrollForDropdown, true)\n clearTimeout(this._scrollEndTimer)\n },\n\n methods: {\n // =========================================================\n // VIRTUAL SCROLL\n // =========================================================\n updateVirtualScroll() {\n const footerH = this.hasFooter ? this._footerRowH : 0\n const containerH = this.height - (this._headerRowH * this.headerDepth) - footerH\n // Use binary search against the Vue-cached cumulativeHeights → O(log n) per scroll\n const result = findVisibleRange(\n this.scrollTop, Math.max(containerH, 100), this.totalRows, this.cumulativeHeights\n )\n this.virtualStartIndex = result.startIndex\n this.virtualEndIndex = result.endIndex\n this.virtualOffsetTop = result.offsetTop\n this.totalVirtualHeight = result.totalHeight\n },\n\n onBodyScroll(e) {\n const target = e.target\n // Sync sibling scroll containers immediately (visual alignment, no layout trigger)\n if (this.$refs.scrollColHeader) this.$refs.scrollColHeader.scrollLeft = target.scrollLeft\n if (this.$refs.scrollFooter) this.$refs.scrollFooter.scrollLeft = target.scrollLeft\n if (this.$refs.scrollRowHeaders) this.$refs.scrollRowHeaders.scrollTop = target.scrollTop\n\n // Close dropdown on scroll — fixed-position dropdown drifts away from the moving cell\n if (this.selectDropdown) this.closeSelectDropdown()\n\n // Suppress row transitions during scroll to avoid animation on every virtual-scroll paint\n if (this.transitionEnabled) {\n this._isScrolling = true\n clearTimeout(this._scrollEndTimer)\n this._scrollEndTimer = setTimeout(() => { this._isScrolling = false }, 150)\n }\n\n // Throttle reactive state updates + virtual scroll recalc to one rAF per frame\n if (!this._scrollRAF) {\n this._scrollRAF = requestAnimationFrame(() => {\n this._scrollRAF = null\n this.scrollTop = target.scrollTop\n this.scrollLeft = target.scrollLeft\n this.updateVirtualScroll()\n })\n }\n },\n\n onScroll(e) {},\n\n // .eg-scroll-col-header (overflow:hidden, synced via JS) has no native scrollbar, but\n // .eg-scroll-body (overflow:auto) does — its real vertical scrollbar eats into its\n // clientWidth. If both containers just grow via flex:1, the header ends up wider than\n // the body, so its max scrollLeft is smaller and syncing clamps it before the body\n // reaches its rightmost scroll position — column borders desync right at the scroll\n // end. Locking the header to the body's exact measured clientWidth (scrollColHeaderStyle)\n // makes both max scrollLeft values identical.\n syncHeaderViewportWidth() {\n const el = this.$refs.scrollBody\n if (!el) return\n if (this.headerViewportWidth !== el.clientWidth) this.headerViewportWidth = el.clientWidth\n },\n\n // =========================================================\n // HELPERS\n // =========================================================\n isRowHidden(ri) { return this.hiddenRowSet.has(ri) },\n isColHidden(ci) { return this.effectiveHiddenColSet.has(ci) },\n\n getColW(ci) {\n return this.colWidths[ci] !== undefined ? this.colWidths[ci] : (this.leafCols[ci] ? this.leafCols[ci].width || DEFAULT_COL_WIDTH : DEFAULT_COL_WIDTH)\n },\n\n getRowH(ri) {\n if (this.isRowHidden(ri)) return 0\n return this.rowHeights[ri] || this._rowH\n },\n\n getColLeft(ci) {\n let left = 0\n for (let i = 0; i < ci && i < this.leafCols.length; i++) {\n if (!this.isColHidden(i)) left += this.getColW(i)\n }\n return left\n },\n\n getFrozenWidth() {\n return this.hasPinnedLeft ? this.pinnedLeftWidth : 0\n },\n\n isColumnPinnedLeft(ci) {\n return this.pinnedLeftSet.has(ci)\n },\n\n isColumnPinnedRight(ci) {\n return this.pinnedRightSet.has(ci)\n },\n\n isColumnScrollable(ci) {\n return !this.isColumnPinnedLeft(ci) && !this.isColumnPinnedRight(ci)\n },\n\n getZoneColLeft(ci, zone) {\n let left = 0\n if (zone === 'left') {\n for (const col of this.pinnedLeftCols) {\n if (col._flatIndex === ci) break\n if (!this.isColHidden(col._flatIndex)) left += this.getColW(col._flatIndex)\n }\n } else if (zone === 'right') {\n for (const col of this.pinnedRightCols) {\n if (col._flatIndex === ci) break\n if (!this.isColHidden(col._flatIndex)) left += this.getColW(col._flatIndex)\n }\n } else {\n for (let i = 0; i < this.leafCols.length; i++) {\n const col = this.leafCols[i]\n if (col._flatIndex === ci) break\n if (!this.isColHidden(col._flatIndex) && this.isColumnScrollable(col._flatIndex)) {\n left += this.getColW(col._flatIndex)\n }\n }\n }\n return left\n },\n\n isCellInZone(cell, rowIdx, zone) {\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const z = this.isColumnPinnedLeft(i) ? 'left' : (this.isColumnPinnedRight(i) ? 'right' : 'scroll')\n if (z === zone) return true\n }\n return false\n }\n const ci = cell.column._flatIndex\n if (this.isColHidden(ci)) return false\n const z = this.isColumnPinnedLeft(ci) ? 'left' : (this.isColumnPinnedRight(ci) ? 'right' : 'scroll')\n return z === zone\n },\n\n getHeaderCellStyleForZone(cell, rowIndex, zone) {\n let leftLeafIdx = cell._firstLeafFlatIndex\n if (cell.isGroup) {\n const end = leftLeafIdx + cell.colspan - 1\n for (let i = leftLeafIdx; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const z = this.isColumnPinnedLeft(i) ? 'left' : (this.isColumnPinnedRight(i) ? 'right' : 'scroll')\n if (z === zone) { leftLeafIdx = i; break }\n }\n }\n const left = this.getZoneColLeft(leftLeafIdx, zone)\n let width = 0\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const z = this.isColumnPinnedLeft(i) ? 'left' : (this.isColumnPinnedRight(i) ? 'right' : 'scroll')\n if (z !== zone) continue\n width += this.getColW(i)\n }\n } else {\n width = this.getColW(cell.column._flatIndex)\n }\n return {\n position: 'absolute',\n top: (this._headerRowH * rowIndex) + 'px',\n left: left + 'px',\n width: width + 'px',\n height: (this._headerRowH * cell.rowspan) + 'px',\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n boxSizing: 'border-box',\n borderRight: '1px solid #ccc', borderBottom: '1px solid #ccc',\n background: '#f0f0f0', fontSize: '12px', fontWeight: 600,\n overflow: 'hidden', whiteSpace: 'nowrap', userSelect: 'none', cursor: 'pointer'\n }\n },\n\n getRangeWidth(startCol, endCol) {\n let w = 0\n for (let i = startCol; i <= endCol && i < this.leafCols.length; i++) {\n if (!this.isColHidden(i)) w += this.getColW(i)\n }\n return w\n },\n\n getRangeHeight(startRow, endRow) {\n let h = 0\n for (let i = startRow; i <= endRow && i < this.totalRows; i++) {\n if (!this.isRowHidden(i)) h += this.getRowH(i)\n }\n return h\n },\n\n getCellValue(ri, ci) {\n const col = this.leafCols[ci]\n if (!col) return ''\n const row = this._dataRaw[ri]\n if (!row) return ''\n const v = row[col.key]\n return v !== undefined ? v : ''\n },\n\n isEditing(ri, ci) {\n return this.editingCell && this.editingCell.row === ri && this.editingCell.col === ci\n },\n\n isMergedAway(ri, ci) {\n const merge = findMergeAtCell(ri, ci, this.internalMerges)\n if (!merge) return false\n const nm = normalizeRange(merge)\n return !(nm.startRow === ri && nm.startCol === ci)\n },\n\n getMergeSpan(ri, ci) {\n const merge = findMergeAtCell(ri, ci, this.internalMerges)\n if (!merge) return { rowspan: 1, colspan: 1 }\n const nm = normalizeRange(merge)\n return {\n rowspan: nm.endRow - nm.startRow + 1,\n colspan: nm.endCol - nm.startCol + 1\n }\n },\n\n isSelected(ri, ci) {\n return isCellInAnyRange(ri, ci, this.selectionRanges)\n },\n\n isActiveCell(ri, ci) {\n return this.activeCell.row === ri && this.activeCell.col === ci\n },\n\n isCopyHighlighted(ri, ci) {\n if (!this.clipboardRange || this.clipboardMode !== 'copy') return false\n return isCellInRange(ri, ci, this.clipboardRange)\n },\n\n // =========================================================\n // STYLES\n // =========================================================\n getRowStyle(ri) {\n const h = this.getRowH(ri)\n const key = h\n if (this._rowStyleCache[key]) return this._rowStyleCache[key]\n const style = { height: h + 'px', minHeight: h + 'px' }\n this._rowStyleCache[key] = style\n return style\n },\n\n getRowHeaderStyle(ri) {\n const h = this.getRowH(ri)\n const key = h\n if (this._rowHeaderStyleCache[key]) return this._rowHeaderStyleCache[key]\n const style = { height: h + 'px' }\n this._rowHeaderStyleCache[key] = style\n return style\n },\n\n getCellStyle(ri, ci) {\n const merge = findMergeAtCell(ri, ci, this.internalMerges)\n const nm = merge ? normalizeRange(merge) : null\n let w = this.getColW(ci)\n let h = this.getRowH(ri)\n\n if (nm && nm.startRow === ri && nm.startCol === ci) {\n w = this.getRangeWidth(nm.startCol, nm.endCol)\n h = this.getRangeHeight(nm.startRow, nm.endRow)\n }\n\n // Position cells with the same absolute-left arithmetic the header uses\n // (getZoneColLeft), instead of relying on flex layout to accumulate widths —\n // flex and a JS integer sum can drift apart by a few px over many columns,\n // desyncing the body's column borders from the header's.\n const zone = this.isColumnPinnedLeft(ci) ? 'left' : (this.isColumnPinnedRight(ci) ? 'right' : 'scroll')\n const left = this.getZoneColLeft(ci, zone)\n\n // Cache by dimensions+position — same (w,h,left) → same object ref → Vue 2 skips DOM style writes\n const key = w + '_' + h + '_' + left\n if (this._cellStyleCache[key]) return this._cellStyleCache[key]\n const style = { position: 'absolute', left: left + 'px', top: '0px', width: w + 'px', minWidth: w + 'px', height: h + 'px' }\n this._cellStyleCache[key] = style\n return style\n },\n\n getCellClasses(ri, ci) {\n const col = this.leafCols[ci]\n return {\n 'eg-cell-active': this.isActiveCell(ri, ci),\n 'eg-cell-selected': this.isSelected(ri, ci) && !this.isActiveCell(ri, ci),\n 'eg-cell-fill-preview': this.isInFillPreview(ri, ci),\n 'eg-cell-copy': this.isCopyHighlighted(ri, ci),\n 'eg-cell-merged-hidden': this.isMergedAway(ri, ci),\n 'eg-cell-select': !!(col && this.isSelectLikeCol(col)),\n 'eg-cell-checkbox': !!(col && col.type === 'checkbox'),\n 'eg-cell-readonly': !this.isCellEditable(ri, ci),\n 'eg-cell-invalid': !!(this.invalidCell && this.invalidCell.row === ri && this.invalidCell.col === ci)\n }\n },\n\n isInFillPreview(ri, ci) {\n if (!this.fillDrag.active || !this.fillDrag.sourceRange) return false\n const src = this.fillDrag.sourceRange\n const targetRow = this.fillDrag.currentRow\n const targetCol = this.fillDrag.currentCol\n if (targetRow > src.endRow) {\n return ri > src.endRow && ri <= targetRow && ci >= src.startCol && ci <= src.endCol\n }\n if (targetCol > src.endCol) {\n return ci > src.endCol && ci <= targetCol && ri >= src.startRow && ri <= src.endRow\n }\n return false\n },\n\n getHeaderCellStyle(cell, rowIndex, isFrozen) {\n const zone = isFrozen ? 'left' : 'scroll'\n let leftLeafIdx = cell._firstLeafFlatIndex\n if (cell.isGroup) {\n const end = leftLeafIdx + cell.colspan - 1\n for (let i = leftLeafIdx; i <= end; i++) {\n if (this.isColHidden(i)) continue\n if (isFrozen ? this.isColumnPinnedLeft(i) : this.isColumnScrollable(i)) { leftLeafIdx = i; break }\n }\n }\n const left = this.getZoneColLeft(leftLeafIdx, zone)\n\n let width = 0\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const inZone = isFrozen ? this.isColumnPinnedLeft(i) : this.isColumnScrollable(i)\n if (!inZone) continue\n width += this.getColW(i)\n }\n } else {\n width = this.getColW(cell.column._flatIndex)\n }\n\n return {\n position: 'absolute',\n top: (this._headerRowH * rowIndex) + 'px',\n left: left + 'px',\n width: width + 'px',\n height: (this._headerRowH * cell.rowspan) + 'px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxSizing: 'border-box',\n borderRight: '1px solid #ccc',\n borderBottom: '1px solid #ccc',\n background: '#f0f0f0',\n fontSize: '12px',\n fontWeight: 600,\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n cursor: 'pointer'\n }\n },\n\n // =========================================================\n // FOOTER (column totals)\n // =========================================================\n computeColumnTotals() {\n const totals = {}\n const rows = this._dataRaw || []\n this.leafCols.forEach(col => {\n if (!col.showTotal) return\n let sum = 0\n let hasNumber = false\n for (let ri = 0; ri < rows.length; ri++) {\n const row = rows[ri]\n const v = row ? row[col.key] : undefined\n if (typeof v === 'number' && isFinite(v)) {\n sum += v\n hasNumber = true\n }\n }\n totals[col._flatIndex] = hasNumber ? Math.round(sum * 1e6) / 1e6 : null\n })\n this.columnTotals = totals\n },\n\n getColumnTotalDisplay(ci) {\n const col = this.leafCols[ci]\n if (!col || !col.showTotal) return ''\n const v = this.columnTotals[ci]\n return v === null || v === undefined ? '-' : v\n },\n\n getFooterCellStyle(ci) {\n const zone = this.isColumnPinnedLeft(ci) ? 'left' : (this.isColumnPinnedRight(ci) ? 'right' : 'scroll')\n const left = this.getZoneColLeft(ci, zone)\n const w = this.getColW(ci)\n return {\n position: 'absolute',\n left: left + 'px',\n top: '0px',\n width: w + 'px',\n minWidth: w + 'px',\n height: this._footerRowH + 'px'\n }\n },\n\n isCellVisible(cell, rowIndex, isFrozen) {\n if (cell.isGroup) {\n const start = cell._firstLeafFlatIndex\n const end = start + cell.colspan - 1\n for (let i = start; i <= end; i++) {\n if (this.isColHidden(i)) continue\n const inZone = isFrozen ? this.isColumnPinnedLeft(i) : this.isColumnScrollable(i)\n if (inZone) return true\n }\n return false\n }\n const ci = cell.column._flatIndex\n if (this.isColHidden(ci)) return false\n return isFrozen ? this.isColumnPinnedLeft(ci) : this.isColumnScrollable(ci)\n },\n\n getRowGroupBtnStyle(group) {\n const top = getRowTop(group.startIndex, this._rowH, this.rowHeights) - this.scrollTop\n return {\n position: 'absolute',\n top: top + 'px',\n left: '2px',\n width: '16px',\n height: '16px',\n lineHeight: '16px',\n textAlign: 'center',\n fontSize: '10px',\n cursor: 'pointer',\n background: '#e8eaf6',\n border: '1px solid #9fa8da',\n borderRadius: '2px',\n zIndex: 5\n }\n },\n\n getColGroupBtnStyle(group) {\n const left = this.getColLeft(group.startIndex) - this.scrollLeft\n return {\n position: 'absolute',\n left: left + 'px',\n top: '2px',\n width: '16px',\n height: '16px',\n lineHeight: '16px',\n textAlign: 'center',\n fontSize: '10px',\n cursor: 'pointer',\n background: '#e8eaf6',\n border: '1px solid #9fa8da',\n borderRadius: '2px'\n }\n },\n\n // =========================================================\n // CELL SELECTION (3.1)\n // =========================================================\n selectCell(row, col, addToSelection = false) {\n if (row < 0 || row >= this.totalRows || col < 0 || col >= this.leafCols.length) return\n this.activeCell = { row, col }\n if (addToSelection) {\n this.selectionRanges.push(singleCellRange(row, col))\n } else {\n this.selectionRanges = [singleCellRange(row, col)]\n }\n this.emitSelectionChange()\n },\n\n selectRange(startRow, startCol, endRow, endCol, replace = true) {\n const range = { startRow, startCol, endRow, endCol }\n if (replace) {\n this.selectionRanges = [range]\n } else {\n this.selectionRanges.push(range)\n }\n this.emitSelectionChange()\n },\n\n selectRow(ri) {\n this.activeCell = { row: ri, col: 0 }\n this.selectionRanges = [{ startRow: ri, endRow: ri, startCol: 0, endCol: this.leafCols.length - 1 }]\n this.emitSelectionChange()\n },\n\n selectColumn(ci) {\n this.activeCell = { row: 0, col: ci }\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: ci, endCol: ci }]\n this.emitSelectionChange()\n },\n\n selectAll() {\n this.activeCell = { row: 0, col: 0 }\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: 0, endCol: this.leafCols.length - 1 }]\n this.emitSelectionChange()\n },\n\n emitSelectionChange() {\n this.$emit('selection-change', {\n activeCell: { ...this.activeCell },\n ranges: this.selectionRanges.map(r => normalizeRange(r))\n })\n },\n\n onCellMouseDown(e, ri, ci) {\n this.invalidCell = null\n if (this.editingCell) {\n const editCol = this.leafCols[this.editingCell.col]\n if (editCol && this._hasCustomEdit(editCol)) {\n this.cancelEdit()\n } else {\n this.commitEdit()\n }\n }\n this.hideContextMenu()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n\n if (e.ctrlKey || e.metaKey) {\n if (this.selectDropdown) this.closeSelectDropdown()\n // Ctrl+click: start new independent range\n this.activeCell = { row: ri, col: ci }\n this.mouseDownRange = { startRow: ri, startCol: ci, endRow: ri, endCol: ci }\n this.selectionRanges.push(this.mouseDownRange)\n this.isCtrlDrag = true\n } else if (e.shiftKey && this.selectionRanges.length > 0) {\n if (this.selectDropdown) this.closeSelectDropdown()\n // Shift+click: extend primary range to this cell\n this.selectionRanges = [buildRange(this.activeCell, { row: ri, col: ci })]\n } else {\n const wasActive = this.activeCell.row === ri && this.activeCell.col === ci\n this.activeCell = { row: ri, col: ci }\n this.mouseDownRange = { startRow: ri, startCol: ci, endRow: ri, endCol: ci }\n this.selectionRanges = [this.mouseDownRange]\n this.isCtrlDrag = false\n\n // Open select dropdown on click if cell was already active (2nd click)\n const col = this.leafCols[ci]\n if (wasActive && col && this.isSelectLikeCol(col) && !this._hasCustomEdit(col) && this.isCellEditable(ri, ci)) {\n this.openSelectDropdown(ri, ci, e)\n e.preventDefault()\n this.isMouseDown = true\n this.emitSelectionChange()\n return\n }\n if (this.selectDropdown) this.closeSelectDropdown()\n }\n\n this.isMouseDown = true\n this.emitSelectionChange()\n e.preventDefault()\n },\n\n onCellMouseOver(e, ri, ci) {\n if (!this.isMouseDown) return\n if (this.fillDrag.active) return\n if (this.dragMove.active) {\n this.dragMove.targetRow = ri\n this.dragMove.targetCol = ci\n return\n }\n\n // Extend current drag range\n if (this.mouseDownRange) {\n this.mouseDownRange.endRow = ri\n this.mouseDownRange.endCol = ci\n this.emitSelectionChange()\n }\n },\n\n onCellMouseUp() {\n this.isMouseDown = false\n },\n\n onCellDblClick(ri, ci) {\n const col = this.leafCols[ci]\n if (!this.isCellEditable(ri, ci)) return\n if (col && this.isSelectLikeCol(col) && !this._hasCustomEdit(col)) {\n this.openSelectDropdownForActive()\n return\n }\n if (col && col.type === 'checkbox') return\n this.startEditing(ri, ci, null, true)\n },\n\n onRowHeaderMouseDown(e, ri) {\n const endCol = this.leafCols.length - 1\n const fullRow = { startRow: ri, endRow: ri, startCol: 0, endCol }\n\n if (e.ctrlKey || e.metaKey) {\n if (e.shiftKey && this.selectionRanges.length > 0) {\n // Ctrl+Shift: active qatordan ri gacha yangi range qo'sh\n const sr = Math.min(this.activeCell.row, ri)\n const er = Math.max(this.activeCell.row, ri)\n this.selectionRanges = [...this.selectionRanges, { startRow: sr, endRow: er, startCol: 0, endCol }]\n } else {\n // Ctrl+click: qatorni toggle qil\n const idx = this.selectionRanges.findIndex(r =>\n r.startRow === ri && r.endRow === ri && r.startCol === 0 && r.endCol === endCol\n )\n if (idx >= 0) {\n const next = this.selectionRanges.filter((_, i) => i !== idx)\n this.selectionRanges = next.length ? next : [fullRow]\n } else {\n this.activeCell = { row: ri, col: 0 }\n this.selectionRanges = [...this.selectionRanges, fullRow]\n }\n }\n } else if (e.shiftKey && this.selectionRanges.length > 0) {\n const sr = Math.min(this.activeCell.row, ri)\n const er = Math.max(this.activeCell.row, ri)\n this.selectionRanges = [{ startRow: sr, endRow: er, startCol: 0, endCol }]\n } else {\n this.activeCell = { row: ri, col: 0 }\n this.selectionRanges = [fullRow]\n }\n\n this.mouseDownRange = this.selectionRanges[this.selectionRanges.length - 1]\n this.isMouseDown = true\n this.emitSelectionChange()\n e.preventDefault()\n },\n\n onColHeaderMouseDown(e, cell) {\n if (cell.isGroup) return\n const ci = cell.column._flatIndex\n if (e.shiftKey && this.selectionRanges.length > 0) {\n const sc = Math.min(this.activeCell.col, ci)\n const ec = Math.max(this.activeCell.col, ci)\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: sc, endCol: ec }]\n } else {\n this.activeCell = { row: 0, col: ci }\n this.selectionRanges = [{ startRow: 0, endRow: this.totalRows - 1, startCol: ci, endCol: ci }]\n }\n this.isMouseDown = true\n this.mouseDownRange = this.selectionRanges[0]\n this.emitSelectionChange()\n e.preventDefault()\n },\n\n onColHeaderDblClick(e, cell) {\n if (cell.isGroup) return\n this.autoFitColumn(cell.column._flatIndex)\n },\n\n onColHeaderContextMenu(e, cell) {\n if (!cell.isGroup) {\n this.contextMenu = { visible: true, x: e.clientX, y: e.clientY, row: -1, col: cell.column._flatIndex }\n }\n },\n\n onRowContextMenu(e, ri) {\n this.contextMenu = { visible: true, x: e.clientX, y: e.clientY, row: ri, col: -1 }\n },\n\n onCellContextMenu(e, ri, ci) {\n if (!this.isSelected(ri, ci)) {\n this.selectCell(ri, ci)\n }\n this.contextMenu = { visible: true, x: e.clientX, y: e.clientY, row: ri, col: ci }\n },\n\n hideContextMenu() {\n this.contextMenu.visible = false\n },\n\n // =========================================================\n // KEYBOARD NAVIGATION (3.2)\n // =========================================================\n onKeyDown(e) {\n // Delegate keyboard navigation to the active dropdown component\n if (this.selectDropdown) {\n const comp = this.$refs.selectDropdownComp\n if (comp && comp.handleKey) comp.handleKey(e)\n return\n }\n\n if (this.editingCell) return // handled by onEditKeyDown\n const key = e.key\n\n if ((e.ctrlKey || e.metaKey) && key === 'z' && !e.shiftKey) {\n e.preventDefault()\n this.undo()\n return\n }\n if ((e.ctrlKey || e.metaKey) && (key === 'y' || (key === 'z' && e.shiftKey))) {\n e.preventDefault()\n this.redo()\n return\n }\n\n if (key === 'a' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.selectAll()\n return\n }\n\n if (key === 'c' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.copySelection()\n return\n }\n if (key === 'x' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.cutSelection()\n return\n }\n if (key === 'v' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n this.pasteAt(this.activeCell.row, this.activeCell.col)\n return\n }\n\n if (key === 'Delete' || key === 'Backspace') {\n e.preventDefault()\n this.clearSelection()\n return\n }\n\n if (key === 'F2') {\n e.preventDefault()\n const activeCol = this.leafCols[this.activeCell.col]\n if (activeCol && this.isSelectLikeCol(activeCol) && !this._hasCustomEdit(activeCol)) {\n this.openSelectDropdownForActive()\n } else if (activeCol && activeCol.type === 'checkbox') {\n this.toggleCheckbox(this.activeCell.row, this.activeCell.col)\n } else {\n this.startEditing(this.activeCell.row, this.activeCell.col, null, true)\n }\n return\n }\n\n if (key === ' ') {\n const activeCol = this.leafCols[this.activeCell.col]\n if (activeCol && activeCol.type === 'checkbox') {\n e.preventDefault()\n this.toggleCheckbox(this.activeCell.row, this.activeCell.col)\n return\n }\n }\n\n if (key === 'Escape') {\n this.clipboardRange = null\n this.clipboardMatrix = null\n this.clipboardMode = null\n return\n }\n\n if (key === 'Tab') {\n e.preventDefault()\n this.moveActive(0, e.shiftKey ? -1 : 1, e)\n return\n }\n if (key === 'Enter') {\n e.preventDefault()\n this.moveActive(e.shiftKey ? -1 : 1, 0, e)\n return\n }\n\n const arrows = { ArrowUp: [-1,0], ArrowDown: [1,0], ArrowLeft: [0,-1], ArrowRight: [0,1] }\n if (arrows[key]) {\n e.preventDefault()\n const [dr, dc] = arrows[key]\n if (e.shiftKey) {\n // Extend selection\n const pr = this.primaryRange || singleCellRange(this.activeCell.row, this.activeCell.col)\n const nm = normalizeRange(pr)\n // Determine which edge to extend\n if (dr === -1) pr.startRow = Math.max(0, nm.startRow - 1)\n if (dr === 1) pr.endRow = Math.min(this.totalRows - 1, nm.endRow + 1)\n if (dc === -1) pr.startCol = Math.max(0, nm.startCol - 1)\n if (dc === 1) pr.endCol = Math.min(this.leafCols.length - 1, nm.endCol + 1)\n this.selectionRanges = [pr]\n this.emitSelectionChange()\n } else {\n this.moveActive(dr, dc, e)\n }\n return\n }\n\n if (key === 'Home') {\n e.preventDefault()\n if (e.ctrlKey) {\n this.selectCell(0, 0)\n } else {\n this.selectCell(this.activeCell.row, 0)\n }\n return\n }\n if (key === 'End') {\n e.preventDefault()\n if (e.ctrlKey) {\n this.selectCell(this.totalRows - 1, this.leafCols.length - 1)\n } else {\n this.selectCell(this.activeCell.row, this.leafCols.length - 1)\n }\n return\n }\n if (key === 'PageUp') {\n e.preventDefault()\n const visibleRows = Math.floor((this.height - this._headerRowH * this.headerDepth) / this._rowH)\n this.moveActive(-visibleRows, 0, e)\n return\n }\n if (key === 'PageDown') {\n e.preventDefault()\n const visibleRows = Math.floor((this.height - this._headerRowH * this.headerDepth) / this._rowH)\n this.moveActive(visibleRows, 0, e)\n return\n }\n },\n\n onKeyPress(e) {\n if (this.editingCell) return\n if (this.selectDropdown) return\n if (e.ctrlKey || e.metaKey || e.altKey) return\n if (e.key && e.key.length === 1) {\n const activeCol = this.leafCols[this.activeCell.col]\n if (activeCol && this.isSelectLikeCol(activeCol) && !this._hasCustomEdit(activeCol)) {\n e.preventDefault()\n this.openSelectDropdownForActive()\n return\n }\n if (activeCol && activeCol.type === 'checkbox') {\n e.preventDefault()\n return\n }\n this.startEditing(this.activeCell.row, this.activeCell.col, e.key, false)\n }\n },\n\n onGlobalKeyDown(e) {\n // Handled by focused wrapper\n },\n\n moveActive(dr, dc, e) {\n let row = this.activeCell.row + dr\n let col = this.activeCell.col + dc\n\n // Tab: wrap around rows\n if (e && e.key === 'Tab') {\n if (col >= this.leafCols.length) { col = 0; row++ }\n if (col < 0) { col = this.leafCols.length - 1; row-- }\n }\n\n row = Math.max(0, Math.min(this.totalRows - 1, row))\n col = Math.max(0, Math.min(this.leafCols.length - 1, col))\n\n // Skip hidden cols\n while (this.isColHidden(col) && col > 0 && col < this.leafCols.length - 1) {\n col += dc >= 0 ? 1 : -1\n }\n // Skip hidden rows\n while (this.isRowHidden(row) && row > 0 && row < this.totalRows - 1) {\n row += dr >= 0 ? 1 : -1\n }\n\n // Handle merge navigation\n const resolved = resolveMergeAnchor(row, col, this.internalMerges)\n row = resolved.row\n col = resolved.col\n\n this.activeCell = { row, col }\n this.selectionRanges = [singleCellRange(row, col)]\n this.emitSelectionChange()\n this.scrollToCell(row, col)\n },\n\n scrollToCell(ri, ci) {\n const scrollBody = this.$refs.scrollBody\n if (!scrollBody) return\n const rowTop = getRowTop(ri, this._rowH, this.rowHeights)\n const rowBottom = rowTop + this.getRowH(ri)\n const colLeft = this.getColLeft(ci) - this.getFrozenWidth()\n const colRight = colLeft + this.getColW(ci)\n\n if (rowTop < scrollBody.scrollTop) scrollBody.scrollTop = rowTop\n if (rowBottom > scrollBody.scrollTop + scrollBody.clientHeight) {\n scrollBody.scrollTop = rowBottom - scrollBody.clientHeight\n }\n if (colLeft < scrollBody.scrollLeft) scrollBody.scrollLeft = colLeft\n if (colRight > scrollBody.scrollLeft + scrollBody.clientWidth) {\n scrollBody.scrollLeft = colRight - scrollBody.clientWidth\n }\n },\n\n // =========================================================\n // EDITING (used by 3.1 + 3.2)\n // =========================================================\n startEditing(ri, ci, initialChar = null, keepValue = false) {\n const col = this.leafCols[ci]\n if (!col || !this.isCellEditable(ri, ci)) return\n if (ri < 0 || ri >= this.totalRows) return\n if (!this._hasCustomEdit(col)) {\n if (this.isSelectLikeCol(col)) { this.openSelectDropdownForActive(); return }\n if (col.type === 'checkbox') return\n }\n\n this.invalidCell = null\n this.editingCell = { row: ri, col: ci }\n if (initialChar !== null) {\n this.editValue = initialChar\n } else if (keepValue) {\n this.editValue = String(this.getCellValue(ri, ci) || '')\n } else {\n this.editValue = ''\n }\n this.editOriginalValue = String(this.getCellValue(ri, ci) || '')\n\n this.$nextTick(() => {\n const inputs = this.$refs.editInput\n if (inputs) {\n const input = Array.isArray(inputs) ? inputs[0] : inputs\n if (input) {\n input.focus()\n if (keepValue) {\n input.setSelectionRange(input.value.length, input.value.length)\n }\n }\n }\n })\n },\n\n commitEdit() {\n if (!this.editingCell) return\n const { row, col } = this.editingCell\n const colDef = this.leafCols[col]\n if (!colDef) { this.editingCell = null; return }\n\n if (!this.isCellValueValid(row, col, this.editValue)) {\n this._showInvalidCell(row, col, this.editValue)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n return\n }\n\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[row] = { ...oldData[row], [colDef.key]: this.editValue }\n this._emitDataChange(oldData, newData)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n cancelEdit() {\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n hasCellSlot(col) {\n return !!(col && col.key && this.$scopedSlots && this.$scopedSlots['cell-' + col.key])\n },\n hasLegacyEditSlot(col) {\n return !!(col && col.key && this.$scopedSlots && this.$scopedSlots['edit-' + col.key])\n },\n _hasCustomEdit(col) {\n // For select/treeSelect: cell-{key} slot only customizes display; dropdown still opens normally\n if (col.type === 'select' || col.type === 'treeSelect') {\n return this.hasLegacyEditSlot(col)\n }\n return this.hasCellSlot(col) || this.hasLegacyEditSlot(col)\n },\n\n commitEditSlot(value) {\n if (!this.editingCell) return\n const { row, col } = this.editingCell\n const colDef = this.leafCols[col]\n if (!colDef) { this.editingCell = null; return }\n if (!this.isCellValueValid(row, col, value)) {\n this._showInvalidCell(row, col, value)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n return\n }\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[row] = { ...oldData[row], [colDef.key]: value }\n this._emitDataChange(oldData, newData)\n this.editingCell = null\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n // =========================================================\n // CHECKBOX CELLS\n // =========================================================\n\n isCheckboxChecked(ri, ci) {\n const v = this.getCellValue(ri, ci)\n if (v === true || v === 1) return true\n if (typeof v === 'string') return ['true', '1'].includes(v.trim().toLowerCase())\n return false\n },\n\n setCheckboxValue(ri, ci, value) {\n const col = this.leafCols[ci]\n if (!col || !this.isCellEditable(ri, ci)) return\n if (!this.isCellValueValid(ri, ci, value)) {\n this._showInvalidCell(ri, ci, value)\n return\n }\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[ri] = { ...oldData[ri], [col.key]: value }\n this._emitDataChange(oldData, newData)\n },\n\n toggleCheckbox(ri, ci) {\n this.setCheckboxValue(ri, ci, !this.isCheckboxChecked(ri, ci))\n },\n\n // =========================================================\n // CELL EDITABILITY (3.17)\n // =========================================================\n\n isCellEditable(ri, ci) {\n const col = this.leafCols[ci]\n if (!col) return false\n const row = this._dataRaw[ri] || {}\n if (typeof this.cellEditable === 'function') return !!this.cellEditable(row, ri, col, ci)\n if (typeof col.editable === 'function') return !!col.editable(row, ri)\n if (col.editable === false) return false\n return true\n },\n\n // =========================================================\n // CELL VALUE VALIDATION (cellValidator)\n // =========================================================\n\n isCellValueValid(ri, ci, value) {\n if (typeof this.cellValidator !== 'function') return true\n const col = this.leafCols[ci]\n if (!col) return true\n const row = this._dataRaw[ri] || {}\n return !!this.cellValidator(value, row, ri, col, ci)\n },\n\n _resolveValidationMessage(ri, ci, value) {\n const col = this.leafCols[ci]\n const row = this._dataRaw[ri] || {}\n if (typeof this.cellValidationMessage === 'function') return this.cellValidationMessage(value, row, ri, col, ci)\n if (typeof this.cellValidationMessage === 'string' && this.cellValidationMessage) return this.cellValidationMessage\n return this.t.invalidValue\n },\n\n // Find the on-screen rect of the cell currently being committed — the edit\n // input if still mounted (commitEdit, before editingCell is cleared), else\n // the active cell element (checkbox/select commits, which never enter edit mode).\n _invalidCellRect() {\n const inputs = this.$refs.editInput\n const input = Array.isArray(inputs) ? inputs[0] : inputs\n if (input) return input.getBoundingClientRect()\n const el = this.$el ? this.$el.querySelector('.eg-cell-active') : null\n return el ? el.getBoundingClientRect() : null\n },\n\n _showInvalidCell(ri, ci, value) {\n const message = this._resolveValidationMessage(ri, ci, value)\n this.invalidCell = { row: ri, col: ci, message, rect: this._invalidCellRect() }\n this.$emit('cell-validation-error', { row: ri, col: ci, value, message })\n },\n\n // Resolve+emit messages for a batch of rejected { row, col, value } entries\n // from a bulk write (paste / fill / clear). blocked=true means the whole\n // operation was aborted (strictBulkValidation); false means these cells\n // were skipped but the rest of the write still applied.\n _emitBulkValidationError(skipped, blocked) {\n const cells = skipped.map(c => ({\n row: c.row, col: c.col, value: c.value,\n message: this._resolveValidationMessage(c.row, c.col, c.value)\n }))\n this.$emit('bulk-validation-error', { blocked, cells })\n },\n\n // =========================================================\n // SELECT DROPDOWN (3.16) + TREE SELECT (3.22)\n // =========================================================\n\n isSelectLikeCol(col) {\n return !!(col && (col.type === 'select' || col.type === 'treeSelect'))\n },\n\n treeOptionKeys(col) {\n return {\n valueKey: col.optionValueKey || 'value',\n labelKey: col.optionLabelKey || 'label',\n childrenKey: col.optionChildrenKey || 'children'\n }\n },\n\n _treeFindLabel(nodes, keys, value) {\n for (const node of (nodes || [])) {\n if (String(node[keys.valueKey]) === value) {\n return node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey])\n }\n const found = this._treeFindLabel(node[keys.childrenKey], keys, value)\n if (found != null) return found\n }\n return null\n },\n\n colOptions(ci) {\n const col = this.leafCols[ci]\n if (!col || !Array.isArray(col.options)) return []\n return col.options.map(o =>\n typeof o === 'string' ? { value: o, label: o } : { value: o.value, label: o.label != null ? o.label : String(o.value), color: o.color }\n )\n },\n\n getSelectLabel(ci, value) {\n const col = this.leafCols[ci]\n const v = value == null ? '' : String(value)\n if (col && col.type === 'treeSelect') {\n if (!Array.isArray(col.options)) return v\n const found = this._treeFindLabel(col.options, this.treeOptionKeys(col), v)\n return found != null ? found : v\n }\n const opts = this.colOptions(ci)\n if (!opts.length) return v\n const found = opts.find(o => String(o.value) === v)\n return found ? found.label : v\n },\n\n _buildSelectDropdownState(ri, ci, col, currentValue, rect) {\n const rectObj = { top: rect.top, bottom: rect.bottom, left: rect.left, width: rect.width }\n return {\n row: ri, col: ci,\n tree: col.type === 'treeSelect',\n keys: col.type === 'treeSelect' ? this.treeOptionKeys(col) : null,\n rawOptions: col.options || [],\n currentValue,\n rect: rectObj,\n enableSearch: !!col.enableSearch\n }\n },\n\n openSelectDropdown(ri, ci, e) {\n const col = this.leafCols[ci]\n if (!col || !Array.isArray(col.options)) return\n const el = e.currentTarget\n const rect = el.getBoundingClientRect()\n const currentValue = String(this.getCellValue(ri, ci) != null ? this.getCellValue(ri, ci) : '')\n this.selectDropdown = this._buildSelectDropdownState(ri, ci, col, currentValue, rect)\n },\n\n openSelectDropdownForActive() {\n const { row, col } = this.activeCell\n const colDef = this.leafCols[col]\n if (!colDef || !Array.isArray(colDef.options)) return\n this.$nextTick(() => {\n const el = this.$el ? this.$el.querySelector('.eg-cell-active') : null\n if (!el) return\n const rect = el.getBoundingClientRect()\n const currentValue = String(this.getCellValue(row, col) != null ? this.getCellValue(row, col) : '')\n this.selectDropdown = this._buildSelectDropdownState(row, col, colDef, currentValue, rect)\n })\n },\n\n onComponentSelect({ value, label }) {\n if (!this.selectDropdown) return\n const { row, col } = this.selectDropdown\n const colDef = this.leafCols[col]\n if (colDef) {\n if (!this.isCellValueValid(row, col, value)) {\n this._showInvalidCell(row, col, value)\n this.closeSelectDropdown()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n return\n }\n this.invalidCell = null\n const oldData = this.data\n const newData = oldData.slice()\n newData[row] = { ...oldData[row], [colDef.key]: value }\n this._emitDataChange(oldData, newData)\n if (typeof colDef.onSelect === 'function') {\n colDef.onSelect({ value, label }, newData[row], row, colDef)\n }\n }\n this.closeSelectDropdown()\n this.$refs.wrapper && this.$refs.wrapper.focus()\n },\n\n closeSelectDropdown() {\n this.selectDropdown = null\n },\n\n _onWindowScrollForDropdown(e) {\n if (!this.selectDropdown) return\n const comp = this.$refs.selectDropdownComp\n const dd = comp && comp.$el ? comp.$el : null\n if (dd && dd.contains(e.target)) return\n this.closeSelectDropdown()\n },\n\n _onDocMouseDownForDropdown(e) {\n if (!this.selectDropdown) return\n const comp = this.$refs.selectDropdownComp\n const dd = comp && comp.$el ? comp.$el : null\n if (dd && dd.contains(e.target)) return\n this.closeSelectDropdown()\n },\n\n onEditKeyDown(e) {\n if (e.key === 'Escape') {\n e.preventDefault()\n this.cancelEdit()\n } else if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n this.commitEdit()\n this.moveActive(1, 0, e)\n } else if (e.key === 'Tab') {\n e.preventDefault()\n this.commitEdit()\n this.moveActive(0, e.shiftKey ? -1 : 1, e)\n }\n },\n\n clearSelection() {\n if (!this.selectionRanges.length) return\n const oldData = this.data\n const newData = oldData.slice()\n let hasChanges = false\n const skipped = []\n for (const range of this.selectionRanges) {\n const r = {\n startRow: Math.min(range.startRow, range.endRow),\n endRow: Math.max(range.startRow, range.endRow),\n startCol: Math.min(range.startCol, range.endCol),\n endCol: Math.max(range.startCol, range.endCol)\n }\n for (let ri = r.startRow; ri <= r.endRow && ri < newData.length; ri++) {\n let rowUpdated = null\n for (let ci = r.startCol; ci <= r.endCol; ci++) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, '')) { skipped.push({ row: ri, col: ci, value: '' }); continue }\n if (!rowUpdated) rowUpdated = { ...newData[ri] }\n rowUpdated[col.key] = ''\n hasChanges = true\n }\n }\n if (rowUpdated) newData[ri] = rowUpdated\n }\n }\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n return\n }\n if (hasChanges) this._emitDataChange(oldData, newData)\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n },\n\n // =========================================================\n // FILL HANDLE (3.3)\n // =========================================================\n startFillDrag(e) {\n const pr = this.primaryRange\n if (!pr) return\n this.fillDrag.active = true\n this.fillDrag.sourceRange = normalizeRange(pr)\n this.fillDrag.currentRow = pr.endRow\n this.fillDrag.currentCol = pr.endCol\n e.preventDefault()\n },\n\n onGlobalMouseMove(e) {\n if (this.resizing.active) {\n this.handleResizeMove(e)\n return\n }\n if (this.fillDrag.active) {\n this.handleFillDragMove(e)\n return\n }\n if (this.dragMove.active) {\n this.handleDragMoveMove(e)\n return\n }\n },\n\n onGlobalMouseUp(e) {\n if (this.resizing.active) {\n this.commitResize()\n return\n }\n if (this.fillDrag.active) {\n this.commitFillDrag()\n return\n }\n if (this.dragMove.active) {\n this.commitDragMove(e)\n return\n }\n this.isMouseDown = false\n },\n\n handleFillDragMove(e) {\n const scrollBody = this.$refs.scrollBody\n if (!scrollBody) return\n const rect = scrollBody.getBoundingClientRect()\n const relY = e.clientY - rect.top + scrollBody.scrollTop\n const relX = e.clientX - rect.left + scrollBody.scrollLeft\n\n // Binary search on cumulativeHeights — O(log n) instead of O(n)\n const cum = this.cumulativeHeights\n const total = this.totalRows\n let lo = 0, hi = total - 1\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (cum[mid + 1] <= relY) lo = mid + 1\n else hi = mid\n }\n const row = Math.min(lo, total - 1)\n\n // Columns are few — O(columns) is fast\n let left = this.getFrozenWidth()\n let col = 0\n for (let i = 0; i < this.leafCols.length; i++) {\n if (!this.isColumnScrollable(i)) continue\n left += this.getColW(i)\n if (relX < left) { col = i; break }\n col = i\n }\n\n this.fillDrag.currentRow = row\n this.fillDrag.currentCol = col\n },\n\n commitFillDrag() {\n const src = this.fillDrag.sourceRange\n if (!src) { this.fillDrag.active = false; return }\n\n const targetRow = this.fillDrag.currentRow\n const targetCol = this.fillDrag.currentCol\n\n const oldData = this.data\n const newData = oldData.slice() // only affected rows get new objects\n let targetRange\n const skipped = []\n\n if (targetRow > src.endRow) {\n // Fill down\n const count = targetRow - src.endRow\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n if (!col) continue\n const srcValues = []\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n srcValues.push(oldData[ri] ? oldData[ri][col.key] : '')\n }\n const filled = generateFillValues(srcValues, count)\n for (let k = 0; k < count; k++) {\n const ri = src.endRow + 1 + k\n if (ri < this.totalRows && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, filled[k])) { skipped.push({ row: ri, col: ci, value: filled[k] }); continue }\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n newData[ri][col.key] = filled[k]\n }\n }\n }\n targetRange = { startRow: src.endRow + 1, endRow: targetRow, startCol: src.startCol, endCol: src.endCol }\n } else if (targetCol > src.endCol) {\n // Fill right\n const count = targetCol - src.endCol\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n const srcValues = []\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n srcValues.push(col && oldData[ri] ? oldData[ri][col.key] : '')\n }\n const filled = generateFillValues(srcValues, count)\n for (let k = 0; k < count; k++) {\n const ci = src.endCol + 1 + k\n if (ci < this.leafCols.length) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, filled[k])) { skipped.push({ row: ri, col: ci, value: filled[k] }); continue }\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n newData[ri][col.key] = filled[k]\n }\n }\n }\n }\n targetRange = { startRow: src.startRow, endRow: src.endRow, startCol: src.endCol + 1, endCol: targetCol }\n }\n\n if (targetRange) {\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n } else {\n this._emitDataChange(oldData, newData)\n this.$emit('fill', { sourceRange: src, targetRange, values: newData })\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n }\n }\n\n this.fillDrag.active = false\n this.fillDrag.sourceRange = null\n },\n\n doubleFillDown() {\n const pr = this.primaryRange\n if (!pr) return\n const src = normalizeRange(pr)\n // Find adjacent column filled length\n const adjCol = src.startCol > 0 ? src.startCol - 1 : src.endCol + 1\n if (adjCol < 0 || adjCol >= this.leafCols.length) return\n const adjColDef = this.leafCols[adjCol]\n if (!adjColDef) return\n let lastFilledRow = src.endRow\n for (let ri = src.endRow + 1; ri < this.totalRows; ri++) {\n if (this.data[ri] && this.data[ri][adjColDef.key] !== '' && this.data[ri][adjColDef.key] !== undefined) {\n lastFilledRow = ri\n } else break\n }\n if (lastFilledRow <= src.endRow) return\n\n const count = lastFilledRow - src.endRow\n const oldData = this.data\n const newData = oldData.slice()\n const skipped = []\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n if (!col) continue\n const srcValues = []\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n srcValues.push(oldData[ri] ? oldData[ri][col.key] : '')\n }\n const filled = generateFillValues(srcValues, count)\n for (let k = 0; k < count; k++) {\n const ri = src.endRow + 1 + k\n if (ri < this.totalRows && this.isCellEditable(ri, ci)) {\n if (!this.isCellValueValid(ri, ci, filled[k])) { skipped.push({ row: ri, col: ci, value: filled[k] }); continue }\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n newData[ri][col.key] = filled[k]\n }\n }\n }\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n return\n }\n this._emitDataChange(oldData, newData)\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n },\n\n // =========================================================\n // COPY / PASTE (3.4)\n // =========================================================\n copySelection() {\n const pr = this.primaryRange\n if (!pr) return\n const r = normalizeRange(pr)\n this.clipboardMatrix = extractMatrix(this.data, this.leafCols, r)\n this.clipboardRange = r\n this.clipboardMode = 'copy'\n\n // Write to system clipboard as TSV\n const tsv = matrixToTSV(this.clipboardMatrix)\n if (navigator.clipboard) {\n navigator.clipboard.writeText(tsv).catch(() => {})\n }\n },\n\n cutSelection() {\n this.copySelection()\n this.clipboardMode = 'cut'\n },\n\n async pasteAt(targetRow, targetCol, options = {}) {\n const { mode = 'all', transpose = false } = options\n\n let matrix = null\n\n // Try system clipboard first\n if (navigator.clipboard) {\n try {\n const text = await navigator.clipboard.readText()\n if (text) matrix = tsvToMatrix(text)\n } catch (_) {}\n }\n\n // Fall back to internal clipboard\n if (!matrix && this.clipboardMatrix) {\n matrix = this.clipboardMatrix\n }\n\n if (!matrix) return\n\n if (transpose) matrix = transposeMatrix(matrix)\n\n // ── Excel-style selection broadcast / tiling ──────────────────────────\n // When pasting over a multi-cell selection and the clipboard matrix is\n // smaller than the selection, tile the matrix to fill the selection —\n // provided the selection dimensions are exact multiples of the matrix\n // dimensions (matches Excel's tiling rule exactly).\n // Cut-paste is never tiled (source must be cleared exactly once).\n const isCopyPaste = !this.clipboardMode || this.clipboardMode === 'copy'\n if (isCopyPaste) {\n const pr = this.primaryRange\n if (pr) {\n const sel = normalizeRange(pr)\n const selRows = sel.endRow - sel.startRow + 1\n const selCols = sel.endCol - sel.startCol + 1\n const matRows = matrix.length\n const matCols = matrix[0] ? matrix[0].length : 0\n\n // Only tile when selection is strictly larger than clipboard\n if ((selRows > matRows || selCols > matCols) &&\n selRows % matRows === 0 && selCols % matCols === 0) {\n matrix = tileMatrix(matrix, selRows, selCols)\n // Paste at selection origin, not just at the active cell\n targetRow = sel.startRow\n targetCol = sel.startCol\n }\n }\n }\n\n const oldData = this.data\n const { newData, affectedRange, skipped } = applyMatrix(\n oldData, this.leafCols, targetRow, targetCol, matrix, mode,\n (ri, ci) => this.isCellEditable(ri, ci),\n (ri, ci, v) => this.isCellValueValid(ri, ci, v)\n )\n\n if (this.strictBulkValidation && skipped.length > 0) {\n this._emitBulkValidationError(skipped, true)\n return\n }\n\n if (this.clipboardMode === 'cut' && this.clipboardRange) {\n // Clear source — ensure each source row is a new copy before mutation\n const src = this.clipboardRange\n for (let ri = src.startRow; ri <= src.endRow; ri++) {\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n for (let ci = src.startCol; ci <= src.endCol; ci++) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci) && this.isCellValueValid(ri, ci, '')) {\n newData[ri][col.key] = ''\n }\n }\n }\n this.clipboardRange = null\n this.clipboardMode = null\n }\n\n this._emitDataChange(oldData, newData)\n if (skipped.length > 0) this._emitBulkValidationError(skipped, false)\n },\n\n ctxCopy() { this.hideContextMenu(); this.copySelection() },\n ctxCut() { this.hideContextMenu(); this.cutSelection() },\n ctxPaste() { this.hideContextMenu(); this.pasteAt(this.activeCell.row, this.activeCell.col) },\n ctxPasteSpecial(mode, transpose = false) {\n this.hideContextMenu()\n this.pasteAt(this.activeCell.row, this.activeCell.col, { mode, transpose })\n },\n\n // =========================================================\n // RESIZE (3.5)\n // =========================================================\n startColResize(e, ci) {\n const rect = this.$refs.wrapper.getBoundingClientRect()\n this.resizing = {\n active: true,\n type: 'col',\n index: ci,\n startX: e.clientX,\n startY: e.clientY,\n startSize: this.getColW(ci),\n currentPos: e.clientX\n }\n e.preventDefault()\n },\n\n startRowResize(e, ri) {\n this.resizing = {\n active: true,\n type: 'row',\n index: ri,\n startX: e.clientX,\n startY: e.clientY,\n startSize: this.getRowH(ri),\n currentPos: e.clientY\n }\n e.preventDefault()\n },\n\n handleResizeMove(e) {\n if (this.resizing.type === 'col') {\n this.resizing.currentPos = e.clientX\n const newWidth = Math.max(20, this.resizing.startSize + (e.clientX - this.resizing.startX))\n this.$set(this.colWidths, this.resizing.index, newWidth)\n this._cellStyleCache = Object.create(null)\n } else {\n this.resizing.currentPos = e.clientY\n this._resizePreviewHeight = Math.max(10, this.resizing.startSize + (e.clientY - this.resizing.startY))\n // Batch $set + updateVirtualScroll to rAF — prevents O(n) cumulativeHeights rebuild\n // on every mousemove (200+ Hz). At most 60 rebuilds/sec instead of 200+.\n if (!this._resizeRAF) {\n this._resizeRAF = requestAnimationFrame(() => {\n this._resizeRAF = null\n this._rowStyleCache = Object.create(null)\n this._rowHeaderStyleCache = Object.create(null)\n this._cellStyleCache = Object.create(null)\n this.$set(this.rowHeights, this.resizing.index, this._resizePreviewHeight)\n this.updateVirtualScroll()\n })\n }\n }\n },\n\n commitResize() {\n if (this._resizeRAF) {\n cancelAnimationFrame(this._resizeRAF)\n this._resizeRAF = null\n // Apply the last pending preview height immediately on commit\n if (this.resizing.type === 'row' && this._resizePreviewHeight !== undefined) {\n this.$set(this.rowHeights, this.resizing.index, this._resizePreviewHeight)\n }\n }\n if (this.resizing.type === 'col') {\n this._cellStyleCache = Object.create(null)\n const newSize = this.colWidths[this.resizing.index]\n this.$emit('column-resize', { index: this.resizing.index, newSize })\n } else {\n this._rowStyleCache = Object.create(null)\n this._rowHeaderStyleCache = Object.create(null)\n this._cellStyleCache = Object.create(null)\n const newSize = this.rowHeights[this.resizing.index]\n this.$emit('row-resize', { index: this.resizing.index, newSize })\n this.updateVirtualScroll()\n }\n this.resizing.active = false\n },\n\n autoFitColumn(ci) {\n const col = this.leafCols[ci]\n if (!col) return\n let maxLen = (col.title || '').length\n for (let ri = 0; ri < Math.min(this.totalRows, 500); ri++) {\n const v = this.data[ri] ? String(this.data[ri][col.key] || '') : ''\n if (v.length > maxLen) maxLen = v.length\n }\n const newWidth = Math.max(50, maxLen * 8 + 16)\n this.$set(this.colWidths, ci, newWidth)\n this._cellStyleCache = Object.create(null)\n this.$emit('column-resize', { index: ci, newSize: newWidth })\n },\n\n _applyAutoWidth() {\n if (!this.autoWidth || !this.leafCols.length) return\n this.leafCols.forEach((col, ci) => {\n let maxLen = (col.title || '').length\n for (let ri = 0; ri < Math.min(this.totalRows, 500); ri++) {\n const v = this.data[ri] ? String(this.data[ri][col.key] || '') : ''\n if (v.length > maxLen) maxLen = v.length\n }\n this.$set(this.colWidths, ci, Math.max(50, maxLen * 8 + 16))\n })\n this._cellStyleCache = Object.create(null)\n },\n\n ctxHideRow() {\n this.hideContextMenu()\n const pr = this.primaryRange\n if (!pr) return\n const r = normalizeRange(pr)\n const newHidden = [...this.hiddenRows]\n for (let ri = r.startRow; ri <= r.endRow; ri++) {\n if (!newHidden.includes(ri)) newHidden.push(ri)\n }\n this.$emit('update:hiddenRows', newHidden)\n },\n\n ctxHideColumn() {\n this.hideContextMenu()\n const pr = this.primaryRange\n if (!pr) return\n const r = normalizeRange(pr)\n const newHidden = [...this.hiddenColumns]\n for (let ci = r.startCol; ci <= r.endCol; ci++) {\n if (!newHidden.includes(ci)) newHidden.push(ci)\n }\n this.$emit('update:hiddenColumns', newHidden)\n },\n\n ctxUnhideRows() {\n this.hideContextMenu()\n this.$emit('update:hiddenRows', [])\n },\n\n ctxUnhideColumns() {\n this.hideContextMenu()\n this.$emit('update:hiddenColumns', [])\n },\n\n ctxPinLeft() {\n const col = this.leafCols[this.contextMenu.col]\n if (!col) { this.hideContextMenu(); return }\n this.$emit('update:pinnedColumns', { ...this.pinnedColumns, [col.key]: 'left' })\n this.hideContextMenu()\n },\n\n ctxPinRight() {\n const col = this.leafCols[this.contextMenu.col]\n if (!col) { this.hideContextMenu(); return }\n this.$emit('update:pinnedColumns', { ...this.pinnedColumns, [col.key]: 'right' })\n this.hideContextMenu()\n },\n\n ctxUnpin() {\n const col = this.leafCols[this.contextMenu.col]\n if (!col) { this.hideContextMenu(); return }\n const next = { ...this.pinnedColumns }\n delete next[col.key]\n this.$emit('update:pinnedColumns', next)\n this.hideContextMenu()\n },\n\n isColPinned(ci) {\n const col = this.leafCols[ci]\n return !!(col && this.pinnedColumns[col.key])\n },\n\n // =========================================================\n // MERGE CELLS (3.8)\n // =========================================================\n mergeCells(range) {\n const oldMerges = this.internalMerges.slice()\n const { merges: newMerges, conflict } = addMerge(range, this.internalMerges)\n if (conflict) {\n this.$emit('merge-error', { range, reason: 'Overlaps existing merge' })\n return false\n }\n this.internalMerges = newMerges\n this._emitMergeChange(oldMerges, [...newMerges])\n return true\n },\n\n unmergeCells(row, col) {\n const oldMerges = this.internalMerges.slice()\n const newMerges = removeMergeAt(row, col, this.internalMerges)\n this.internalMerges = newMerges\n this._emitMergeChange(oldMerges, [...newMerges])\n },\n\n ctxMerge() {\n this.hideContextMenu()\n const pr = this.primaryRange\n if (!pr) return\n this.mergeCells(normalizeRange(pr))\n },\n\n ctxUnmerge() {\n this.hideContextMenu()\n this.unmergeCells(this.activeCell.row, this.activeCell.col)\n },\n\n // =========================================================\n // GROUPING (3.7)\n // =========================================================\n groupRows(startIndex, endIndex) {\n this.internalGroups = addGroup('row', startIndex, endIndex, this.internalGroups)\n },\n\n groupColumns(startIndex, endIndex) {\n this.internalGroups = addGroup('column', startIndex, endIndex, this.internalGroups)\n },\n\n ungroupRows(startIndex, endIndex) {\n this.internalGroups = removeGroup('row', startIndex, endIndex, this.internalGroups)\n },\n\n ungroupColumns(startIndex, endIndex) {\n this.internalGroups = removeGroup('column', startIndex, endIndex, this.internalGroups)\n },\n\n toggleGroup(group) {\n this.internalCollapsedGroups = toggleGroupCollapse(group, this.internalCollapsedGroups)\n this.$emit('update:collapsedGroups', [...this.internalCollapsedGroups])\n this.$emit('group-toggle', { group, collapsed: _isGroupCollapsed(group, this.internalCollapsedGroups) })\n this.updateVirtualScroll()\n },\n\n isGroupCollapsed(group) {\n return _isGroupCollapsed(group, this.internalCollapsedGroups)\n },\n\n expandAll() {\n this.internalCollapsedGroups = []\n this.$emit('update:collapsedGroups', [])\n this.updateVirtualScroll()\n },\n\n collapseAll() {\n this.internalCollapsedGroups = [...this.internalGroups]\n this.$emit('update:collapsedGroups', [...this.internalGroups])\n this.updateVirtualScroll()\n },\n\n // =========================================================\n // DRAG AND DROP (3.9)\n // =========================================================\n startDragMove(e, ri, ci) {\n const pr = this.primaryRange\n if (!pr) return\n this.dragMove = {\n active: true,\n sourceRange: normalizeRange(pr),\n startX: e.clientX,\n startY: e.clientY,\n targetRow: ri,\n targetCol: ci,\n copyMode: e.ctrlKey || e.metaKey\n }\n this.$emit('cell-drag-start', { sourceRange: this.dragMove.sourceRange })\n e.preventDefault()\n },\n\n handleDragMoveMove(e) {\n this.dragMove.copyMode = e.ctrlKey || e.metaKey\n this.$emit('cell-drag-move', {\n sourceRange: this.dragMove.sourceRange,\n targetRow: this.dragMove.targetRow,\n targetCol: this.dragMove.targetCol\n })\n },\n\n async commitDragMove(e) {\n if (!this.dragMove.active) return\n const { sourceRange, targetRow, targetCol, copyMode } = this.dragMove\n\n const targetRange = {\n startRow: targetRow,\n startCol: targetCol,\n endRow: targetRow + (sourceRange.endRow - sourceRange.startRow),\n endCol: targetCol + (sourceRange.endCol - sourceRange.startCol)\n }\n\n let proceed = true\n if (this.beforeDrop) {\n proceed = await Promise.resolve(this.beforeDrop(sourceRange, targetRange))\n }\n\n if (proceed) {\n const oldData = this.data\n const matrix = extractMatrix(oldData, this.leafCols, sourceRange)\n const { newData } = applyMatrix(oldData, this.leafCols, targetRow, targetCol, matrix, 'all', (ri, ci) => this.isCellEditable(ri, ci))\n\n if (!copyMode) {\n // Clear source — ensure each source row is a new copy before mutation\n for (let ri = sourceRange.startRow; ri <= sourceRange.endRow; ri++) {\n if (newData[ri] === oldData[ri]) newData[ri] = { ...oldData[ri] }\n for (let ci = sourceRange.startCol; ci <= sourceRange.endCol; ci++) {\n const col = this.leafCols[ci]\n if (col && this.isCellEditable(ri, ci)) newData[ri][col.key] = ''\n }\n }\n }\n this._emitDataChange(oldData, newData)\n this.$emit('cell-drop', { sourceRange, targetRange })\n }\n\n this.dragMove = { active: false, sourceRange: null, startX: 0, startY: 0, targetRow: -1, targetCol: -1, copyMode: false }\n this.isMouseDown = false\n },\n\n // =========================================================\n // EXCEL EXPORT (3.11)\n // =========================================================\n async exportToExcel(options = {}) {\n const {\n filename = 'export.xlsx',\n sheetName = 'Sheet1',\n scope = 'all',\n includeHidden = false\n } = options\n\n this.$emit('export-start')\n this.exportState = { active: true }\n\n const workerPayload = {\n data: this.data,\n columns: this.columns,\n leafCols: this.leafCols,\n merges: this.internalMerges,\n hiddenCols: [...this.effectiveHiddenColSet],\n hiddenRows: [...this.hiddenRowSet],\n range: scope === 'selection' ? this.primaryRange : null,\n filename,\n sheetName,\n scope,\n includeHidden\n }\n\n try {\n const worker = createExcelExportWorker()\n if (worker) {\n await this._exportViaWorker(worker, workerPayload)\n } else {\n // Fallback: synchronous export on main thread\n _exportToExcel({\n ...workerPayload,\n hiddenColSet: this.effectiveHiddenColSet,\n hiddenRowSet: this.hiddenRowSet\n })\n }\n this.$emit('export-success')\n } catch (err) {\n this.$emit('export-error', err)\n } finally {\n this.exportState = { active: false }\n }\n },\n\n _exportViaWorker(worker, payload) {\n return new Promise((resolve, reject) => {\n let done = false\n const finish = (fn, val) => {\n if (done) return\n done = true\n worker.terminate()\n fn(val)\n }\n\n worker.onmessage = (e) => {\n const { type, buffer, filename, message } = e.data\n if (type === 'result') {\n // Trigger browser download from the main thread (DOM required)\n const blob = new Blob([new Uint8Array(buffer)], {\n type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n finish(resolve)\n } else if (type === 'error') {\n finish(reject, new Error(message))\n }\n }\n\n worker.onerror = (err) => finish(reject, new Error(err.message || 'Export worker xatosi'))\n\n worker.postMessage(payload)\n })\n },\n\n // =========================================================\n // EXCEL IMPORT (3.12)\n // =========================================================\n async importFromFile(file, options = {}) {\n this.importState = { active: true, percent: 5, message: \"Fayl o'qilmoqda...\" }\n\n try {\n let result\n const worker = createExcelImportWorker()\n\n if (worker) {\n result = await this._importViaWorker(worker, file, options)\n } else {\n // Fallback: chunked async on main thread\n result = await _importFromFile(\n file,\n this.columns,\n options,\n (percent, message) => { this.importState = { active: true, percent, message } }\n )\n }\n\n const { columns, merges } = result\n // Freeze each row so Vue 2 skips deep Object.defineProperty observation.\n // Vue 2 checks Object.isExtensible() — frozen objects are skipped entirely.\n // All edit paths already use spread ({ ...row }) so frozen rows are safe.\n const data = result.data.map(row => Object.freeze(row))\n this.$emit('input', data)\n this.$emit('update:columns', columns)\n\n const hasMerges = merges && merges.length > 0\n this.internalMerges = hasMerges ? merges : []\n this.$emit('update:merges', hasMerges ? merges : [])\n this.$emit('update:collapsedGroups', [])\n this.$emit('update:hiddenRows', [])\n this.$emit('import-success', { data, columns, merges })\n\n // Reset scroll state and re-derive virtual scroll after DOM settles.\n // Without this, stale scrollTop + old rowHeights cause blank bands on scroll.\n this.rowHeights = {}\n this.scrollTop = 0\n this.scrollLeft = 0\n this.$nextTick(() => {\n if (this.$refs.scrollBody) {\n this.$refs.scrollBody.scrollTop = 0\n this.$refs.scrollBody.scrollLeft = 0\n }\n if (this.$refs.scrollColHeader) this.$refs.scrollColHeader.scrollLeft = 0\n if (this.$refs.scrollFooter) this.$refs.scrollFooter.scrollLeft = 0\n if (this.$refs.scrollRowHeaders) this.$refs.scrollRowHeaders.scrollTop = 0\n this.updateVirtualScroll()\n })\n } catch (err) {\n this.$emit('import-error', err)\n } finally {\n this.importState = { active: false, percent: 0, message: '' }\n }\n },\n\n _importViaWorker(worker, file, options) {\n return new Promise(async (resolve, reject) => {\n let done = false\n const finish = (fn, val) => {\n if (done) return\n done = true\n worker.terminate()\n fn(val)\n }\n\n worker.onmessage = (e) => {\n const { type, percent, message, data, columns, merges } = e.data\n if (type === 'progress') {\n this.importState = { active: true, percent, message }\n } else if (type === 'result') {\n finish(resolve, { data, columns, merges })\n } else if (type === 'error') {\n finish(reject, new Error(message))\n }\n }\n\n worker.onerror = (err) => finish(reject, new Error(err.message || 'Import worker xatosi'))\n\n try {\n // Transfer the ArrayBuffer zero-copy into the worker\n const buffer = await file.arrayBuffer()\n worker.postMessage(\n { buffer, existingColumns: this.columns, options },\n [buffer]\n )\n } catch (err) {\n finish(reject, err)\n }\n })\n },\n\n // =========================================================\n // UNDO / REDO (3.13)\n // =========================================================\n\n // Push one entry onto the non-reactive history stack.\n // Truncates the redo tail, caps at 100 entries.\n _pushHistory(entry) {\n if (this._historyIndex < this._history.length - 1) {\n this._history.splice(this._historyIndex + 1)\n }\n this._history.push(entry)\n if (this._history.length > 100) {\n this._history.shift()\n // historyIndex stays the same — it now points to the new last entry\n } else {\n this._historyIndex++\n }\n },\n\n // Emit 'input' and record a data diff entry (only changed row indices).\n // Use slice() + per-row spread in callers so unchanged rows keep the\n // same reference — then the identity check here catches only real changes.\n _emitDataChange(oldData, newData) {\n if (!this._historySkip) {\n const indices = []\n const beforeRows = []\n const afterRows = []\n const len = Math.max(oldData.length, newData.length)\n for (let i = 0; i < len; i++) {\n if (oldData[i] !== newData[i]) {\n indices.push(i)\n beforeRows.push(oldData[i])\n afterRows.push(newData[i])\n }\n }\n if (indices.length > 0) {\n this._pushHistory({\n type: 'data',\n before: { indices, rows: beforeRows },\n after: { indices, rows: afterRows }\n })\n }\n }\n this.$emit('input', newData)\n },\n\n // Emit 'update:merges' and record a merges diff entry.\n _emitMergeChange(oldMerges, newMerges) {\n if (!this._historySkip) {\n this._pushHistory({\n type: 'merges',\n before: oldMerges,\n after: newMerges\n })\n }\n this.$emit('update:merges', newMerges)\n },\n\n undo() {\n if (this._historyIndex < 0) return\n const entry = this._history[this._historyIndex--]\n this._historySkip = true\n try {\n if (entry.type === 'data') {\n const newData = this.data.slice()\n for (let i = 0; i < entry.before.indices.length; i++) {\n newData[entry.before.indices[i]] = entry.before.rows[i]\n }\n this.$emit('input', newData)\n } else if (entry.type === 'merges') {\n this.internalMerges = entry.before.slice()\n this.$emit('update:merges', entry.before.slice())\n }\n } finally {\n this._historySkip = false\n }\n },\n\n redo() {\n if (this._historyIndex >= this._history.length - 1) return\n const entry = this._history[++this._historyIndex]\n this._historySkip = true\n try {\n if (entry.type === 'data') {\n const newData = this.data.slice()\n for (let i = 0; i < entry.after.indices.length; i++) {\n newData[entry.after.indices[i]] = entry.after.rows[i]\n }\n this.$emit('input', newData)\n } else if (entry.type === 'merges') {\n this.internalMerges = entry.after.slice()\n this.$emit('update:merges', entry.after.slice())\n }\n } finally {\n this._historySkip = false\n }\n }\n }\n}\n<\/script>\n\n<style scoped>\n.eg-wrapper {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-sizing: border-box;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n}\n\n.eg-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.eg-header-area {\n display: flex;\n flex-shrink: 0;\n box-shadow: 0 2px 0 0 #d0d0d0;\n position: relative;\n z-index: 31;\n}\n\n.eg-header-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n border-right: 1px solid #ccc;\n border-bottom: 1px solid #ccc;\n background: #f0f0f0;\n font-size: 12px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n position: absolute;\n user-select: none;\n cursor: pointer;\n}\n\n.eg-header-cell:hover { background: #e0e0e0; }\n.eg-header-group { background: #e8eaf6; }\n\n.eg-col-resize-handle {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n width: 5px;\n cursor: col-resize;\n z-index: 5;\n}\n\n.eg-body-area {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n/* ===== FOOTER AREA (column totals) ===== */\n.eg-footer-area {\n border-top: 1px solid #ccc;\n}\n\n.eg-frozen-col-footer,\n.eg-scroll-col-footer,\n.eg-pinned-right-footer {\n position: relative;\n}\n\n.eg-footer-cell {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n font-weight: 600;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: #f8f9fa;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.eg-row {\n position: relative;\n}\n\n.eg-cell {\n box-sizing: border-box;\n border-right: 1px solid #e0e0e0;\n border-bottom: 1px solid #e0e0e0;\n overflow: hidden;\n white-space: nowrap;\n font-size: 13px;\n padding: 0 4px;\n position: absolute;\n top: 0;\n background: white;\n cursor: default;\n display: flex;\n align-items: center;\n}\n\n.eg-cell-active {\n outline: 2px solid #1a73e8 !important;\n outline-offset: -2px;\n z-index: 3;\n}\n\n.eg-cell-selected {\n background: #e3f2fd !important;\n}\n\n.eg-cell-fill-preview {\n background: #bbdefb !important;\n outline: 1px dashed #1565c0;\n outline-offset: -1px;\n}\n\n.eg-cell-copy {\n outline: 1px dashed #1a73e8;\n outline-offset: -1px;\n}\n\n.eg-cell-merged-hidden {\n display: none !important;\n}\n\n.eg-cell-invalid {\n outline: 2px solid #e53935 !important;\n outline-offset: -2px;\n z-index: 4;\n}\n\n.eg-cell-input {\n width: 100%;\n height: 100%;\n border: none;\n outline: none;\n background: white;\n font-size: 13px;\n font-family: inherit;\n padding: 0;\n}\n\n.eg-row-header-cell {\n box-sizing: border-box;\n background: #f0f0f0;\n border-bottom: 1px solid #e0e0e0;\n border-right: 2px solid #bbb;\n text-align: center;\n font-size: 11px;\n color: #666;\n cursor: pointer;\n user-select: none;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.eg-row-resize-handle {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 4px;\n cursor: row-resize;\n z-index: 5;\n}\n\n.eg-fill-handle {\n position: absolute;\n width: 6px;\n height: 6px;\n background: #1a73e8;\n border: 1px solid white;\n cursor: crosshair;\n z-index: 10;\n}\n\n.eg-context-menu {\n position: fixed;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n z-index: 9999;\n min-width: 160px;\n font-size: 13px;\n padding: 4px 0;\n}\n\n.eg-context-item {\n padding: 6px 16px;\n cursor: pointer;\n position: relative;\n}\n\n.eg-context-item:hover { background: #f0f0f0; }\n\n.eg-context-separator {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n\n.eg-context-submenu-parent::after { content: ' ▶'; float: right; }\n\n.eg-context-submenu {\n display: none;\n position: absolute;\n left: 100%;\n top: -4px;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n box-shadow: 2px 4px 12px rgba(0,0,0,0.15);\n min-width: 160px;\n padding: 4px 0;\n z-index: 10000;\n}\n\n.eg-context-submenu-parent:hover .eg-context-submenu { display: block; }\n\n.eg-group-btn {\n position: absolute;\n width: 16px;\n height: 16px;\n line-height: 14px;\n text-align: center;\n font-size: 10px;\n cursor: pointer;\n background: #e8eaf6;\n border: 1px solid #9fa8da;\n border-radius: 2px;\n z-index: 5;\n user-select: none;\n}\n\n.eg-group-btn.collapsed { background: #c5cae9; }\n\n.eg-drag-ghost {\n position: absolute;\n border: 2px dashed #1a73e8;\n pointer-events: none;\n z-index: 20;\n}\n\n.eg-resize-line {\n pointer-events: none;\n z-index: 9999;\n}\n\n/* Pin left separator shadow */\n.eg-frozen-col-body::after {\n content: '';\n position: absolute;\n top: 0;\n right: -4px;\n width: 4px;\n height: 100%;\n background: linear-gradient(to right, rgba(0,0,0,0.1), transparent);\n pointer-events: none;\n}\n\n.eg-pinned-right-body {\n position: sticky;\n right: 0;\n z-index: 10;\n background: white;\n flex-shrink: 0;\n}\n\n.eg-pinned-right-body::before {\n content: '';\n position: absolute;\n top: 0;\n left: -4px;\n bottom: 0;\n width: 4px;\n background: linear-gradient(to left, rgba(0,0,0,0.10), transparent);\n pointer-events: none;\n}\n\n.eg-pinned-right-header {\n overflow: hidden;\n flex-shrink: 0;\n border-left: 2px solid #bbb;\n background: #f0f0f0;\n}\n\n.eg-import-overlay {\n position: absolute;\n inset: 0;\n background: rgba(255, 255, 255, 0.85);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n border-radius: 4px;\n}\n\n.eg-import-dialog {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 24px 32px;\n min-width: 280px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.15);\n text-align: center;\n}\n\n.eg-import-title {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n margin-bottom: 16px;\n}\n\n.eg-import-bar-wrap {\n height: 8px;\n background: #e0e0e0;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 10px;\n}\n\n.eg-import-bar {\n height: 100%;\n background: linear-gradient(to right, #1a73e8, #4caf50);\n border-radius: 4px;\n transition: width 0.15s ease;\n}\n\n.eg-import-msg {\n font-size: 12px;\n color: #666;\n margin-bottom: 4px;\n}\n\n.eg-import-pct {\n font-size: 13px;\n font-weight: 600;\n color: #1a73e8;\n}\n.eg-export-spinner {\n width: 32px;\n height: 32px;\n margin: 12px auto 0;\n border: 3px solid #e0e0e0;\n border-top-color: #1a73e8;\n border-radius: 50%;\n animation: eg-spin 0.8s linear infinite;\n}\n@keyframes eg-spin {\n to { transform: rotate(360deg); }\n}\n\n/* ===== READONLY CELL ===== */\n.eg-cell-readonly {\n background: #f8f8f8;\n cursor: default;\n}\n.eg-cell-readonly.eg-cell-selected {\n background: #dce8f5;\n}\n.eg-cell-readonly.eg-cell-active {\n outline-color: #aaa;\n}\n\n/* ===== SELECT DROPDOWN ===== */\n.eg-cell-select {\n cursor: pointer;\n}\n.eg-cell-select::after {\n content: '▾';\n position: absolute;\n right: 4px;\n top: 50%;\n transform: translateY(-50%);\n color: #aaa;\n font-size: 11px;\n pointer-events: none;\n line-height: 1;\n}\n\n/* ===== CHECKBOX CELL ===== */\n.eg-cell-checkbox {\n justify-content: center;\n padding: 0 2px;\n}\n.eg-validation-tooltip {\n background: #fff0f0;\n border: 1px solid #e53935;\n color: #c62828;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n white-space: nowrap;\n pointer-events: none;\n}\n\n/* ===== ROW TRANSITION ===== */\n.eg-row-enter-active,\n.eg-row-leave-active {\n transition: opacity 0.18s ease, transform 0.18s ease;\n}\n.eg-row-enter,\n.eg-row-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n\n/* ===== SIZE VARIANTS ===== */\n\n/* small */\n.eg-size-small .eg-cell {\n font-size: 12px;\n padding: 0 3px;\n}\n.eg-size-small .eg-cell-input {\n font-size: 12px;\n}\n.eg-size-small .eg-header-cell {\n font-size: 11px;\n}\n.eg-size-small .eg-footer-cell {\n font-size: 12px;\n}\n.eg-size-small .eg-row-header-cell {\n font-size: 10px;\n}\n.eg-size-small .eg-select-option {\n font-size: 12px;\n padding: 4px 10px;\n}\n.eg-size-small .eg-context-menu,\n.eg-size-small .eg-context-item {\n font-size: 12px;\n}\n.eg-size-small .eg-context-item {\n padding: 4px 14px;\n}\n\n/* large */\n.eg-size-large .eg-cell {\n font-size: 14px;\n padding: 0 6px;\n}\n.eg-size-large .eg-cell-input {\n font-size: 14px;\n}\n.eg-size-large .eg-header-cell {\n font-size: 13px;\n}\n.eg-size-large .eg-footer-cell {\n font-size: 14px;\n}\n.eg-size-large .eg-row-header-cell {\n font-size: 12px;\n}\n.eg-size-large .eg-select-option {\n font-size: 14px;\n padding: 8px 14px;\n}\n.eg-size-large .eg-context-menu,\n.eg-size-large .eg-context-item {\n font-size: 14px;\n}\n.eg-size-large .eg-context-item {\n padding: 8px 18px;\n}\n</style>\n"]},media:void 0})},q,"data-v-5b531cb4",false,void 0,!1,V,void 0,void 0);var J={install(e){e.component("ExcelGrid",Z),e.component("EgCheckbox",F),e.component("EgSelect",G),e.component("EgTreeSelect",U)}};e.EgCheckbox=F,e.EgSelect=G,e.EgTreeSelect=U,e.ExcelGrid=Z,e.default=J,Object.defineProperty(e,"__esModule",{value:!0})});
|