diff --git a/packages/engine-formula/src/basics/common.ts b/packages/engine-formula/src/basics/common.ts index fd741f78f3b7..8a6121de6627 100644 --- a/packages/engine-formula/src/basics/common.ts +++ b/packages/engine-formula/src/basics/common.ts @@ -101,7 +101,7 @@ export interface IOtherFormulaData { } export interface INumfmtItemMap { - [unitId: string]: Nullable<{ [sheetId: string]: IObjectMatrixPrimitiveType }>; + [unitId: string]: Nullable<{ [sheetId: string]: IObjectMatrixPrimitiveType> }>; } /** @@ -113,7 +113,6 @@ export interface IFormulaDataItem { x?: number; // Offset from x direction y?: number; // Offset from y direction si?: string; // formulaId, - p?: string; // number format // row: number; // column: number; // sheetId: string; diff --git a/packages/engine-formula/src/commands/mutations/set-numfmt-formula-data.mutation.ts b/packages/engine-formula/src/commands/mutations/set-numfmt-formula-data.mutation.ts new file mode 100644 index 000000000000..7105cb4657c9 --- /dev/null +++ b/packages/engine-formula/src/commands/mutations/set-numfmt-formula-data.mutation.ts @@ -0,0 +1,36 @@ +/** + * 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 type { IMutation } from '@univerjs/core'; +import { CommandType } from '@univerjs/core'; +import type { IAccessor } from '@wendellhu/redi'; + +import type { INumfmtItemMap } from '../../basics/common'; +import { FormulaDataModel } from '../../models/formula-data.model'; + +export interface ISetNumfmtFormulaDataMutationParams { + numfmtItemMap: INumfmtItemMap; +} + +export const SetNumfmtFormulaDataMutation: IMutation = { + id: 'formula.mutation.set-numfmt-formula-data', + type: CommandType.MUTATION, + handler: (accessor: IAccessor, params: ISetNumfmtFormulaDataMutationParams) => { + const formulaDataModel = accessor.get(FormulaDataModel); + formulaDataModel.updateNumfmtItemMap(params.numfmtItemMap); + return true; + }, +}; diff --git a/packages/engine-formula/src/controller/calculate.controller.ts b/packages/engine-formula/src/controller/calculate.controller.ts index ac838526e6e0..e680ba135451 100644 --- a/packages/engine-formula/src/controller/calculate.controller.ts +++ b/packages/engine-formula/src/controller/calculate.controller.ts @@ -15,7 +15,7 @@ */ import type { ICommandInfo, IUnitRange } from '@univerjs/core'; -import { Disposable, ICommandService, IUniverInstanceService, LifecycleStages, OnLifecycle } from '@univerjs/core'; +import { Disposable, ICommandService, IUniverInstanceService, LifecycleStages, OnLifecycle, Tools } from '@univerjs/core'; import { Inject } from '@wendellhu/redi'; import type { IDirtyUnitFeatureMap, IDirtyUnitSheetNameMap, IFormulaData, INumfmtItemMap } from '../basics/common'; @@ -34,6 +34,7 @@ import { FormulaDataModel } from '../models/formula-data.model'; import { CalculateFormulaService } from '../services/calculate-formula.service'; import type { IAllRuntimeData } from '../services/runtime.service'; import { FormulaExecutedStateType } from '../services/runtime.service'; +import { SetNumfmtFormulaDataMutation } from '../commands/mutations/set-numfmt-formula-data.mutation'; @OnLifecycle(LifecycleStages.Ready, CalculateController) export class CalculateController extends Disposable { @@ -188,7 +189,7 @@ export class CalculateController extends Disposable { } private async _applyFormula(data: IAllRuntimeData) { - const { unitData, unitOtherData, arrayFormulaRange, arrayFormulaCellData, clearArrayFormulaCellData } = data; + const { unitData, unitOtherData, arrayFormulaRange, arrayFormulaCellData, clearArrayFormulaCellData, numfmtItemMap } = data; if (!unitData) { console.error('No sheetData from Formula Engine!'); @@ -217,6 +218,19 @@ export class CalculateController extends Disposable { ); } + // Synchronous to the main thread + if (!Tools.isEmptyObject(numfmtItemMap)) { + this._commandService.executeCommand( + SetNumfmtFormulaDataMutation.id, + { + numfmtItemMap, + }, + { + onlyLocal: true, + } + ); + } + this._commandService.executeCommand( SetFormulaCalculationResultMutation.id, { diff --git a/packages/engine-formula/src/controller/formula.controller.ts b/packages/engine-formula/src/controller/formula.controller.ts index 94acf29f348d..3291cbc3c97a 100644 --- a/packages/engine-formula/src/controller/formula.controller.ts +++ b/packages/engine-formula/src/controller/formula.controller.ts @@ -58,6 +58,7 @@ import { functionText } from '../functions/text/function-map'; import { functionUniver } from '../functions/univer/function-map'; import { functionWeb } from '../functions/web/function-map'; import { IFunctionService } from '../services/function.service'; +import { SetNumfmtFormulaDataMutation } from '../commands/mutations/set-numfmt-formula-data.mutation'; @OnLifecycle(LifecycleStages.Ready, FormulaController) export class FormulaController extends Disposable { @@ -85,6 +86,7 @@ export class FormulaController extends Disposable { SetFormulaCalculationStopMutation, SetFormulaCalculationNotificationMutation, SetFormulaCalculationResultMutation, + SetNumfmtFormulaDataMutation, SetDefinedNameMutation, RemoveDefinedNameMutation, diff --git a/packages/engine-formula/src/engine/analysis/__tests__/parser.spec.ts b/packages/engine-formula/src/engine/analysis/__tests__/parser.spec.ts index 0fe51ba21135..299a16153e92 100644 --- a/packages/engine-formula/src/engine/analysis/__tests__/parser.spec.ts +++ b/packages/engine-formula/src/engine/analysis/__tests__/parser.spec.ts @@ -59,6 +59,7 @@ describe('Test indirect', () => { formulaData: {}, arrayFormulaCellData: {}, forceCalculate: false, + numfmtItemMap: {}, dirtyRanges: [], dirtyNameMap: {}, dirtyUnitFeatureMap: {}, diff --git a/packages/engine-formula/src/engine/reference-object/base-reference-object.ts b/packages/engine-formula/src/engine/reference-object/base-reference-object.ts index f2ccb09ca797..e362956d4880 100644 --- a/packages/engine-formula/src/engine/reference-object/base-reference-object.ts +++ b/packages/engine-formula/src/engine/reference-object/base-reference-object.ts @@ -145,6 +145,9 @@ export class BaseReferenceObject extends ObjectClassType { return callback(new ErrorValueObject(ErrorType.VALUE), startRow, startColumn); } + const unitId = this._forcedUnitId || this._defaultUnitId; + const sheetId = this._forcedSheetId || this._defaultSheetId; + for (let r = startRow; r <= endRow; r++) { for (let c = startColumn; c <= endColumn; c++) { if (r < 0 || c < 0) { @@ -160,6 +163,9 @@ export class BaseReferenceObject extends ObjectClassType { const resultObjectValue = this.getCellValueObject(cell); + const pattern = this._numfmtItemData[unitId]?.[sheetId]?.[r]?.[c]; + pattern && resultObjectValue.setPattern(pattern); + result = callback(resultObjectValue, r, c); if (result === false) { @@ -177,7 +183,15 @@ export class BaseReferenceObject extends ObjectClassType { return new NumberValueObject(0, true); } - return this.getCellValueObject(cell); + const cellValueObject = this.getCellValueObject(cell); + + // Set numfmt pattern + const unitId = this._forcedUnitId || this._defaultUnitId; + const sheetId = this._forcedSheetId || this._defaultSheetId; + const numfmtItem = this._numfmtItemData[unitId]?.[sheetId]?.[startRow]?.[startColumn]; + numfmtItem && cellValueObject.setPattern(numfmtItem); + + return cellValueObject; } getRangeData() { @@ -290,7 +304,7 @@ export class BaseReferenceObject extends ObjectClassType { this._runtimeFeatureCellData = unitData; } - getNmfmtItemData() { + getNumfmtItemData() { return this._numfmtItemData; } @@ -471,10 +485,6 @@ export class BaseReferenceObject extends ObjectClassType { } arrayValueList[row][column] = valueObject; - - if (rowIndex === startRow && columnIndex === startColumn) { - return false; - } }); const arrayValueObjectData: IArrayValueObject = { diff --git a/packages/engine-formula/src/engine/reference-object/cell-reference-object.ts b/packages/engine-formula/src/engine/reference-object/cell-reference-object.ts index 7613562a10e7..47d4cbe58a96 100644 --- a/packages/engine-formula/src/engine/reference-object/cell-reference-object.ts +++ b/packages/engine-formula/src/engine/reference-object/cell-reference-object.ts @@ -96,6 +96,8 @@ export class CellReferenceObject extends BaseReferenceObject { rangeReferenceObject.setRuntimeData(this.getRuntimeData()); + rangeReferenceObject.setNumfmtItemData(this.getNumfmtItemData()); + rangeReferenceObject.setArrayFormulaCellData(this.getArrayFormulaCellData()); rangeReferenceObject.setRuntimeArrayFormulaCellData(this.getRuntimeArrayFormulaCellData()); diff --git a/packages/engine-formula/src/engine/value-object/primitive-object.ts b/packages/engine-formula/src/engine/value-object/primitive-object.ts index cb22709c64a2..f8face671a64 100644 --- a/packages/engine-formula/src/engine/value-object/primitive-object.ts +++ b/packages/engine-formula/src/engine/value-object/primitive-object.ts @@ -398,6 +398,9 @@ export class NumberValueObject extends BaseValueObject { return this; } + // Set number format + object.setPattern(this.getPattern() || valueObject.getPattern()); + return object; } @@ -418,6 +421,9 @@ export class NumberValueObject extends BaseValueObject { return this; } + // Set number format + object.setPattern(this.getPattern() || valueObject.getPattern()); + return object; } @@ -425,7 +431,12 @@ export class NumberValueObject extends BaseValueObject { if (valueObject.isArray()) { return valueObject.multiply(this); } - return this.multiplyBy(valueObject.getValue()); + const object = this.multiplyBy(valueObject.getValue()); + + // Set number format + object.setPattern(this.getPattern() || valueObject.getPattern()); + + return object; } override divided(valueObject: BaseValueObject): BaseValueObject { @@ -436,7 +447,12 @@ export class NumberValueObject extends BaseValueObject { } return (o as BaseValueObject).multiply(this); } - return this.dividedBy(valueObject.getValue()); + const object = this.dividedBy(valueObject.getValue()); + + // Set number format + object.setPattern(this.getPattern() || valueObject.getPattern()); + + return object; } override concatenateFront(valueObject: BaseValueObject): BaseValueObject { diff --git a/packages/engine-formula/src/functions/lookup/indirect/__test__/index.spec.ts b/packages/engine-formula/src/functions/lookup/indirect/__test__/index.spec.ts index d09cb1e6e8ac..031dd37243e4 100644 --- a/packages/engine-formula/src/functions/lookup/indirect/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/lookup/indirect/__test__/index.spec.ts @@ -60,6 +60,7 @@ describe('Test indirect', () => { formulaData: {}, arrayFormulaCellData: {}, forceCalculate: false, + numfmtItemMap: {}, dirtyRanges: [], dirtyNameMap: {}, dirtyUnitFeatureMap: {}, diff --git a/packages/engine-formula/src/functions/lookup/offset/__tests__/index.spec.ts b/packages/engine-formula/src/functions/lookup/offset/__tests__/index.spec.ts index 9a0e0a6c0931..266d1df435f6 100644 --- a/packages/engine-formula/src/functions/lookup/offset/__tests__/index.spec.ts +++ b/packages/engine-formula/src/functions/lookup/offset/__tests__/index.spec.ts @@ -57,6 +57,7 @@ describe('Test offset', () => { formulaData: {}, arrayFormulaCellData: {}, forceCalculate: false, + numfmtItemMap: {}, dirtyRanges: [], dirtyNameMap: {}, dirtyUnitFeatureMap: {}, diff --git a/packages/engine-formula/src/index.ts b/packages/engine-formula/src/index.ts index 19dbcd162ce2..6f741e399271 100644 --- a/packages/engine-formula/src/index.ts +++ b/packages/engine-formula/src/index.ts @@ -135,3 +135,5 @@ export { IFunctionService } from './services/function.service'; export { type IOtherFormulaManagerService, OtherFormulaManagerService } from './services/other-formula-manager.service'; export { FormulaExecuteStageType, type IExecutionInProgressParams } from './services/runtime.service'; export { FormulaExecutedStateType, type IAllRuntimeData } from './services/runtime.service'; +export { SetNumfmtFormulaDataMutation } from './commands/mutations/set-numfmt-formula-data.mutation'; +export type { ISetNumfmtFormulaDataMutationParams } from './commands/mutations/set-numfmt-formula-data.mutation'; diff --git a/packages/engine-formula/src/models/formula-data.model.ts b/packages/engine-formula/src/models/formula-data.model.ts index b549e78ad2b3..e595c6c46427 100644 --- a/packages/engine-formula/src/models/formula-data.model.ts +++ b/packages/engine-formula/src/models/formula-data.model.ts @@ -23,6 +23,7 @@ import type { IArrayFormulaUnitCellType, IFormulaData, IFormulaDataItem, + INumfmtItemMap, IRuntimeUnitDataType, ISheetData, IUnitData, @@ -43,6 +44,9 @@ export class FormulaDataModel extends Disposable { private _arrayFormulaCellData: IArrayFormulaUnitCellType = {}; + // TODO@Dushusir: Determine the node.js environment and synchronize to the resource plugin when SSC is used + private _numfmtItemMap: INumfmtItemMap = {}; + constructor( @IUniverInstanceService private readonly _currentUniverService: IUniverInstanceService, @Inject(LexerTreeBuilder) private readonly _lexerTreeBuilder: LexerTreeBuilder @@ -161,20 +165,55 @@ export class FormulaDataModel extends Disposable { this._formulaData = value; } + getArrayFormulaRange(): IArrayFormulaRangeType { + return this._arrayFormulaRange; + } + setArrayFormulaRange(value: IArrayFormulaRangeType) { this._arrayFormulaRange = value; } - getArrayFormulaRange(): IArrayFormulaRangeType { - return this._arrayFormulaRange; + getArrayFormulaCellData() { + return this._arrayFormulaCellData; } setArrayFormulaCellData(value: IArrayFormulaUnitCellType) { this._arrayFormulaCellData = value; } - getArrayFormulaCellData() { - return this._arrayFormulaCellData; + getNumfmtItemMap() { + return this._numfmtItemMap; + } + + updateNumfmtItemMap(value: INumfmtItemMap) { + Object.keys(value).forEach((unitId) => { + const sheetData = value[unitId]; + + if (sheetData == null) { + return true; + } + + if (this._numfmtItemMap[unitId] == null) { + this._numfmtItemMap[unitId] = {}; + } + + Object.keys(sheetData).forEach((sheetId) => { + const numfmtItemMap = sheetData[sheetId]; + const numfmtItemMatrix = new ObjectMatrix(numfmtItemMap); + + if (this._numfmtItemMap[unitId]![sheetId] == null) { + this._numfmtItemMap[unitId]![sheetId] = {}; + } + + numfmtItemMatrix.forValue((r, c, numfmtItem) => { + if (this._numfmtItemMap[unitId]![sheetId][r] == null) { + this._numfmtItemMap[unitId]![sheetId][r] = {}; + } + + this._numfmtItemMap[unitId]![sheetId][r][c] = numfmtItem; + }); + }); + }); } mergeArrayFormulaRange(formulaData: IArrayFormulaRangeType) { @@ -450,6 +489,46 @@ export class FormulaDataModel extends Disposable { }); } + updateNumfmtData( + unitId: string, + sheetId: string, + cellValue: IObjectMatrixPrimitiveType> + ) { + // remove the array formula range when cell value is null + + const arrayFormulaRange = this._arrayFormulaRange[unitId]?.[sheetId]; + const arrayFormulaRangeMatrix = new ObjectMatrix(arrayFormulaRange); + + const numfmtData = this._numfmtItemMap[unitId]?.[sheetId]; + + if (!numfmtData) return; + + const numfmtDataMatrix = new ObjectMatrix(numfmtData); + + const cellMatrix = new ObjectMatrix(cellValue); + cellMatrix.forValue((r, c, cell) => { + const formulaString = cell?.f || ''; + const formulaId = cell?.si || ''; + + const checkFormulaString = isFormulaString(formulaString); + const checkFormulaId = isFormulaId(formulaId); + + if (!checkFormulaString && !checkFormulaId) { + numfmtDataMatrix.setValue(r, c, null); + + const arrayFormulaRangeValue = arrayFormulaRangeMatrix.getValue(r, c); + if (arrayFormulaRangeValue) { + const { startRow, startColumn, endRow, endColumn } = arrayFormulaRangeValue; + for (let row = startRow; row <= endRow; row++) { + for (let column = startColumn; column <= endColumn; column++) { + numfmtDataMatrix.setValue(row, column, null); + } + } + } + } + }); + } + getFormulaItemBySId(sId: string, sheetId: string, unitId: string): Nullable { const formulaData = this._formulaData; if (formulaData[unitId] == null) { diff --git a/packages/engine-formula/src/services/runtime.service.ts b/packages/engine-formula/src/services/runtime.service.ts index f4e4bb37bf0e..88592ca7dbf8 100644 --- a/packages/engine-formula/src/services/runtime.service.ts +++ b/packages/engine-formula/src/services/runtime.service.ts @@ -21,6 +21,7 @@ import { createIdentifier } from '@wendellhu/redi'; import type { IArrayFormulaRangeType, IFeatureDirtyRangeType, + INumfmtItemMap, IRuntimeOtherUnitDataType, IRuntimeUnitDataType, } from '../basics/common'; @@ -66,6 +67,7 @@ export interface IAllRuntimeData { functionsExecutedState: FormulaExecutedStateType; arrayFormulaCellData: IRuntimeUnitDataType; clearArrayFormulaCellData: IRuntimeUnitDataType; + numfmtItemMap: INumfmtItemMap; runtimeFeatureRange: { [featureId: string]: IFeatureDirtyRangeType }; runtimeFeatureCellData: { [featureId: string]: IRuntimeUnitDataType }; @@ -194,6 +196,8 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime private _runtimeClearArrayFormulaCellData: IRuntimeUnitDataType = {}; + private _numfmtItemMap: INumfmtItemMap = {}; + private _runtimeFeatureRange: { [featureId: string]: IFeatureDirtyRangeType } = {}; private _runtimeFeatureCellData: { [featureId: string]: IRuntimeUnitDataType } = {}; @@ -330,6 +334,7 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime this._runtimeData = {}; this._runtimeOtherData = {}; this._unitArrayFormulaRange = {}; + this._numfmtItemMap = {}; this._runtimeArrayFormulaCellData = {}; this._runtimeClearArrayFormulaCellData = {}; @@ -407,6 +412,16 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime this._unitArrayFormulaRange[unitId] = {}; } + if (this._numfmtItemMap[unitId] == null) { + this._numfmtItemMap[unitId] = {}; + } + + if (this._numfmtItemMap[unitId]![sheetId] == null) { + this._numfmtItemMap[unitId]![sheetId] = {}; + } + + const numfmtItem = this._numfmtItemMap[unitId]![sheetId]; + const arrayFormulaRange = this._unitArrayFormulaRange[unitId]!; let arrayData = new ObjectMatrix(); @@ -454,9 +469,17 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime * then it is not treated as an array range and is directly assigned. */ if (startRow === endRow && startColumn === endColumn) { - const valueObject = this._objectValueToCellValue(objectValueRefOrArray.getFirstCell()); + const firstCell = objectValueRefOrArray.getFirstCell(); + const valueObject = this._objectValueToCellValue(firstCell); sheetData.setValue(row, column, valueObject); clearArrayUnitData.setValue(row, column, valueObject); + + if (numfmtItem[row] == null) { + numfmtItem[row] = {}; + } + + numfmtItem[row]![column] = firstCell.getPattern(); + return; } @@ -494,7 +517,20 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime } sheetData.setValue(row, column, { ...value }); } - arrayUnitData.setValue(rowIndex - startRow + row, columnIndex - startColumn + column, value); + + const currentRow = rowIndex - startRow + row; + const currentColumn = columnIndex - startColumn + column; + + arrayUnitData.setValue(currentRow, currentColumn, value); + + if (numfmtItem[currentRow] == null) { + numfmtItem[currentRow] = {}; + } + + const pattern = valueObject?.getPattern(); + if (pattern) { + numfmtItem[currentRow]![currentColumn] = pattern; + } }); } } else { @@ -512,6 +548,10 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime return this._unitArrayFormulaRange; } + getNumfmtItemMap() { + return this._numfmtItemMap; + } + getRuntimeOtherData() { return this._runtimeOtherData; } @@ -548,6 +588,7 @@ export class FormulaRuntimeService extends Disposable implements IFormulaRuntime functionsExecutedState: this._functionsExecutedState, arrayFormulaCellData: this.getRuntimeArrayFormulaCellData(), clearArrayFormulaCellData: this.getRuntimeClearArrayFormulaCellData(), + numfmtItemMap: this.getNumfmtItemMap(), runtimeFeatureRange: this.getRuntimeFeatureRange(), runtimeFeatureCellData: this.getRuntimeFeatureCellData(), diff --git a/packages/sheets-formula/package.json b/packages/sheets-formula/package.json index 704c8973e687..b807a3e3e46e 100644 --- a/packages/sheets-formula/package.json +++ b/packages/sheets-formula/package.json @@ -53,6 +53,7 @@ "@univerjs/engine-formula": "workspace:*", "@univerjs/engine-render": "workspace:*", "@univerjs/sheets": "workspace:*", + "@univerjs/sheets-numfmt": "workspace:*", "@univerjs/sheets-ui": "workspace:*", "@univerjs/ui": "workspace:*", "@wendellhu/redi": ">=0.12.13", @@ -71,6 +72,7 @@ "@univerjs/engine-render": "workspace:*", "@univerjs/shared": "workspace:*", "@univerjs/sheets": "workspace:*", + "@univerjs/sheets-numfmt": "workspace:*", "@univerjs/sheets-ui": "workspace:*", "@univerjs/ui": "workspace:*", "@vitejs/plugin-react": "^4.2.1", diff --git a/packages/sheets-formula/src/controllers/array-formula-display.controller.ts b/packages/sheets-formula/src/controllers/array-formula-display.controller.ts index 1d96918e03e5..90b6dc022da6 100644 --- a/packages/sheets-formula/src/controllers/array-formula-display.controller.ts +++ b/packages/sheets-formula/src/controllers/array-formula-display.controller.ts @@ -15,10 +15,11 @@ */ import type { ICommandInfo } from '@univerjs/core'; -import { Disposable, ICommandService, LifecycleStages, OnLifecycle } from '@univerjs/core'; +import { CellValueType, Disposable, ICommandService, LifecycleStages, OnLifecycle, ThemeService } from '@univerjs/core'; import type { ISetArrayFormulaDataMutationParams } from '@univerjs/engine-formula'; import { FormulaDataModel, SetArrayFormulaDataMutation } from '@univerjs/engine-formula'; import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets'; +import { getPatternPreview } from '@univerjs/sheets-numfmt'; import { Inject } from '@wendellhu/redi'; @OnLifecycle(LifecycleStages.Ready, ArrayFormulaDisplayController) @@ -26,7 +27,8 @@ export class ArrayFormulaDisplayController extends Disposable { constructor( @ICommandService private readonly _commandService: ICommandService, @Inject(SheetInterceptorService) private _sheetInterceptorService: SheetInterceptorService, - @Inject(FormulaDataModel) private readonly _formulaDataModel: FormulaDataModel + @Inject(FormulaDataModel) private readonly _formulaDataModel: FormulaDataModel, + @Inject(ThemeService) private readonly _themeService: ThemeService ) { super(); @@ -78,6 +80,37 @@ export class ArrayFormulaDisplayController extends Disposable { return next(cell); } + const numfmtItemMap = this._formulaDataModel.getNumfmtItemMap(); + const numfmtItem = numfmtItemMap[unitId]?.[subUnitId]?.[row]?.[col]; + + if (numfmtItem) { + const value = cellData?.v; + const type = cellData?.t; + + if (value == null || type !== CellValueType.NUMBER) { + return next(cell); + } + + const info = getPatternPreview(numfmtItem, value as number); + + if (info.color) { + const colorMap = this._themeService.getCurrentTheme(); + const color = colorMap[`${info.color}500`]; + return { + ...cell, + v: info.result, + t: CellValueType.STRING, + s: { cl: { rgb: color } }, + }; + } + + return { + ...cell, + v: info.result, + t: CellValueType.STRING, + }; + } + return next({ ...cell, ...cellData }); }, }) diff --git a/packages/sheets-formula/src/controllers/numfmt-formula-display.controller.ts b/packages/sheets-formula/src/controllers/numfmt-formula-display.controller.ts new file mode 100644 index 000000000000..4ec00ae773e8 --- /dev/null +++ b/packages/sheets-formula/src/controllers/numfmt-formula-display.controller.ts @@ -0,0 +1,108 @@ +/** + * 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 type { ICommandInfo } from '@univerjs/core'; +import { CellValueType, Disposable, ICommandService, LifecycleStages, OnLifecycle, ThemeService } from '@univerjs/core'; +import type { ISetNumfmtFormulaDataMutationParams } from '@univerjs/engine-formula'; +import { FormulaDataModel, SetNumfmtFormulaDataMutation } from '@univerjs/engine-formula'; +import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets'; +import { getPatternPreview } from '@univerjs/sheets-numfmt'; +import { Inject } from '@wendellhu/redi'; + +@OnLifecycle(LifecycleStages.Ready, NumfmtFormulaDisplayController) +export class NumfmtFormulaDisplayController extends Disposable { + constructor( + @ICommandService private readonly _commandService: ICommandService, + @Inject(SheetInterceptorService) private _sheetInterceptorService: SheetInterceptorService, + @Inject(FormulaDataModel) private readonly _formulaDataModel: FormulaDataModel, + @Inject(ThemeService) private readonly _themeService: ThemeService + ) { + super(); + + this._initialize(); + } + + private _initialize(): void { + this._commandExecutedListener(); + + this._initInterceptorCellContent(); + } + + private _commandExecutedListener() { + this.disposeWithMe( + this._commandService.onCommandExecuted((command: ICommandInfo) => { + // Synchronous data from worker + if (command.id !== SetNumfmtFormulaDataMutation.id) { + return; + } + + const params = command.params as ISetNumfmtFormulaDataMutationParams; + + if (params == null) { + return; + } + + const { numfmtItemMap } = params; + this._formulaDataModel.updateNumfmtItemMap(numfmtItemMap); + }) + ); + } + + private _initInterceptorCellContent() { + this.disposeWithMe( + this._sheetInterceptorService.intercept(INTERCEPTOR_POINT.CELL_CONTENT, { + priority: 98, + handler: (cell, location, next) => { + const { unitId, subUnitId, row, col } = location; + + const numfmtItemMap = this._formulaDataModel.getNumfmtItemMap(); + const numfmtItem = numfmtItemMap[unitId]?.[subUnitId]?.[row]?.[col]; + + if (numfmtItem == null) { + return next(cell); + } + + const rawValue = location.worksheet.getCellRaw(row, col); + const value = rawValue?.v; + const type = rawValue?.t; + + if (value == null || type !== CellValueType.NUMBER) { + return next(cell); + } + + const info = getPatternPreview(numfmtItem, value as number); + + if (info.color) { + const colorMap = this._themeService.getCurrentTheme(); + const color = colorMap[`${info.color}500`]; + return { + ...cell, + v: info.result, + t: CellValueType.STRING, + s: { cl: { rgb: color } }, + }; + } + + return { + ...cell, + v: info.result, + t: CellValueType.STRING, + }; + }, + }) + ); + } +} diff --git a/packages/sheets-formula/src/controllers/trigger-calculation.controller.ts b/packages/sheets-formula/src/controllers/trigger-calculation.controller.ts index e7f3c3bb9630..475a4ca8f022 100644 --- a/packages/sheets-formula/src/controllers/trigger-calculation.controller.ts +++ b/packages/sheets-formula/src/controllers/trigger-calculation.controller.ts @@ -15,11 +15,10 @@ */ import type { ICommandInfo, IUnitRange } from '@univerjs/core'; -import { Disposable, ICommandService, LifecycleStages, ObjectMatrix, OnLifecycle, Tools } from '@univerjs/core'; +import { Disposable, ICommandService, LifecycleStages, OnLifecycle } from '@univerjs/core'; import type { IDirtyUnitFeatureMap, IDirtyUnitSheetNameMap, - IFormulaDataItem, INumfmtItemMap, ISetFormulaCalculationNotificationMutation, } from '@univerjs/engine-formula'; @@ -171,51 +170,27 @@ export class TriggerCalculationController extends Disposable { numfmtItemMap[unitId]![sheetId] = {}; } - if (!Tools.isEmptyObject(numfmtItemMap[unitId]![sheetId])) { - return; - } + const numfmtModel = this._numfmtService.getModel(unitId, sheetId); + + if (!numfmtModel) return; + + const refMode = this._numfmtService.getRefModel(unitId); + + numfmtModel.forValue((row, col, numfmtValue) => { + if (numfmtValue && refMode) { + const refValue = refMode.getValue(numfmtValue?.i); - const numfmtItem = this._numfmtService.getModel(unitId, sheetId); - if (!numfmtItem) return; + if (!refValue) return; - numfmtItem.forValue((row, col, numfmt) => { - if (numfmt) { if (numfmtItemMap[unitId]![sheetId][row] == null) { numfmtItemMap[unitId]![sheetId][row] = {}; } - numfmtItemMap[unitId]![sheetId][row][col] = numfmt.i; + numfmtItemMap[unitId]![sheetId][row][col] = refValue.pattern; } }); }); - const formulaData = this._formulaDataModel.getFormulaData(); - Object.keys(formulaData).forEach((unitId) => { - const unitData = formulaData[unitId]; - if (!unitData) return; - Object.keys(unitData).forEach((sheetId) => { - const sheetData = unitData[sheetId]; - const formulaDataItemMatrix = new ObjectMatrix(sheetData); - formulaDataItemMatrix.forValue((row, col, formulaDataItem) => { - if (formulaDataItem?.n) { - if (numfmtItemMap[unitId] == null) { - numfmtItemMap[unitId] = {}; - } - - if (numfmtItemMap[unitId]![sheetId] == null) { - numfmtItemMap[unitId]![sheetId] = {}; - } - - if (numfmtItemMap[unitId]![sheetId][row] == null) { - numfmtItemMap[unitId]![sheetId][row] = {}; - } - - numfmtItemMap[unitId]![sheetId][row][col] = formulaDataItem.n; - } - }); - }); - }); - return { dirtyRanges: allDirtyRanges, dirtyNameMap: allDirtyNameMap, diff --git a/packages/sheets-formula/src/controllers/update-formula.controller.ts b/packages/sheets-formula/src/controllers/update-formula.controller.ts index b0de86020c90..b2a506284d4c 100644 --- a/packages/sheets-formula/src/controllers/update-formula.controller.ts +++ b/packages/sheets-formula/src/controllers/update-formula.controller.ts @@ -50,6 +50,7 @@ import { serializeRangeToRefString, SetArrayFormulaDataMutation, SetFormulaDataMutation, + SetNumfmtFormulaDataMutation, } from '@univerjs/engine-formula'; import type { IDeleteRangeMoveLeftCommandParams, @@ -253,6 +254,7 @@ export class UpdateFormulaController extends Disposable { this._formulaDataModel.updateFormulaData(unitId, sheetId, cellValue); this._formulaDataModel.updateArrayFormulaCellData(unitId, sheetId, cellValue); this._formulaDataModel.updateArrayFormulaRange(unitId, sheetId, cellValue); + this._formulaDataModel.updateNumfmtData(unitId, sheetId, cellValue); this._commandService.executeCommand( SetFormulaDataMutation.id, @@ -275,6 +277,16 @@ export class UpdateFormulaController extends Disposable { remove: true, // remove array formula range shape } ); + + this._commandService.executeCommand( + SetNumfmtFormulaDataMutation.id, + { + numfmtItemMap: this._formulaDataModel.getNumfmtItemMap(), + }, + { + onlyLocal: true, + } + ); } private _handleRemoveSheetMutation(params: IRemoveSheetMutationParams) { @@ -378,6 +390,7 @@ export class UpdateFormulaController extends Disposable { if (result) { const { unitSheetNameMap } = this._formulaDataModel.getCalculateData(); let oldFormulaData = this._formulaDataModel.getFormulaData(); + const oldNumfmtItemMap = this._formulaDataModel.getNumfmtItemMap(); // change formula reference const { newFormulaData: formulaData, refRanges } = this._getFormulaReferenceMoveInfo( @@ -436,6 +449,12 @@ export class UpdateFormulaController extends Disposable { formulaData: this._formulaDataModel.getFormulaData(), }); + // offset numfmtItemMap + const offsetNumfmtItemMap = offsetFormula(oldNumfmtItemMap, command, unitId, sheetId, selections); + this._commandService.executeCommand(SetNumfmtFormulaDataMutation.id, { + numfmtItemMap: offsetNumfmtItemMap, + }); + return this._getUpdateFormulaMutations(oldFormulaData, offsetFormulaData); } diff --git a/packages/sheets-formula/src/formula-ui-plugin.ts b/packages/sheets-formula/src/formula-ui-plugin.ts index f1f4b22b0b98..38077a78ea64 100644 --- a/packages/sheets-formula/src/formula-ui-plugin.ts +++ b/packages/sheets-formula/src/formula-ui-plugin.ts @@ -40,6 +40,7 @@ import { import { FormulaInputService, IFormulaInputService } from './services/formula-input.service'; import { FormulaPromptService, IFormulaPromptService } from './services/prompt.service'; import { IRegisterFunctionService, RegisterFunctionService } from './services/register-function.service'; +import { NumfmtFormulaDisplayController } from './controllers/numfmt-formula-display.controller'; /** * The configuration of the formula UI plugin. @@ -86,6 +87,7 @@ export class UniverSheetsFormulaPlugin extends Plugin { [FormulaAutoFillController], [FormulaClipboardController], [ArrayFormulaDisplayController], + [NumfmtFormulaDisplayController], [TriggerCalculationController], [UpdateFormulaController], [FormulaEditorShowController], diff --git a/packages/sheets-numfmt/src/index.ts b/packages/sheets-numfmt/src/index.ts index e252b9ead633..901bfa7a3669 100644 --- a/packages/sheets-numfmt/src/index.ts +++ b/packages/sheets-numfmt/src/index.ts @@ -17,3 +17,4 @@ export { type ISetNumfmtCommandParams, SetNumfmtCommand } from './commands/commands/set-numfmt.command'; export { enUS, zhCN } from './locale'; export { UniverSheetsNumfmtPlugin } from './numfmt-plugin'; +export { getPatternPreview } from './utils/pattern'; diff --git a/packages/sheets-ui/src/controllers/status-bar.controller.ts b/packages/sheets-ui/src/controllers/status-bar.controller.ts index f31940a408a2..bad2b8b4a206 100644 --- a/packages/sheets-ui/src/controllers/status-bar.controller.ts +++ b/packages/sheets-ui/src/controllers/status-bar.controller.ts @@ -56,7 +56,7 @@ export class StatusBarController extends Disposable { ) { super(); - this._init(); + // this._init(); } private _init(): void { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8a18e72125d4..74cd712af120 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -757,6 +757,9 @@ importers: '@univerjs/sheets': specifier: workspace:* version: link:../sheets + '@univerjs/sheets-numfmt': + specifier: workspace:* + version: link:../sheets-numfmt '@univerjs/sheets-ui': specifier: workspace:* version: link:../sheets-ui