## 需要完善的内容
[ ] 明明规定了 type="body" && area="flatten", 为何一定要level
[ ] dataType写错了, 但没有得到提示
[ ] 需要一个设置: 不显示 row-indicator / col-indicator
[ ] row-no 应该在某个area内生效

## 需要测试的内容
[√] 整合到成都咨询系统, 注意valor-app-utils和valoe-random-ui的版本
[√] 在最后插入一个100行的长树, 看看是不是全部加载出来了, 然后滚动到根结点, 删除这棵树
[√] 各种增删行列操作, 看看selection会不会报错
[√] 移动树表的行的代码有修改, 需要测试 (主要是看setDimensions的影响)
[√] Dimension发生变化, 对外部程序的影响
[√] bug: 直接输入=号, 公式未出来
[√] bug: VScrollBar上mousedown, 好象没有mouseup
[√] 有时滚动很不正常, 例如在插入行后, 然后将插入的行滚出, 会自动又滚入
[√] 选择区域 和 scroll不同步
[√] presentCell 与 scroll不同步
[√] 所有setDimensions的地方都要考虑 readyDimensions
[√] 显然clearSelectionCommand应该直接访问状态机
[√] 初始只有10行, 选中第6行, 然后上滚一点(不要滚满1格), 发现selected消失
[-] 当删除一行, 然后再undo, 发现某个单元格无法编辑了(未重现)
[√] 在进入编辑模式下, 直接按下=号, 不出公式编辑器
[ ] bug: 滚动过程中, selected位置与表格位置不同步 ( 作为优化内容 )
[√] 折叠后, selected位置错误
[√] bug: Selected移到被滚走的区域, 没有自动滚进来
[√] bug: 千分位显示异常
[√] 如果没有 freezeAt, 则不应该显示滚动条, 也应禁止滚动
[√] 若将storybook的formula-editor改成100x100, 则滚动条错乱
[√] 若选中冻结单元格, 则滚动时会自动复位
[√] 选中 滚动条并拖动时, 如果拖动到sheet内, 发现 selection 突然变大, 这会导致一些问题
[√] 非冻结sheet, 横向滚动时selected错位
[√] 当行高有小数时, selected的位置会出现累计偏差
[√] 千分位bug
[√] 有富文本时, 滚动时行高错误

## 2021-01-13
[√] 冻结行不能隐藏
[√] 改变窗口大小后, 滚动条 及 被选中/被编辑单元格 等是否正常显示
[√] 当变动窗口大小 / 重新填写单元格的值 / 增删行列 后, 重置 containerBox & colDimensions & rowDimensions
[√] bug: 滚动条无法上滚 ( 只能单向下滚 )
    将 DataSheet 绑定的: 
    ``` onWheel={this.handleWheel} ```
    转移到 SpreadSheet下的 `spread-sheet-wrapper` 上
[√] 实现rows的滚动 ( 当前是通过allVisibleRows 来控制的 )
[√] 将setColDimensions/setRowDimensions改为: patch , 而非 replace ( 有则替换, 无则增加, 但不可/不需删除 )
[-] 当进行pick单元格操作时, 会产生不可预期的现象 ( 貌似tr.display=none被取消了 )
[√] getAllCollapsedRowIds判断indexOf较慢, 改成 数组 
[√] 首次调用时, 貌似所有行都有高度
[√] 禁止容器自身滚动, 而且有时 表头会滚出容器外? 
[√] 将setDimensions移到runtime里
[?] dimensions何时更新: 
    当容器宽高发生变化 ( 如果只加载了1行, 然后放大窗口, 其实与滚动的作用类似, 也需要动态计算1+行的宽高 )
    当滚动时 ( 加载时,可能只加载了10行,所以不知道10+行的宽高, 需要在滚动后动态计算 )
    当增行 / 删行 / 改单元格 时 (虽然原则上只需对rowDimension进行patch, 但比较麻烦, 所以统一重新 resetDimensions )
    当折叠前后, 需要重新 resetDimensions
    以上注意如果无改动, 一定要返回null
