diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js index 5d92cbe..bb65d79 100644 --- a/lib/ui/grid/grid.js +++ b/lib/ui/grid/grid.js @@ -67,7 +67,13 @@ let r = lang; /** * 键值字典 * @template T - * @typedef {Map} KeyMap + * @typedef {{[key: string]: T}} KeyMap + */ + +/** + * 索引字典 + * @template T + * @typedef {Map} IndexMap */ /** @@ -468,6 +474,24 @@ const GridColumnDirection = { * @this Grid */ +/** + * @typedef GridVirtualRow + * @property {boolean} editing - 行处于编辑状态 + * @property {IndexMap} cells - 虚拟单元格数组 + * @private + */ + +/** + * @typedef GridVirtualCell + * @property {string} background - 单元格背景色 + * @property {string} value - 单元格值 + * @property {string} tooltip - 单元格提示文本 + * @property {boolean} enabled - 单元格是否可用 + * @property {string} style - 单元格样式字符串 + * @property {string} attrs - 单元格附加属性字符串 + * @private + */ + /** * @typedef GridColumnAttr * @property {boolean} dragging - 列正在拖拽 @@ -630,6 +654,12 @@ export class Grid { * @private */ rowCount: -1, + /** + * 虚拟单元格字典 + * @type {IndexMap} + * @private + */ + virtualRows: {}, /** * 列类型缓存字典 * @type {KeyMap} @@ -2401,6 +2431,8 @@ export class Grid { const readonly = this.readonly; for (let i = 0; i < count; ++i) { const row = createElement('tr', 'ui-grid-row'); + const virtualRow = { cells: {} }; + this._var.virtualRows[exists + i] = virtualRow; let left = this.expandable ? ExpandableWidth : 0; if (this.expandable) { const icon = createIcon('fa-solid', 'caret-right'); @@ -2418,6 +2450,7 @@ export class Grid { } cols.forEach((col, j) => { const cell = createElement('td', 'ui-grid-cell'); + virtualRow.cells[j] = {}; if (col.visible !== false) { let style = this._get(col.key, 'style') ?? {}; if (col.isfixed) { @@ -2434,8 +2467,9 @@ export class Grid { if (style !== '') { cell.style.cssText = style; } + let element; if (!readonly && GridColumnTypeEnum.isCheckbox(col.type)) { - cell.appendChild(GridCheckboxColumn.createEdit(e => this._onRowChanged(e, exists + i, col, e.target.checked, cell))); + element = GridCheckboxColumn.createEdit(e => this._onRowChanged(e, exists + i, col, e.target.checked, cell)); // this._var.colTypes[col.key] = GridCheckboxColumn; } else { let type = this._var.colTypes[col.key]; @@ -2448,11 +2482,19 @@ export class Grid { type ??= GridColumn; this._var.colTypes[col.key] = type; } - const element = type.create(col, i, this); + element = type.create(col, i, this); if (typeof col.class === 'string') { type.setClass(element, col.class); } - cell.appendChild(element); + } + cell.appendChild(element); + if (col.events != null) { + for (let ev of Object.entries(col.events)) { + element[ev[0]] = e => { + const item = this._var.currentSource[this._var.startIndex + exists + i].values; + ev[1].call(item, e); + }; + } } } else { cell.style.display = 'none'; @@ -2481,17 +2523,17 @@ export class Grid { _fillRows(rows, cols, widths) { const startIndex = this._var.startIndex; const selectedIndexes = this._var.selectedIndexes; - const stateChanged = - this._var.oldIndex !== startIndex || - selectedIndexes == null || - this._var.oldSelectedIndexes?.length !== selectedIndexes.length || - this._var.oldSelectedIndexes.find((s, i) => s !== selectedIndexes[i]) != null; - if (stateChanged) { - this._var.oldIndex = startIndex; - if (selectedIndexes != null) { - this._var.oldSelectedIndexes = selectedIndexes.slice(); - } - } + // const stateChanged = + // this._var.oldIndex !== startIndex || + // selectedIndexes == null || + // this._var.oldSelectedIndexes?.length !== selectedIndexes.length || + // this._var.oldSelectedIndexes.find((s, i) => s !== selectedIndexes[i]) != null; + // if (stateChanged) { + // this._var.oldIndex = startIndex; + // if (selectedIndexes != null) { + // this._var.oldSelectedIndexes = selectedIndexes.slice(); + // } + // } const offset = this.expandable ? 1 : 0; const readonly = this.readonly; rows.forEach((row, i) => { @@ -2502,6 +2544,7 @@ export class Grid { if (!isPositive(row.children.length)) { return; } + const virtualRow = this._var.virtualRows[i]; const item = vals.values; const selected = selectedIndexes.includes(startIndex + i); if (selected) { @@ -2509,6 +2552,8 @@ export class Grid { } else if (row.classList.contains('selected')) { row.classList.remove('selected'); } + const stateChanged = virtualRow.editing !== selected; + virtualRow.editing = selected; // data if (this.expandable) { const expanded = vals.__expanded; @@ -2566,6 +2611,12 @@ export class Grid { if (cell == null) { return; } + let virtualCell; + if (stateChanged) { + virtualRow.cells[j] = virtualCell = {}; + } else { + virtualCell = virtualRow.cells[j]; + } let val; if (col.text != null) { val = col.text; @@ -2586,10 +2637,13 @@ export class Grid { if (typeof bg === 'function') { bg = col.background(item); } - cell.style.backgroundColor = bg ?? ''; } else if (typeof col.bgFilter === 'function') { - const bgColor = col.bgFilter(item); - cell.style.backgroundColor = bgColor ?? ''; + bg = col.bgFilter(item); + } + bg ??= ''; + if (bg !== virtualCell.background) { + virtualCell.background = bg; + cell.style.backgroundColor = bg; } const isCheckbox = GridColumnTypeEnum.isCheckbox(col.type); const type = isCheckbox ? GridCheckboxColumn : this._var.colTypes[col.key] ?? GridColumn; @@ -2624,35 +2678,49 @@ export class Grid { type.setClass(element, col.class); } cell.replaceChildren(element); + if (col.events != null) { + for (let ev of Object.entries(col.events)) { + element[ev[0]] = ev[1].bind(item); + } + } } else { element = cell.children[0]; } } else { element = cell.children[0]; } - let enabled; - if (readonly) { - enabled = false; - } else { - enabled = col.enabled; - if (typeof enabled === 'function') { - enabled = enabled.call(col, item); - } else if (typeof enabled === 'string') { - enabled = item[enabled]; + if (val !== virtualCell.value) { + virtualCell.value = val; + type.setValue(element, val, vals, col, this); + } + if (typeof type.setEnabled === 'function') { + let enabled; + if (readonly) { + enabled = false; + } else { + enabled = col.enabled; + if (typeof enabled === 'function') { + enabled = enabled.call(col, item); + } else if (typeof enabled === 'string') { + enabled = item[enabled]; + } + } + if (enabled !== virtualCell.enabled) { + virtualCell.enabled = enabled; + type.setEnabled(element, enabled); } } - type.setValue(element, val, vals, col, this); let tip = col.tooltip; if (typeof tip === 'function') { tip = tip.call(col, item); } - if (nullOrEmpty(tip)) { - element.querySelector('.ui-tooltip-wrapper')?.remove(); - } else { - setTooltip(element, tip, false, this.element); - } - if (typeof type.setEnabled === 'function') { - type.setEnabled(element, enabled); + if (tip !== virtualCell.tooltip) { + virtualCell.tooltip = tip; + if (nullOrEmpty(tip)) { + element.querySelector('.ui-tooltip-wrapper')?.remove(); + } else { + setTooltip(element, tip, false, this.element); + } } // auto resize if (this._var.needResize && widths != null && this._get(col.key, 'autoResize')) { @@ -2667,31 +2735,29 @@ export class Grid { if (typeof style === 'function') { style = col.style(item); } - if (style != null) { - type.setStyle(element, style); - } else { - element.style.cssText = ''; - } } else if (typeof col.styleFilter === 'function') { - const style = col.styleFilter(item); + style = col.styleFilter(item); + } + const styleText = style != null ? convertCssStyle(style) : ''; + if (styleText !== virtualCell.style) { + virtualCell.style = styleText; if (style != null) { type.setStyle(element, style); } else { element.style.cssText = ''; } } - if (col.events != null) { - for (let ev of Object.entries(col.events)) { - element[ev[0]] = ev[1].bind(item); - } - } if (col.attrs != null) { let attrs = col.attrs; if (typeof attrs === 'function') { attrs = attrs(item); } - for (let attr of Object.entries(attrs)) { - element.setAttribute(attr[0], attr[1]); + const attrsText = convertCssStyle(attrs); + if (attrsText !== virtualCell.attrs) { + virtualCell.attrs = attrsText; + for (let attr of Object.entries(attrs)) { + element.setAttribute(attr[0], attr[1]); + } } } }); diff --git a/package-lock.json b/package-lock.json index 5febb65..6c844b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,16 +13,16 @@ "docdash": "^2.0.2", "jsdoc": "^4.0.2", "postcss-preset-env": "^9.4.0", - "sass": "^1.71.0", - "typedoc": "^0.25.8", - "vite": "^5.1.3", + "sass": "^1.71.1", + "typedoc": "^0.25.10", + "vite": "^5.1.4", "vite-plugin-externals": "^0.6.2" } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -352,9 +352,9 @@ } }, "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.10.tgz", - "integrity": "sha512-PwKOxVuX8lo52bPtPeKjaIp6oH2EzhcBxCndRcvGZKsqZYQ35k9A5G4yihZ+wp7PoxPqDNiXuhQsvQG2lqMpOA==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.11.tgz", + "integrity": "sha512-LFom5jCVUfzF+iuiOZvhvX7RRN8vc+tKpcKo9s4keEBAU2mPwV5/Fgz5iylEfXP/DZbEdq2C0At20urMi/lupw==", "dev": true, "funding": [ { @@ -1358,14 +1358,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -1381,9 +1381,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1406,9 +1406,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1674,9 +1674,9 @@ "dev": true }, "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "version": "10.4.18", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", + "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", "dev": true, "funding": [ { @@ -1693,8 +1693,8 @@ } ], "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001591", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -1801,9 +1801,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001588", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", - "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", + "version": "1.0.30001593", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001593.tgz", + "integrity": "sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==", "dev": true, "funding": [ { @@ -1969,9 +1969,9 @@ } }, "node_modules/cssdb": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.0.tgz", - "integrity": "sha512-YUVAJhjDcTZzVD5XE49l3PQtGE29vvhzaL1bM3BtkvSmIRJeYENdfn1dn5jauBI7BBF+IyyiBS+oSVx3Hz/Gaw==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.1.tgz", + "integrity": "sha512-F0nEoX/Rv8ENTHsjMPGHd9opdjGfXkgRBafSUGnQKPzGZFB7Lm0BbT10x21TMOCrKLbVsJ0NoCDMk6AfKqw8/A==", "dev": true, "funding": [ { @@ -2016,9 +2016,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.677", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.677.tgz", - "integrity": "sha512-erDa3CaDzwJOpyvfKhOiJjBVNnMM0qxHq47RheVVwsSQrgBA9ZSGV9kdaOfZDPXcHzhG7lBxhj6A7KvfLJBd6Q==", + "version": "1.4.690", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.690.tgz", + "integrity": "sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA==", "dev": true }, "node_modules/entities": { @@ -2966,9 +2966,9 @@ } }, "node_modules/postcss-nesting": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.3.tgz", - "integrity": "sha512-yrtMRPFNkfZMv9ikBvZ/Eh3RxhpMBKQ3KzD7LCY8+jYVlgju/Mdcxi4JY8bW2Y7ISXw8GTLuF/o+kFtp+yaVfQ==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.4.tgz", + "integrity": "sha512-WuCe0KnP4vKjLZK8VNoUWKL8ZLOv/5jiM94mHcI3VszLropHwmjotdUyP/ObzqZpXuQKP2Jf9R12vIHKFSStKw==", "dev": true, "funding": [ { @@ -3398,9 +3398,9 @@ } }, "node_modules/terser": { - "version": "5.27.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.2.tgz", - "integrity": "sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==", + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz", + "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -3440,9 +3440,9 @@ "dev": true }, "node_modules/typedoc": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.8.tgz", - "integrity": "sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.10.tgz", + "integrity": "sha512-v10rtOFojrjW9og3T+6wAKeJaGMuojU87DXGZ33sfs+554wgPTRG+s07Ag1BjPZI85Y5QPVouPI63JQ6fcQM5w==", "dev": true, "dependencies": { "lunr": "^2.3.9", @@ -3532,9 +3532,9 @@ "dev": true }, "node_modules/vite": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz", - "integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", "dev": true, "dependencies": { "esbuild": "^0.19.3", diff --git a/package.json b/package.json index 58ba2a6..af1c231 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "docdash": "^2.0.2", "jsdoc": "^4.0.2", "postcss-preset-env": "^9.4.0", - "sass": "^1.71.0", - "typedoc": "^0.25.8", - "vite": "^5.1.3", + "sass": "^1.71.1", + "typedoc": "^0.25.10", + "vite": "^5.1.4", "vite-plugin-externals": "^0.6.2" } }