From 135e5c0ef1e18825b57ac11c107b0d350ab85cae Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 17 Aug 2024 14:22:15 +0800 Subject: [PATCH] fix(sheets-data-validation): data-validation absolute offset (#3091) --- .../src/engine/utils/relative-formula.ts | 63 ------------------ packages/engine-formula/src/index.ts | 1 - .../src/services/dv-custom-formula.service.ts | 65 +++++++------------ .../src/utils/formula.ts | 4 ++ .../src/validators/custom-validator.ts | 33 +++++++++- 5 files changed, 59 insertions(+), 107 deletions(-) delete mode 100644 packages/engine-formula/src/engine/utils/relative-formula.ts diff --git a/packages/engine-formula/src/engine/utils/relative-formula.ts b/packages/engine-formula/src/engine/utils/relative-formula.ts deleted file mode 100644 index 8baaec822f98..000000000000 --- a/packages/engine-formula/src/engine/utils/relative-formula.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2023-present DreamNum Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Rectangle, Tools } from '@univerjs/core'; - -import type { LexerTreeBuilder } from '../analysis/lexer-tree-builder'; -import { generateStringWithSequence, sequenceNodeType } from './sequence'; -import { deserializeRangeWithSheet, serializeRange } from './reference'; - -export function isFormulaTransformable(lexerTreeBuilder: LexerTreeBuilder, formula: string) { - const originSequenceNodes = lexerTreeBuilder.sequenceNodesBuilder(formula); - if (!originSequenceNodes) { - return false; - } - // no reference node, return origin formula - if (originSequenceNodes.every((node) => typeof node === 'string' || node.nodeType !== sequenceNodeType.REFERENCE)) { - return false; - } - - return true; -} - -export function transformFormula(lexerTreeBuilder: LexerTreeBuilder, formula: string, originRow: number, originCol: number, targetRow: number, targetCol: number) { - if (!isFormulaTransformable(lexerTreeBuilder, formula)) { - return formula; - } - - const originSequenceNodes = lexerTreeBuilder.sequenceNodesBuilder(formula); - const getRangeFromCell = (row: number, col: number) => ({ startRow: row, endRow: row, startColumn: col, endColumn: col }); - const originRange = getRangeFromCell(originRow, originCol); - - const relativeRange = Rectangle.getRelativeRange(getRangeFromCell(targetRow, targetCol), originRange); - const sequenceNodes = Tools.deepClone(originSequenceNodes); - const transformSequenceNodes = Array.isArray(sequenceNodes) - ? sequenceNodes.map((node) => { - if (typeof node === 'object' && node.nodeType === sequenceNodeType.REFERENCE) { - const gridRangeName = deserializeRangeWithSheet(node.token); - const newRange = Rectangle.getPositionRange(relativeRange, gridRangeName.range); - const newToken = serializeRange(newRange); - return { - ...node, token: newToken, - }; - } - return node; - }) - : sequenceNodes; - const formulaString = transformSequenceNodes && generateStringWithSequence(transformSequenceNodes); - - return `=${formulaString}`; -} diff --git a/packages/engine-formula/src/index.ts b/packages/engine-formula/src/index.ts index 23fb2ffcd2e8..4f69747c5554 100644 --- a/packages/engine-formula/src/index.ts +++ b/packages/engine-formula/src/index.ts @@ -114,7 +114,6 @@ export { FormulaExecutedStateType, type IAllRuntimeData } from './services/runti export { isReferenceString } from './basics/regex'; export { matchRefDrawToken } from './basics/match-token'; export { IDefinedNamesService, DefinedNamesService, type IDefinedNamesServiceParam, type IDefinedNameMapItem } from './services/defined-names.service'; -export { isFormulaTransformable, transformFormula } from './engine/utils/relative-formula'; export { IFormulaRuntimeService, FormulaRuntimeService } from './services/runtime.service'; export { IFormulaCurrentConfigService, FormulaCurrentConfigService, type IFormulaDirtyData } from './services/current-data.service'; diff --git a/packages/sheets-data-validation/src/services/dv-custom-formula.service.ts b/packages/sheets-data-validation/src/services/dv-custom-formula.service.ts index bfb921fee142..9f762a560bbc 100644 --- a/packages/sheets-data-validation/src/services/dv-custom-formula.service.ts +++ b/packages/sheets-data-validation/src/services/dv-custom-formula.service.ts @@ -15,8 +15,8 @@ */ import type { IRange, ISheetDataValidationRule } from '@univerjs/core'; -import { DataValidationType, Disposable, Inject, isFormulaString, IUniverInstanceService, ObjectMatrix, Range, UniverInstanceType } from '@univerjs/core'; -import { isFormulaTransformable, LexerTreeBuilder, transformFormula } from '@univerjs/engine-formula'; +import { DataValidationType, Disposable, ILogService, Inject, isFormulaString, IUniverInstanceService, ObjectMatrix, Range, UniverInstanceType } from '@univerjs/core'; +import { LexerTreeBuilder } from '@univerjs/engine-formula'; import { DataValidationModel } from '@univerjs/data-validation'; import { RegisterOtherFormulaService } from '@univerjs/sheets-formula'; import { DataValidationCacheService } from './dv-cache.service'; @@ -46,7 +46,10 @@ type UnitId = string; type SubUnitId = string; type FormulaId = string; -// +function transformFormula(lexerTreeBuilder: LexerTreeBuilder, formula: string, originRow: number, originCol: number, targetRow: number, targetCol: number) { + return lexerTreeBuilder.moveFormulaRefOffset(formula, targetCol - originCol, targetRow - originRow); +} + export class DataValidationCustomFormulaService extends Disposable { private _formulaMap: Map>> = new Map(); /** @@ -64,7 +67,8 @@ export class DataValidationCustomFormulaService extends Disposable { @Inject(RegisterOtherFormulaService) private _registerOtherFormulaService: RegisterOtherFormulaService, @Inject(LexerTreeBuilder) private _lexerTreeBuilder: LexerTreeBuilder, @Inject(DataValidationModel) private readonly _dataValidationModel: DataValidationModel, - @Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService + @Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService, + @ILogService private readonly _logService: ILogService ) { super(); @@ -175,54 +179,36 @@ export class DataValidationCustomFormulaService extends Disposable { return; } - const isTransformable = isFormulaTransformable( - this._lexerTreeBuilder, - formula - ); - const originRow = ranges[0].startRow; const originCol = ranges[0].startColumn; let originFormulaId: string | undefined; - if (isTransformable) { - ranges.forEach((range) => { - Range.foreach(range, (row, column) => { - const relativeFormula = transformFormula( - this._lexerTreeBuilder, - formula, - originRow, - originCol, - row, - column - ); - const formulaId = this._registerFormula(unitId, subUnitId, ruleId, relativeFormula); - formulaMap.setValue(row, column, { - formulaId, + ranges.forEach((range) => { + Range.foreach(range, (row, column) => { + const relativeFormula = transformFormula( + this._lexerTreeBuilder, + formula, + originRow, + originCol, + row, + column + ); + const formulaId = this._registerFormula(unitId, subUnitId, ruleId, relativeFormula); + formulaMap.setValue(row, column, { + formulaId, // formulaText: relativeFormula, - ruleId, - }); - formulaCellMap.set(formulaId, { row, column }); + ruleId, }); + formulaCellMap.set(formulaId, { row, column }); }); - } else { - originFormulaId = this._registerFormula(unitId, subUnitId, ruleId, formula); - ranges.forEach((range) => { - Range.foreach(range, (row, col) => { - formulaMap.setValue(row, col, { - formulaId: originFormulaId!, - // formulaText: formula, - ruleId, - }); - }); - }); - } + }); ruleFormulaMap.set(ruleId, { formula, originCol, originRow, - isTransformable, formulaId: originFormulaId, + isTransformable: true, }); } @@ -266,7 +252,6 @@ export class DataValidationCustomFormulaService extends Disposable { const relativeText = transformFormula(this._lexerTreeBuilder, formula, originRow, originCol, row, col); const formulaId = this._registerFormula(unitId, subUnitId, ruleId, relativeText); formulaMap.setValue(row, col, { - // formulaText: relativeText, ruleId, formulaId, }); diff --git a/packages/sheets-data-validation/src/utils/formula.ts b/packages/sheets-data-validation/src/utils/formula.ts index 1e885b16a86f..154c5191672f 100644 --- a/packages/sheets-data-validation/src/utils/formula.ts +++ b/packages/sheets-data-validation/src/utils/formula.ts @@ -19,3 +19,7 @@ import type { ICellData, Nullable } from '@univerjs/core'; export function getFormulaResult(result: Nullable[][]>) { return result?.[0]?.[0]?.v; } + +export function getFormulaCellData(result: Nullable[][]>) { + return result?.[0]?.[0]; +} diff --git a/packages/sheets-data-validation/src/validators/custom-validator.ts b/packages/sheets-data-validation/src/validators/custom-validator.ts index 714f3fdba6bb..280e29a4d0be 100644 --- a/packages/sheets-data-validation/src/validators/custom-validator.ts +++ b/packages/sheets-data-validation/src/validators/custom-validator.ts @@ -14,13 +14,18 @@ * limitations under the License. */ -import { DataValidationType, isFormulaString } from '@univerjs/core'; +import { CellValueType, DataValidationType, isFormulaString, Tools } from '@univerjs/core'; import type { CellValue, DataValidationOperator, IDataValidationRule, IDataValidationRuleBase } from '@univerjs/core'; import type { IFormulaResult, IFormulaValidResult, IValidatorCellInfo } from '@univerjs/data-validation'; import { BaseDataValidator } from '@univerjs/data-validation'; +import { ERROR_TYPE_SET } from '@univerjs/engine-formula'; import { CUSTOM_FORMULA_INPUT_NAME } from '../views/formula-input'; import { DataValidationCustomFormulaService } from '../services/dv-custom-formula.service'; -import { getFormulaResult } from '../utils/formula'; +import { getFormulaCellData } from '../utils/formula'; + +function isLegalFormulaResult(res: string) { + return !(ERROR_TYPE_SET as Set).has(res); +} export class CustomFormulaValidator extends BaseDataValidator { override id: string = DataValidationType.CUSTOM; @@ -49,8 +54,30 @@ export class CustomFormulaValidator extends BaseDataValidator { override async isValidType(cellInfo: IValidatorCellInfo, _formula: IFormulaResult, _rule: IDataValidationRule): Promise { const { column, row, unitId, subUnitId } = cellInfo; const result = await this._customFormulaService.getCellFormulaValue(unitId, subUnitId, row, column); + const cellData = getFormulaCellData(result?.result); + const formulaResult = cellData?.v; + + if (Tools.isDefine(formulaResult) && formulaResult !== '') { + if (cellData!.t === CellValueType.BOOLEAN) { + return Boolean(formulaResult); + } + + if (typeof formulaResult === 'boolean') { + return formulaResult; + } + + if (typeof formulaResult === 'number') { + return Boolean(formulaResult); + } + + if (typeof formulaResult === 'string') { + return isLegalFormulaResult(formulaResult); + } + + return Boolean(formulaResult); + } - return Boolean(getFormulaResult(result?.result)); + return false; } override generateRuleErrorMessage(rule: IDataValidationRuleBase): string {