[?] 当增加行时, rowDimension.height=0 或 undefined, 在setDimensions方法中默认设为30了 ( 未检查 )
[√] 写一个memoizeOnce的方法 ( 如果有时间 再加一个 memoizeByCount 的方法, 衍生出momoize1/memoize2等), 在app-utils里
[?] 让全部单元测试通过
[√] 当选整行或整列时, 选中框的大小不正确
[√] insert-tree-row-command storybook 为何无法选中第2行
[√] 当插入行时报错
[√] 自动滚动到高亮位置
    bug1: 未考虑冻结 ( 不用考虑 )
    bug2: 若当前: rowHeight=100, scrollY=90; 计算delta_scrollY是按当前dim来计算的, 与原scrollY并不匹配; 
          可能最大造成接近1行高的误差
          scrollY0与scrollY不应产生关联(没有直接推导关系)
    bug3: 反复滚动
    bug4: 还要考虑整行与整列
[√] 自动重载后ref丢失的问题 ( 未释放监听, 内存泄漏 )
[√] 显示hscrollbar
[√] 实现column的动态加载
[√] 焦点移到屏幕左/上方, 无法自动移动视口
[√] 一旦焦点位于屏幕内部, 则滚动到屏幕外时, 会自动滚回来, 所以这个方案当前有问题
[√] 焦点无法用光标移到第6列
[√] 假如向左滚动2列, 这2列将隐藏(列1,列2). 然后选中列3的单元格, 向左移光标, 列2并不自动滚出 
[√] 焦点移动混乱
[√] 焦点居然会移到屏幕外部 ( 左/上方 )
[-] 监控一下storybook占用的内存, 不知为何有时高达1G(貌似不是程序问题)
[√] 滚动条无法点击/拖拽滚动
[√] 重构selection/getSelectionDimensions方法
    getCellDimensions => getDimensionByScope
    getRowDimensions => getDimensionByScope
    getColumnDimensions => getDimensionByScope
    getRowRangeDimensions => getRowRangeScope => getDimensionByScope
    getColumnRangeDimensions => getColumnRangeSceop => getDimensionByScope
    getCellRangeDimensions => getCellRangeScope => getDimensionByScope
    getSelectedDimensions  => getSelecionScope => getDimensionByScope
    // base on: 
    getSelectionByIndex(rename to getSelectionScope) => get(RowRange/CellRange/ColumnRange)Scope
[√] 上述Dimensions方法在哪里用到, 应该用offsetDimension还是dimension
    是否引入offsetDimension的概念? 
    计算时注意合并单元格
[√] 无法点击第一列
[√] 点击屏幕外的第4行, 为何会选中第5行? 
[√] 点击F3行, 会自动上移一行
[√] 如果用键盘right键滚动到最右侧, 然后多按一下 right键, 再滚动到左侧, 会发现差一部分

### 影响到的位置
[√] 公式编辑器的正确显示位置
[√] 外部浮层的正确位置(例如 指标单元格的右下角指标属性)
[√] 编辑器为下拉框的单元格

### 最后的清理: 性能
[ ] 当滚动时, 会多次计算setDimensions, 但如果已完整滚动整个文档,  则滚动不再触发渲染

## 2020-08-09
目标: 单元格的操作, 高仿exel
[ ] 第一步: state改进: 划分 selectedCellRange / presentCell / activing
[ ] 第二步: 组件根据state重写, 当手动切换状态时, 组件成功更新
[ ] 第三步: 改状态机 

[ ] sum(a1:b1) 或 a1+b1, 删除b1时, 应该如何改动
[ ] sum(a1:c1), a1/b1/c1高亮
[ ] 复制一个单元格到另一个, 公式如何重新生成
[ ] 绝对坐标的影响 : 仅当需要复制单元格操作时, 绝对坐标才会产生影响 
    可记录 复制前的相对位置 和 复制后的相对位置

## 2020-07-08
[√] 当row.type='header'时, richText无效
[√] richText软回车问题
[√] getExternalValue, 需要获得typedValue
[√] L14 单元得到Error
[√] BB7-BA7有问题
    A => 0, X => 25, AA => 26, AX => 51, B0 => 52, BB => 54

## 2020-07-07
[√] 插列/删列后, 应自动重算 ( 直接全局重算 )
[√] Error后, 无法自动更新 ( 方案: 输入单元格值后, 自动更新全部ERROR单元格)
[√] 不知为何, 现在还是存在懒加载不自动计算的问题, 可能是因为我没取消懒加载

