From 38bad864e0013c1a8d0c550f2909ccc3c8144075 Mon Sep 17 00:00:00 2001
From: Tsanie <lchen@foresightintelligence.com>
Date: Mon, 26 Feb 2024 16:11:42 +0800
Subject: [PATCH] fix: header z-index issue. fix: incorrect `onChanged` event
 trigger on `GridInputColumn`, `GridTextColumn` and `GridDateColumn`.

---
 lib/ui/css/grid.scss   |  1 +
 lib/ui/grid/column.js  | 73 ++++++++++++++++++++++--------------------
 lib/ui/grid/grid.js    | 27 +++++++++++-----
 lib/ui/icon.js         |  5 +--
 lib/utility/strings.js |  2 ++
 5 files changed, 63 insertions(+), 45 deletions(-)

diff --git a/lib/ui/css/grid.scss b/lib/ui/css/grid.scss
index ac5f2c0..c2e8d9d 100644
--- a/lib/ui/css/grid.scss
+++ b/lib/ui/css/grid.scss
@@ -94,6 +94,7 @@
                         // position: relative;
                         top: 0;
                         position: sticky;
+                        z-index: 1;
 
                         &.sticky {
                             position: sticky;
diff --git a/lib/ui/grid/column.js b/lib/ui/grid/column.js
index 11dcaa0..51ac9d9 100644
--- a/lib/ui/grid/column.js
+++ b/lib/ui/grid/column.js
@@ -55,7 +55,8 @@ export class GridColumn {
      * 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。
      * @method
      * @name GridColumn.createEdit
-     * @param {Function} trigger - 编辑事件回调函数,`e` 参数会传递给 [getValue]{@linkcode GridColumn.getValue} 方法
+     * @param {Function} trigger - 编辑事件回调函数
+     * @param {any} trigger.e - 该参数会传递给 [getValue]{@linkcode GridColumn.getValue} 方法
      * @param {GridColumnDefinition} col - 列定义对象
      * @param {HTMLElement} container - 父容器元素
      * @param {GridItemWrapper} wrapper - 行包装对象,其 `values` 属性为行数据对象
@@ -144,6 +145,29 @@ export class GridColumn {
      * @ignore
      */
     static toString() { return '[object Column]' }
+
+    /**
+     * @ignore
+     * @param {string} key 
+     * @param {GridItemWrapper} wrapper 
+     * @param {any} value 
+     */
+    static _changeValue(key, wrapper, value) {
+        const val = wrapper.values[key] ?? null;
+        const hasValue = val != null && Object.prototype.hasOwnProperty.call(val, 'Value');
+        if (wrapper.__editing == null) {
+            wrapper.__editing = {
+                [key]: hasValue ? val.Value : val
+            }
+        } else if (!Object.prototype.hasOwnProperty.call(wrapper.__editing, key)) {
+            wrapper.__editing[key] = hasValue ? val.Value : val;
+        }
+        if (hasValue) {
+            val.Value = value;
+        } else {
+            wrapper.values[key] = value;
+        }
+    }
 }
 
 /**
@@ -158,24 +182,17 @@ export class GridInputColumn extends GridColumn {
 
     /**
      * @ignore
-     * @param {Function} _trigger 
+     * @param {Function} trigger 
      * @param {GridColumnDefinition} col 
      * @param {HTMLElement} _container 
      * @param {GridItemWrapper} wrapper 
      * @returns {HTMLElement}
      */
-    static createEdit(_trigger, col, _container, wrapper) {
+    static createEdit(trigger, col, _container, wrapper) {
         const input = createElement('input');
         input.setAttribute('type', 'text');
-        input.addEventListener('input', () => {
-            if (wrapper.__editing == null) {
-                wrapper.__editing = {
-                    [col.key]: true
-                }
-            } else {
-                wrapper.__editing[col.key] = true;
-            }
-        });
+        input.addEventListener('input', () => super._changeValue(col.key, wrapper, input.value));
+        input.addEventListener('change', trigger);
         return input;
     }
 
@@ -221,23 +238,16 @@ export class GridInputColumn extends GridColumn {
 export class GridTextColumn extends GridInputColumn {
     /**
      * @ignore
-     * @param {Function} _trigger 
+     * @param {Function} trigger 
      * @param {GridColumnDefinition} col 
      * @param {HTMLElement} _container 
      * @param {GridItemWrapper} wrapper 
      * @returns {HTMLElement}
      */
-    static createEdit(_trigger, col, _container, wrapper) {
+    static createEdit(trigger, col, _container, wrapper) {
         const input = createElement('textarea');
-        input.addEventListener('input', () => {
-            if (wrapper.__editing == null) {
-                wrapper.__editing = {
-                    [col.key]: true
-                }
-            } else {
-                wrapper.__editing[col.key] = true;
-            }
-        });
+        input.addEventListener('input', () => super._changeValue(col.key, wrapper, input.value));
+        input.addEventListener('change', trigger);
         return input;
     }
 
@@ -476,7 +486,7 @@ export class GridCheckboxColumn extends GridColumn {
      */
     static createEdit(trigger) {
         const check = createCheckbox({
-            onchange: typeof trigger === 'function' ? trigger : null
+            onchange: trigger
         });
         return check;
     }
@@ -604,13 +614,13 @@ export class GridDateColumn extends GridColumn {
 
     /**
      * @ignore
-     * @param {Function} _trigger 
+     * @param {Function} trigger 
      * @param {GridColumnDefinition} col 
      * @param {HTMLElement} _container 
      * @param {GridItemWrapper} wrapper 
      * @returns {HTMLElement}
      */
-    static createEdit(_trigger, col, _container, wrapper) {
+    static createEdit(trigger, col, _container, wrapper) {
         let enabled = col.enabled;
         if (typeof enabled === 'string') {
             enabled = wrapper.values[enabled];
@@ -621,15 +631,8 @@ export class GridDateColumn extends GridColumn {
             return super.create();
         }
         const date = createDateInput(col.dateMin, col.dateMax);
-        date.addEventListener('change', () => {
-            if (wrapper.__editing == null) {
-                wrapper.__editing = {
-                    [col.key]: true
-                }
-            } else {
-                wrapper.__editing[col.key] = true;
-            }
-        });
+        date.addEventListener('change', () => super._changeValue(col.key, wrapper, date.value));
+        date.addEventListener('blur', trigger);
         return date;
     }
 
diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js
index b7a9118..261d50d 100644
--- a/lib/ui/grid/grid.js
+++ b/lib/ui/grid/grid.js
@@ -95,7 +95,7 @@ let r = lang;
  * @property {KeyMap<GridSourceItem[]>} source - 下拉数据源缓存对象
  * @property {number} __index - 行索引
  * @property {number} __offset - 批量删除时暂存的索引偏移量
- * @property {KeyMap<boolean>} __editing - 列正在编辑
+ * @property {KeyMap<any>} __editing - 正在编辑的列的原始值字典
  * @property {boolean} __changed - 行数据是否发生改变
  * @property {boolean} __expanded - 行是否已展开
  * @property {GridExpandableObject} __expandable_object - 行扩展对象
@@ -2593,19 +2593,30 @@ export class Grid {
                 const type = isCheckbox ? GridCheckboxColumn : this._var.colTypes[col.key] ?? GridColumn;
                 let element;
                 if (!readonly && !isCheckbox && typeof type.createEdit === 'function') {
-                    if (vals.__editing?.[col.key]) {
+                    const oldValue = vals.__editing?.[col.key];
+                    if (oldValue !== undefined) {
                         delete vals.__editing[col.key];
                         if (typeof type.leaveEdit === 'function') {
                             type.leaveEdit(cell.children[0], this._var.el);
                         }
                         if (type.editing) {
                             val = type.getValue({ target: cell.children[0] }, col);
-                            this._onRowChanged(null, i, col, val, cell);
+                            this._onRowChanged(null, i, col, val, cell, oldValue);
                         }
                     }
                     if (stateChanged) {
                         element = selected ?
-                            type.createEdit(e => this._onRowChanged(e, i, col, type.getValue(e, col), cell), col, this._var.el, vals) :
+                            type.createEdit(e => {
+                                let old;
+                                if (type.editing) {
+                                    old = vals.__editing?.[col.key];
+                                    if (old === undefined) {
+                                        return;
+                                    }
+                                    delete vals.__editing[col.key];
+                                }
+                                this._onRowChanged(e, i, col, type.getValue(e, col), cell, old);
+                            }, col, this._var.el, vals) :
                             type.create(col, i, this);
                         if (typeof col.class === 'string') {
                             type.setClass(element, col.class);
@@ -3783,8 +3794,9 @@ export class Grid {
      * @param {GridColumnDefinition} col 
      * @param {any} value 
      * @param {HTMLTableCellElement} cell 
+     * @param {any} [oldValue] 
      */
-    _onRowChanged(e, index, col, value, cell) {
+    _onRowChanged(e, index, col, value, cell, oldValue) {
         if (this._var.currentSource == null) {
             return;
         }
@@ -3802,12 +3814,11 @@ export class Grid {
         }
         if (enabled !== false) {
             const val = item[col.key];
-            let oldValue;
             if (val != null && Object.prototype.hasOwnProperty.call(val, 'Value')) {
-                oldValue = val.Value;
+                oldValue ??= val.Value;
                 val.Value = value;
             } else {
-                oldValue = val;
+                oldValue ??= val;
                 item[col.key] = value;
             }
             let tip = col.tooltip;
diff --git a/lib/ui/icon.js b/lib/ui/icon.js
index d7d9046..ad28973 100644
--- a/lib/ui/icon.js
+++ b/lib/ui/icon.js
@@ -1650,8 +1650,9 @@ const dict = {
 
 function createUse(type, id) {
     const c = typeof consts !== 'undefined' ? consts : {};
-    const netroot = typeof _network !== 'undefined' ? _network.root : '';
-    const path = c.path || netroot;
+    const path = c.path ||
+        (typeof _network !== 'undefined' ? _network.root :
+            (typeof _net !== 'undefined' ? _net.root : ''));
     const ver = c.resver == null ? '' : `?${c.resver}`;
     const use = document.createElementNS(svgns, 'use');
     if (id?.length === 1 && id.charCodeAt(0) > 0xf000) {
diff --git a/lib/utility/strings.js b/lib/utility/strings.js
index 3371e86..5cafa51 100644
--- a/lib/utility/strings.js
+++ b/lib/utility/strings.js
@@ -50,6 +50,8 @@ export function formatUrl(msg) {
             path = consts.path;
         } else if (typeof _network !== 'undefined') {
             path = _network.root;
+        } else if (typeof _net !== 'undefined') {
+            path = _net.root;
         }
         for (let r of rs) {
             msg = msg.replaceAll(r, `<a target="_blank" href="${r}"><svg><use xlink:href="${path || ''}fonts/fa-regular.svg#link"></use></svg></a>`);