## 2020-06-20
[√] 未检查跨表!!!
[√] 合并单元格
[ ] A1+A2, 删除A2的问题 ( 然后再删除 A1), 或 A3:A5删除一个
    如果不能解决, 至少应保证每次打开时, 不显示页面报错
[ ] 通过 整个表格动态加载, 避免以下问题: 
    1. 由于速度加快, 不需要使用 cell.skip=true 了
    2. 由于不需要使用cell.skip=true, 上方的重算按钮基本无用, 每次getData的重算基本无用, 所以性能将得到极到提升
    3. 每一次加载时, 只需要重算而只显示很小一部分, 对于大表性能应该非常好
[ ] 其它性能问题
    1. 可在前端缓存varDeps/cellDeps
    2. 自动重算可做成异步的, 或可选的, 先加载, 再出提示, 再重算
[ ] 有些操作是通过发全局事件, 导致目前多sheet无法共存
[ ] 有时间的话, 可考虑重构整个状态机为状态模式
[√] 换行
    TextareaRender / TextareaEditor

## 2020-06-18
记住: 当前dev分支, 合并单元格未完成!!! 仅完成 右侧插列

## 2020-06-14
约定1: {{idx33}} 全部小写
约定2: SHEET3!A3 => SHEET3全部大写, A3全部大写

## 2020-06-06
[√] bug: 在子节点升级时, 父节点会出现 3个可输入的单元格(costType) 
[√] 重新思考 row 里的costType, 可能使用 cell (ok)
[√] sumRight(ok), sum(a1:a5) ok
[√] formulaManager
[√] 哪些地方需要改 getCellIdByStr() ? (受getExternalValue影响)
    1. 计算时: 初始定义时 / 增删行后的计算 /  修改公式后的计算
    2. 输入公式时, 输入完成(假设输入 B.A2), 需要转换 
[√] 文本型单元格自动换行

[√] cell 的 isChecked / checked , 完全可以这样提供: 
    runtime: {
        isChecked(cell:ICellNN, row:IRow) => row.checkable.includes(cell.id) && !cell.formulaDisabled,
        checkable(cell:ICellNN, row:IRow) => row.checkable.includes(cell.id);
        onCellChecked(setCellData, checked) => {}
    }
    四步: 
    第一步: 外部初始化时, 
            在row里保存checkableList
            所有checkableList里的单元格, 公式设为相同
            使用costType 设置 第n个单元格的formulaDisabled=false, 使公式生效
    第二步: 显示对号
            使用runtime.checkable 显示对号, 如果是isChecked则显示红色
    第三步: 用户点击对号
            使用runtime.onCellChecked.setCellData, 设置 formulaDisabled
    第四步: 保存数据时, 将formulaDisabled转化为 costType


## 2020-06-01
[√] 性能: 可以渐进式地构造一个 cell列表, 这个列表只包含 影响计算的单元格, 从而规避大量循环
[√] 如何获取外部CellValue: 
    1. 在Props里传入extCellGetter: (sCell:"111.333")
    2. store.extCellGetter = props.extCellGetter
    3. 在所有用到new Parser 的地方, 增加 getExtCellValue  = store.extCellGetter ( 可选参数 )
    4. 在Parser里正则解析出ext cell
[√] 如何在输入时, 将 "A2.B5" 解析为 {{111.333}} ?
    0. 将 positioned/named 公式转换器部分暴露出去, 以便外部构造 FormulaTransformer
    1. 在Props里传入 new FormulaTransformer() {fromIdFromula, toIdFormula}
    2. 设置 RuntimeContext.formulaTransfomer
    3. 在Formula的Editor里使用它们

## 2020-05-28
### 支持A1这样的风格
[√] 若没有提供 CellNamedPolicy, 则默认启用 positionName
[√] openFormularEditor支持 positioned formular editor
[√] 同样支持引用单元格高亮
[√] 同样支持点击选择其它单元格
[√] 支持A1这样的公式计算
[ ] 验证变量支持的正确性 ( 可能不需要用到变量, 而是创建表时一次搞定 )
[√] 出错应报错, 不要关闭
[√] auto focus
[√] 样式

[√] 重新计算treeContext
[√] 限制: 暂时只允许一个sheet出现一棵树
[√] 插入行, 将现有InsertCommand改成insertTreeRowCommand, 并新增一个 InsertTreeRowCommand
[√] 影响RowNo计算, 暂时让rowNo只支持tree
[√] DeleteRow, InsertRow, actions/row, push, move

智钧: 
[√] 在row表中, 增加字段 area: 'flatten' | 'tree', 以支持混合表

// 未测试!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[√] 考虑回车确认事件, 不要放到全局
    或 总是先执行组件事件, 组件事件可阻断全局事件
[√] 取消: CellEditor打开时, 按enter确认并退出编辑; 代之以: 由每个Editor分别管理  
[√] 同上, esc由谁负责?  -- 每个editor
[√] 所有editor submit后, 应立即return
[√] positionedFormulaEditor加上pick功能
[√] 公式编辑器的id=>lable, labe=>id 等转换, 抽象成一个类, 由顶部传入, 后面好改成支持跨表
[-] clickoutside也由自己负责  - 放弃

[√]  amendCellI: 这一步应该会被优化掉
[√]  去掉cell.memo
[√]  RowNoRenderer 当删除行时, 是否不能正确卸载 ? 

## 2020-04-01
[√] 目前测试通不过, 需要在 删除时,重置选中状态(先直接发到状态, 再发到fsm, 确保fsm对store的刷新无效!!!!) 状态!!! (先改状态, 再删除行)
[√] 只有选择状态下才能进行增删升降行
[√] 各种通知, 通过dispatchEvent发
[√] 插入行后自动滚动

getCellsByIj, 60行*12列, 耗时近1秒
[√] cell.renderer 是否用到isSelected, 否则可以直接将row.cells在内部求值, 不用在mapProps里求 ( 免deep equals)
[√] 选择性能
[-] 考虑将 400行 改成 10个40行: 貌似暂时不需要

[√] 插入性能
[√] 初步怀疑是因为recalculate引起, 可禁用

[√] 删除性能
[√] 升降级性能
[√] 大表的计算性能

[√] 可能多次调用_objectSpread2太耗时
    ... 改成Object.assign, 节省10倍
    不知道 setDellDeps里用到的 { [depCellId]: [...(acc[depCellId] || []), hostCellId] } 有多耗时

[√] recalculate性能: 
    先整体优化, 400行应优化到 3秒内  ( 目前是300行8秒 )
    再局部优化, 比如插入行时, 应该只影响部分公式, 而非全部

[√] recalculate后, 由于全部cell发生变化, 所以会导致全部重新渲染, 应避免
[√] 升降级的撤销有问题
[√] 选择时, 若单元格已被选中, 则无法点击三个点


## 2020-03-02
[√] 循环依赖未解决
[-] 部分单元格应设置为 只读 , 只有当解锁时才可编辑

## 
[√] 支持多选 
[-] 选择时, 左侧指示器颜色变深
[√] 可选行

[√] store初始化, 防止null
[√] 重构, 将cells normalize
[√] 数据结构包含cell的i,j, row的i
[√] i,j 利用selector优化掉

[√] 双击 , 点两次也是双击
[√] 选中后 回车, 也会激活
[√] 上下左右键可以导航
[√] bug: 不可多选

[√] 实现=1+a1+2
[√] 实现弹框输入公式 ( 注意切换模式到 mode=pick )

[√] undo, redo
[√] 未测试综合计算: 一个包含 变量, 函数, 单元格 的方法, 比如 100-k*(sum(a5:a8)+33)
[√] getCellValue的实现
[-] getRangeValue的实现 : delay, 可能暂时用不上
[√] 公式里包含有哪些依赖? [id1, id2, ...]的样式列出来
[√] variable的终极用途: 例如 其它费用, 0.9/0.8*总价, 这里的0.9和0.8可以作为公式的参数, 相当于存在于excel之外!
[√] 增行,删行, 当前主要考虑对公式的影响
[√] 为避免复杂, parser每次都单独初始化, 应该性能影响很小很小, 而且还可以同时处理多个parser
[√] 先处理parser, 返回 (deps + functions + variables)

## 依赖图的初始化
[√] 全局初始化: 逐个单元格尝试计算, 边计算边生成依赖图 ( 这个计算最好是递归的, 直接单层计算可能出错 )
    如果某个单元格出错, 会导致 后续单元格错, 但只要一修复, 后续单元格就无问题了
    主要注意不要重复计算, 免得时间太长
[√] 单个初始化(场景:新建公式): 只考虑单层 ( 假设 被依赖的单元格已计算完成 ), 这是合理的
[√] 单个初始化(场景:插入行)

## 依赖图改变
对于行的变动, 无法预知影响, 所以只好 全局初始化一次
对于单元格的变动, 只需 按下面依赖的自动计算 推送一下即可

## 主体变更引发依赖图改变(例:A依赖于A1,A2, A升级)
1. 考虑A单元格所在行 升级/降级/删除/移动
    在行进行了操作后, 行内每个单元格都启动一次重算, 并重设依赖图
    因为id没变, 所以是合理的
2. 单元格A本身改公式


## 被依赖的单元格变更引发依赖图改变(例:A依赖于A1,A2, A1升级)
1. A1所在行 升级/降级/删除/移动
这种情况复杂: 比如 L1从第3级升级到第2级, L0如何知道自己要重设依赖?
    -- L0
      -- L0.5
        -- L1
  
2. A1单元格本身改公式

## 依赖的自动计算
[√] 初始化: 先逐个单元格计算依赖, 再生成全局依赖图, 根据全局依赖图逐层更新依赖
[√] 推送方式实现自动更新: 当单元格A1发生变动时, 自动推算所有受影响的单元格

## action的写法
1. 一定是透过顶级组件传出去的, 相当于controller ( 可以考虑透出一个专用的 CommandManager )
2. CommandManager 包含store, 内部 exec 直接操作store.setState, 这样做的好处是:CommandManager不需要设计成React组件
3. 每个操作都有一个独立的Command, 比如 InsertRowAction, 调用时:
commandManager.exec(InsertRowAction, params);  // 执行完后进栈
commandManager.undo(undoStack.pop())
commandManager.redo(redoStack.pop())
4. 从这个意义上, 这个顶级组件可以看成ViewModel ( 见https://www.kancloud.cn/wizardforcel/learning-hard-csharp/111590 )
5. 请设计一个单独的库: valor-command-pattern-for-react-unistore

[√] bug: 运行 formula_support index.test.ts case10 时间长达70秒
[√] getVariable有没有运行? 
[√] varDeps

[√] 将第一次公式计算, 混入初始化代码
[√] 修改公式, 会跟着变
[√] formulaEnabled: 公式是否启用, 相应修改 recalculate
[√] initStore方法泛化

[√] 将 mode:view等代码全部移除
[√] selectCellAbove等的返回值里的mode移除
[√] 键盘操作

# 可撤销的命令汇总: 
[√] 插入行 ( 包含子树 )
[√] 删除行
[√] 行升级
[√] 行降级
[√] 行上移
[√] 行下移
[√] 设置 单元格的值
[√] 设置 单元格的公式
[√] 设置 单元格的样式
[√] collapse bug

# bug
[√] setCellData时, editor 返回的值应该是typed的
[√] 编辑时, 按下Esc, 再点击其它单元格, 结果跑到其它单元格里了 ( esc 貌似无效 ) ok, 直接传id
[√] textEditor与numbereditor同步修改
[√] test 出错
[√] constants
[√] NumericEditor onSumit统一化

# 界面显示
[√] error显示出来
[√] cell.format decimalLength 
[√] 万元的表示 scale
[√] 零值 不显示 showZeroValue
[√] 按下 = 号, 公式要能显示出来
[√] EnumEditor
[√] 其它editor
[√] cellType的注册机制

# 公式输入
[√] 如果已有公式, 则切入active时, 直接打开公式输入器
[√] 如果第一个字符是= 号, 并且仅输入了= 号, 则直接切换为公式输入器
[√] 在picking模式下, 是否可以正常获得picked cells
[√] 在picking模式下, 按下enter键 或 Esc 键是什么情况

# 细节
[√] 是否选中 isSelected
[√] 插入rows时, type==='auto'的处理

# 抽象公式的处理
[√] active抽象公式时, 如果发现抽象公式, 则弹出抽象公式编辑器
[√] 抽象公式编辑器的实现

# 重构
[√] cellTypes 合并为单独的文件夹

