fix(sheets-data-validation): data validation validator status render error (#3752)

This commit is contained in:
zhangw
2024-10-15 16:11:04 +08:00
committed by GitHub
parent 421d0097fd
commit 555e9d5a7c
17 changed files with 239 additions and 46 deletions
+1 -1
View File
@@ -140,7 +140,7 @@ export { isBlackColor, isWhiteColor } from './shared/color/color-kit';
export { cellToRange } from './shared/common';
export { getIntersectRange } from './shared/range';
export { nameCharacterCheck } from './shared/name';
export { fromCallback, takeAfter } from './shared/rxjs';
export { bufferDebounceTime, fromCallback, takeAfter } from './shared/rxjs';
export { awaitTime } from './shared/timer';
export { Range } from './sheets/range';
export {
+14 -1
View File
@@ -14,8 +14,9 @@
* limitations under the License.
*/
import { Observable } from 'rxjs';
import type { OperatorFunction } from 'rxjs';
import type { IDisposable } from '../common/di';
import { debounceTime, map, Observable, tap } from 'rxjs';
type CallbackFn<T extends readonly unknown[]> = (cb: (...args: T) => void) => IDisposable;
@@ -57,3 +58,15 @@ export function takeAfter<T>(callback: (value: T) => boolean) {
};
}
export function bufferDebounceTime<T>(time: number = 0): OperatorFunction<T, T[]> {
return (source: Observable<T>) => {
let bufferedValues: T[] = [];
return source.pipe(
tap((value) => bufferedValues.push(value)),
debounceTime(time),
map(() => bufferedValues),
tap(() => bufferedValues = [])
);
};
}
+1 -1
View File
@@ -20,7 +20,7 @@ export {
} from './common/util';
export { DataValidationResourceController } from './controllers/dv-resource.controller';
export { type DataValidationChangeSource, DataValidationModel, type IRuleChange, type IValidStatusChange } from './models/data-validation-model';
export { type DataValidationChangeSource, DataValidationModel, type IRuleChange } from './models/data-validation-model';
export { UniverDataValidationPlugin } from './plugin';
export { DataValidatorRegistryScope, DataValidatorRegistryService } from './services/data-validator-registry.service';
@@ -14,12 +14,12 @@
* limitations under the License.
*/
import type { IDataValidationRule } from '@univerjs/core';
import type { IUpdateRulePayload } from '../types/interfaces/i-update-rule-payload';
import { Disposable, ILogService, Tools } from '@univerjs/core';
import { debounceTime, Subject } from 'rxjs';
import type { DataValidationStatus, IDataValidationRule } from '@univerjs/core';
import { getRuleOptions, getRuleSetting } from '../common/util';
import { UpdateRuleType } from '../types/enum/update-rule-type';
import type { IUpdateRulePayload } from '../types/interfaces/i-update-rule-payload';
type DataValidationChangeType = 'update' | 'add' | 'remove';
export type DataValidationChangeSource = 'command' | 'patched';
@@ -34,13 +34,6 @@ export interface IRuleChange {
oldRule?: IDataValidationRule;
}
export interface IValidStatusChange {
unitId: string;
subUnitId: string;
ruleId: string;
status: DataValidationStatus;
}
interface ISubUnitDataValidation {
map: Map<string, IDataValidationRule>; list: IDataValidationRule[];
}
@@ -15,13 +15,13 @@
*/
import type { CommandListener, ICommandInfo, IDisposable, IExecutionOptions, IRange, IWorkbookData, Nullable, ObjectMatrix, Workbook } from '@univerjs/core';
import type { IRuleChange, IValidStatusChange } from '@univerjs/data-validation';
import type { IRuleChange } from '@univerjs/data-validation';
import type { IUpdateCommandParams } from '@univerjs/docs-ui';
import type {
ISetSelectionsOperationParams,
ISheetCommandSharedParams,
} from '@univerjs/sheets';
import type { IAddSheetDataValidationCommandParams, IDataValidationResCache, IRemoveSheetAllDataValidationCommandParams, IRemoveSheetDataValidationCommandParams, IUpdateSheetDataValidationOptionsCommandParams, IUpdateSheetDataValidationRangeCommandParams, IUpdateSheetDataValidationSettingCommandParams } from '@univerjs/sheets-data-validation';
import type { IAddSheetDataValidationCommandParams, IDataValidationResCache, IRemoveSheetAllDataValidationCommandParams, IRemoveSheetDataValidationCommandParams, IUpdateSheetDataValidationOptionsCommandParams, IUpdateSheetDataValidationRangeCommandParams, IUpdateSheetDataValidationSettingCommandParams, IValidStatusChange } from '@univerjs/sheets-data-validation';
import type { ICanvasFloatDom } from '@univerjs/sheets-drawing-ui';
import type { ISheetHyperLinkInfo } from '@univerjs/sheets-hyper-link-ui';
import type { CommentUpdate, IAddCommentCommandParams, IDeleteCommentCommandParams } from '@univerjs/thread-comment';
@@ -21,9 +21,10 @@ import type { FilterModel } from '@univerjs/sheets-filter';
import type { FWorkbook, IFICanvasFloatDom } from './f-workbook';
import { Direction, ICommandService, Inject, Injector, ObjectMatrix, RANGE_TYPE } from '@univerjs/core';
import { DataValidationModel } from '@univerjs/data-validation';
import { deserializeRangeWithSheet } from '@univerjs/engine-formula';
import { copyRangeStyles, InsertColCommand, InsertRowCommand, MoveColsCommand, MoveRowsCommand, RemoveColCommand, RemoveRowCommand, SetColHiddenCommand, SetColWidthCommand, SetFrozenCommand, SetRangeValuesMutation, SetRowHeightCommand, SetRowHiddenCommand, SetSpecificColsVisibleCommand, SetSpecificRowsVisibleCommand, SetWorksheetRowIsAutoHeightCommand, SheetsSelectionsService } from '@univerjs/sheets';
import { DataValidationModel, SheetsDataValidationValidatorService } from '@univerjs/sheets-data-validation';
import { SheetsDataValidationValidatorService } from '@univerjs/sheets-data-validation';
import { SheetCanvasFloatDomManagerService } from '@univerjs/sheets-drawing-ui';
import { SheetsFilterService } from '@univerjs/sheets-filter';
import { SheetsThreadCommentModel } from '@univerjs/sheets-thread-comment';
@@ -19,7 +19,7 @@ import { DataValidationStatus, DataValidationType, ICommandService, Inject, Inte
import { DataValidatorRegistryService } from '@univerjs/data-validation';
import { IRenderManagerService } from '@univerjs/engine-render';
import { InterceptCellContentPriority, INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import { getCellValueOrigin, SheetDataValidationModel } from '@univerjs/sheets-data-validation';
import { DataValidationCacheService, getCellValueOrigin, SheetDataValidationModel } from '@univerjs/sheets-data-validation';
import { AutoHeightController, IEditorBridgeService, SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import { IMenuManagerService } from '@univerjs/ui';
import { bufferTime, filter } from 'rxjs';
@@ -44,6 +44,7 @@ export class SheetsDataValidationRenderController extends RxDisposable {
@Inject(SheetDataValidationModel) private readonly _sheetDataValidationModel: SheetDataValidationModel,
@Inject(DataValidatorRegistryService) private readonly _dataValidatorRegistryService: DataValidatorRegistryService,
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService,
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService,
@Optional(IEditorBridgeService) private readonly _editorBridgeService?: IEditorBridgeService
) {
super();
@@ -145,7 +146,7 @@ export class SheetsDataValidationRenderController extends RxDisposable {
if (!rule) {
return next(cell);
}
const validStatus = this._sheetDataValidationModel.validator(cell, rule, pos);
const validStatus = this._dataValidationCacheService.getValue(unitId, subUnitId, row, col)?.status ?? DataValidationStatus.VALID;
const validator = this._dataValidatorRegistryService.getValidatorItem(rule.type);
const cellOrigin = pos.rawData;
let cache: Nullable<CellValue>;
@@ -264,7 +265,8 @@ export class SheetsDataValidationMobileRenderController extends RxDisposable {
@Inject(AutoHeightController) private readonly _autoHeightController: AutoHeightController,
@Inject(DataValidatorRegistryService) private readonly _dataValidatorRegistryService: DataValidatorRegistryService,
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService,
@Inject(SheetDataValidationModel) private readonly _sheetDataValidationModel: SheetDataValidationModel
@Inject(SheetDataValidationModel) private readonly _sheetDataValidationModel: SheetDataValidationModel,
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService
) {
super();
@@ -293,7 +295,7 @@ export class SheetsDataValidationMobileRenderController extends RxDisposable {
if (!rule) {
return next(cell);
}
const validStatus = this._sheetDataValidationModel.validator(cell, rule, pos);
const validStatus = this._dataValidationCacheService.getValue(unitId, subUnitId, row, col)?.status ?? DataValidationStatus.VALID;
const validator = this._dataValidatorRegistryService.getValidatorItem(rule.type);
const cellOrigin = worksheet.getCellRaw(row, col);
const cellValue = getCellValueOrigin(cellOrigin);
@@ -0,0 +1,51 @@
/**
* 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 { Workbook } from '@univerjs/core';
import type { IRenderContext, IRenderModule } from '@univerjs/engine-render';
import type { IValidStatusChange } from '@univerjs/sheets-data-validation';
import { bufferDebounceTime, Disposable, Inject } from '@univerjs/core';
import { SheetDataValidationModel } from '@univerjs/sheets-data-validation';
import { SheetSkeletonManagerService } from '@univerjs/sheets-ui';
export class SheetsDataValidationReRenderController extends Disposable implements IRenderModule {
constructor(
private readonly _context: IRenderContext<Workbook>,
@Inject(SheetDataValidationModel) private readonly _sheetDataValidationModel: SheetDataValidationModel,
@Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService
) {
super();
this._initSkeletonChange();
}
private _initSkeletonChange() {
const reRender = (values: IValidStatusChange[]) => {
if (!values.length) {
return;
}
const sheetIds = new Set<string>();
values.forEach((value) => {
sheetIds.add(value.subUnitId);
});
sheetIds.forEach((sheetId) => {
this._sheetSkeletonManagerService.getWorksheetSkeleton(sheetId)?.skeleton.makeDirty(true);
});
this._context.mainComponent?.makeForceDirty();
};
this.disposeWithMe(this._sheetDataValidationModel.validStatusChange$.pipe(bufferDebounceTime(16)).subscribe(reRender));
}
}
@@ -14,9 +14,10 @@
* limitations under the License.
*/
import type { Dependency } from '@univerjs/core';
import type { Dependency, Workbook } from '@univerjs/core';
import type { IUniverSheetsDataValidationUIConfig } from './controllers/config.schema';
import { ICommandService, IConfigService, Inject, Injector, Plugin, UniverInstanceType } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { AddSheetDataValidationAndOpenCommand } from './commands/commands/data-validation-ui.command';
import {
CloseValidationPanelOperation,
@@ -31,6 +32,7 @@ import { DataValidationAutoFillController } from './controllers/dv-auto-fill.con
import { DataValidationCopyPasteController } from './controllers/dv-copy-paste.controller';
import { DataValidationPermissionController } from './controllers/dv-permission.controller';
import { SheetsDataValidationRenderController } from './controllers/dv-render.controller';
import { SheetsDataValidationReRenderController } from './controllers/dv-rerender.controller';
import { SheetsDataValidationUIController } from './controllers/dv-ui.controller';
import { DataValidationPanelService } from './services/data-validation-panel.service';
import { DataValidationDropdownManagerService } from './services/dropdown-manager.service';
@@ -86,6 +88,12 @@ export class UniverSheetsDataValidationMobileUIPlugin extends Plugin {
override onReady(): void {
this._injector.get(DataValidationCopyPasteController);
this._injector.get(DataValidationPermissionController);
const renderManager = this._injector.get(IRenderManagerService);
renderManager.registerRenderModule<Workbook>(
UniverInstanceType.UNIVER_SHEET,
[SheetsDataValidationReRenderController] as Dependency
);
}
override onRendered(): void {
@@ -14,9 +14,10 @@
* limitations under the License.
*/
import type { Dependency } from '@univerjs/core';
import type { Dependency, Workbook } from '@univerjs/core';
import type { IUniverSheetsDataValidationUIConfig } from './controllers/config.schema';
import { ICommandService, IConfigService, Inject, Injector, Plugin, UniverInstanceType } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { AddSheetDataValidationAndOpenCommand } from './commands/commands/data-validation-ui.command';
import {
CloseValidationPanelOperation,
@@ -32,6 +33,7 @@ import { DataValidationCopyPasteController } from './controllers/dv-copy-paste.c
import { DataValidationPermissionController } from './controllers/dv-permission.controller';
import { DataValidationRejectInputController } from './controllers/dv-reject-input.controller';
import { SheetsDataValidationRenderController } from './controllers/dv-render.controller';
import { SheetsDataValidationReRenderController } from './controllers/dv-rerender.controller';
import { SheetsDataValidationUIController } from './controllers/dv-ui.controller';
import { DataValidationPanelService } from './services/data-validation-panel.service';
import { DataValidationDropdownManagerService } from './services/dropdown-manager.service';
@@ -90,6 +92,12 @@ export class UniverSheetsDataValidationUIPlugin extends Plugin {
this._injector.get(DataValidationPermissionController);
this._injector.get(DataValidationRejectInputController);
this._injector.get(DataValidationAlertController);
const renderManager = this._injector.get(IRenderManagerService);
renderManager.registerRenderModule<Workbook>(
UniverInstanceType.UNIVER_SHEET,
[SheetsDataValidationReRenderController] as Dependency
);
}
override onRendered(): void {
+1 -4
View File
@@ -15,7 +15,7 @@
*/
export { DATA_VALIDATION_PLUGIN_NAME } from './common/const';
export { SheetDataValidationModel } from './models/sheet-data-validation-model';
export { type IValidStatusChange, SheetDataValidationModel } from './models/sheet-data-validation-model';
export { UniverSheetsDataValidationPlugin } from './plugin';
export { DataValidationCacheService } from './services/dv-cache.service';
export type { IDataValidationResCache } from './services/dv-cache.service';
@@ -32,9 +32,6 @@ export { CHECKBOX_FORMULA_1, CHECKBOX_FORMULA_2, CheckboxValidator, transformChe
export { ListMultipleValidator } from './validators/list-multiple-validator';
export { deserializeListOptions, getDataValidationCellValue, serializeListOptions } from './validators/util';
// TODO: @jikkai: I think this is a mistake, it should be exported from here. @weird94
export { DataValidationModel } from '@univerjs/data-validation';
// #region - all commands
export {
@@ -14,10 +14,10 @@
* limitations under the License.
*/
import type { DataValidationType, ICellDataForSheetInterceptor, ISheetDataValidationRule, Nullable } from '@univerjs/core';
import type { IRuleChange, IUpdateRulePayload, IValidStatusChange } from '@univerjs/data-validation';
import type { ISheetDataValidationRule } from '@univerjs/core';
import type { IRuleChange, IUpdateRulePayload } from '@univerjs/data-validation';
import type { IRemoveSheetMutationParams, ISheetLocation } from '@univerjs/sheets';
import { DataValidationStatus, Disposable, ICommandService, Inject, IUniverInstanceService } from '@univerjs/core';
import { DataValidationStatus, DataValidationType, Disposable, ICommandService, Inject, IUniverInstanceService } from '@univerjs/core';
import { DataValidationModel, DataValidatorRegistryService, UpdateRuleType } from '@univerjs/data-validation';
import { RemoveSheetMutation } from '@univerjs/sheets';
import { Subject } from 'rxjs';
@@ -27,6 +27,15 @@ import { DataValidationFormulaService } from '../services/dv-formula.service';
import { getCellValueOrigin } from '../utils/get-cell-data-origin';
import { RuleMatrix } from './rule-matrix';
export interface IValidStatusChange {
unitId: string;
subUnitId: string;
row: number;
col: number;
ruleId: string;
status: DataValidationStatus;
}
export class SheetDataValidationModel extends Disposable {
private readonly _ruleMatrixMap = new Map<string, Map<string, RuleMatrix>>();
private readonly _validStatusChange$ = new Subject<IValidStatusChange>();
@@ -132,7 +141,9 @@ export class SheetDataValidationModel extends Disposable {
ruleMatrix.addRule(rule);
this._dataValidationCacheService.addRule(unitId, subUnitId, rule);
this._dataValidationFormulaService.addRule(unitId, subUnitId, rule.uid, rule.formula1, rule.formula2);
this._dataValidationCustomFormulaService.addRule(unitId, subUnitId, rule);
if (rule.type === DataValidationType.CUSTOM) {
this._dataValidationCustomFormulaService.addRule(unitId, subUnitId, rule);
}
}
private _addRule(unitId: string, subUnitId: string, rule: ISheetDataValidationRule | ISheetDataValidationRule[]): void {
@@ -148,11 +159,20 @@ export class SheetDataValidationModel extends Disposable {
if (payload.type === UpdateRuleType.RANGE) {
ruleMatrix.updateRange(ruleId, payload.payload);
this._dataValidationCacheService.updateRuleRanges(unitId, subUnitId, ruleId, payload.payload, oldRule.ranges);
this._dataValidationCustomFormulaService.updateRuleRanges(unitId, subUnitId, ruleId, oldRule.ranges, payload.payload);
if (oldRule.type === DataValidationType.CUSTOM) {
this._dataValidationCustomFormulaService.updateRuleRanges(unitId, subUnitId, ruleId, oldRule.ranges, payload.payload);
}
} else if (payload.type === UpdateRuleType.SETTING) {
this._dataValidationCacheService.markRangeDirty(unitId, subUnitId, oldRule.ranges);
this._dataValidationFormulaService.updateRuleFormulaText(unitId, subUnitId, ruleId, payload.payload.formula1, payload.payload.formula2);
this._dataValidationCustomFormulaService.updateRuleFormula(unitId, subUnitId, ruleId, oldRule.ranges, payload.payload.formula1!);
if (oldRule.type === DataValidationType.CUSTOM) {
this._dataValidationCustomFormulaService.updateRuleFormula(unitId, subUnitId, ruleId, oldRule.ranges, payload.payload.formula1!);
} else if (payload.payload.type === DataValidationType.CUSTOM) {
this._dataValidationCustomFormulaService.addRule(unitId, subUnitId, {
...oldRule,
...payload.payload,
});
}
}
}
@@ -160,6 +180,9 @@ export class SheetDataValidationModel extends Disposable {
const ruleMatrix = this._ensureRuleMatrix(unitId, subUnitId);
ruleMatrix.removeRule(oldRule);
this._dataValidationCacheService.removeRule(unitId, subUnitId, oldRule);
if (oldRule.type === DataValidationType.CUSTOM) {
this._dataValidationCustomFormulaService.deleteByRuleId(unitId, subUnitId, oldRule.uid);
}
}
getValidator(type: DataValidationType) {
@@ -180,9 +203,11 @@ export class SheetDataValidationModel extends Disposable {
return this._dataValidationModel.getRuleById(unitId, subUnitId, ruleId);
}
validator(cell: Nullable<ICellDataForSheetInterceptor>, rule: ISheetDataValidationRule, pos: ISheetLocation, _onCompete?: (status: DataValidationStatus, changed: boolean) => void): DataValidationStatus {
validator(rule: ISheetDataValidationRule, pos: ISheetLocation, _onCompete?: (status: DataValidationStatus, changed: boolean) => void): DataValidationStatus {
const { col, row, unitId, subUnitId, worksheet } = pos;
const ruleId = rule.uid;
const formula1 = rule.formula1;
const formula2 = rule.formula2;
const onCompete = (status: DataValidationStatus, changed: boolean) => {
if (_onCompete) {
_onCompete(status, changed);
@@ -193,9 +218,13 @@ export class SheetDataValidationModel extends Disposable {
subUnitId,
ruleId: rule.uid,
status,
row,
col,
});
}
};
const cell = worksheet.getCellValueOnly(row, col);
const validator = this.getValidator(rule.type);
const cellRaw = worksheet.getCellRaw(row, col);
const cellValue = getCellValueOrigin(cellRaw);
@@ -204,12 +233,14 @@ export class SheetDataValidationModel extends Disposable {
if (validator) {
const cache = this._dataValidationCacheService.ensureCache(unitId, subUnitId);
const current = cache.getValue(row, col);
if (!current || current.value !== cellValue || current.interceptValue !== interceptValue || current.ruleId !== ruleId) {
if (!current || current.value !== cellValue || current.interceptValue !== interceptValue || current.ruleId !== ruleId || current.formula1 !== formula1 || current.formula2 !== formula2) {
cache.setValue(row, col, {
value: cellValue,
interceptValue,
status: DataValidationStatus.VALIDATING,
ruleId,
formula1: formula1 || '',
formula2: formula2 || '',
});
validator.validator(
{
@@ -231,6 +262,8 @@ export class SheetDataValidationModel extends Disposable {
status: realStatus,
ruleId,
interceptValue,
formula1: formula1 || '',
formula2: formula2 || '',
});
onCompete(realStatus, true);
});
@@ -90,6 +90,8 @@ export class UniverSheetsDataValidationPlugin extends Plugin {
this._commandService.registerCommand(command);
});
this._injector.get(DataValidationCacheService);
this._injector.get(SheetsDataValidationValidatorService);
this._injector.get(DataValidationRefRangeController);
}
@@ -14,8 +14,10 @@
* limitations under the License.
*/
import { ObjectMatrix, Range } from '@univerjs/core';
import type { CellValue, DataValidationStatus, IRange, ISheetDataValidationRule, Nullable } from '@univerjs/core';
import { Disposable, ICommandService, Inject, ObjectMatrix, Range } from '@univerjs/core';
import { type ISetRangeValuesMutationParams, SetRangeValuesMutation } from '@univerjs/sheets';
import { Subject } from 'rxjs';
export interface IDataValidationResCache {
value: Nullable<CellValue>;
@@ -23,10 +25,40 @@ export interface IDataValidationResCache {
status: DataValidationStatus;
ruleId: string;
temp?: boolean;
formula1: string;
formula2: string;
}
export class DataValidationCacheService {
export class DataValidationCacheService extends Disposable {
private _cacheMatrix: Map<string, Map<string, ObjectMatrix<Nullable<IDataValidationResCache>>>> = new Map();
private _dirtyRanges$ = new Subject<{ unitId: string; subUnitId: string; ranges: IRange[] }>();
readonly dirtyRanges$ = this._dirtyRanges$.asObservable();
constructor(
@Inject(ICommandService) private readonly _commandService: ICommandService
) {
super();
this._initDirtyRanges();
}
private _initDirtyRanges() {
this.disposeWithMe(this._commandService.onCommandExecuted((commandInfo) => {
if (commandInfo.id === SetRangeValuesMutation.id) {
const { cellValue, unitId, subUnitId } = commandInfo.params as ISetRangeValuesMutationParams;
if (cellValue) {
const range = new ObjectMatrix(cellValue).getDataRange();
if (range.endRow === -1) return;
this._dirtyRanges$.next({
unitId,
subUnitId,
ranges: [range],
});
}
}
}));
}
private _ensureCache(unitId: string, subUnitId: string) {
let unitMap = this._cacheMatrix.get(unitId);
@@ -86,6 +118,8 @@ export class DataValidationCacheService {
}
});
});
this._dirtyRanges$.next({ unitId, subUnitId, ranges: [...oldRanges, ...newRanges] });
}
markRangeDirty(unitId: string, subUnitId: string, ranges: IRange[]) {
@@ -95,11 +129,14 @@ export class DataValidationCacheService {
cache.setValue(row, col, undefined);
});
});
this._dirtyRanges$.next({ unitId, subUnitId, ranges });
}
markCellDirty(unitId: string, subUnitId: string, row: number, col: number) {
const cache = this._ensureCache(unitId, subUnitId);
cache.setValue(row, col, undefined);
this._dirtyRanges$.next({ unitId, subUnitId, ranges: [{ startRow: row, startColumn: col, endRow: row, endColumn: col }] });
}
private _deleteRange(unitId: string, subUnitId: string, ranges: IRange[]) {
@@ -109,6 +146,7 @@ export class DataValidationCacheService {
cache.realDeleteValue(row, col);
});
});
this._dirtyRanges$.next({ unitId, subUnitId, ranges });
}
getValue(unitId: string, subUnitId: string, row: number, col: number) {
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import { DataValidationType, Disposable, ILogService, Inject, isFormulaString, IUniverInstanceService, ObjectMatrix, Range, UniverInstanceType } from '@univerjs/core';
import type { IRange, ISheetDataValidationRule } from '@univerjs/core';
import { DataValidationType, Disposable, Inject, Injector, isFormulaString, IUniverInstanceService, ObjectMatrix, Range, UniverInstanceType } from '@univerjs/core';
import { DataValidationModel } from '@univerjs/data-validation';
import { LexerTreeBuilder } from '@univerjs/engine-formula';
import { RegisterOtherFormulaService } from '@univerjs/sheets-formula';
import type { IRange, ISheetDataValidationRule } from '@univerjs/core';
import { DataValidationCacheService } from './dv-cache.service';
interface IDataValidationFormula {
@@ -68,7 +68,7 @@ export class DataValidationCustomFormulaService extends Disposable {
@Inject(LexerTreeBuilder) private _lexerTreeBuilder: LexerTreeBuilder,
@Inject(DataValidationModel) private readonly _dataValidationModel: DataValidationModel,
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService,
@ILogService private readonly _logService: ILogService
@Inject(Injector) private readonly _injector: Injector
) {
super();
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import type { Nullable } from '@univerjs/core';
import type { IFormulaInfo, IOtherFormulaResult } from '@univerjs/sheets-formula';
import { Disposable, Inject, isFormulaString, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { DataValidationModel } from '@univerjs/data-validation';
import { RegisterOtherFormulaService } from '@univerjs/sheets-formula';
import type { Nullable } from '@univerjs/core';
import type { IFormulaInfo, IOtherFormulaResult } from '@univerjs/sheets-formula';
import { DataValidationCacheService } from './dv-cache.service';
type RuleId = string;
@@ -14,19 +14,65 @@
* limitations under the License.
*/
import { DataValidationStatus, Inject, IUniverInstanceService, Range, Tools, UniverInstanceType } from '@univerjs/core';
import type { IDataValidationRule, IRange, Nullable, ObjectMatrix, Workbook } from '@univerjs/core';
import type { IDataValidationResCache } from './dv-cache.service';
import { bufferDebounceTime, DataValidationStatus, Disposable, Inject, IUniverInstanceService, LifecycleService, LifecycleStages, Range, Tools, UniverInstanceType } from '@univerjs/core';
import { bufferWhen, filter } from 'rxjs';
import { SheetDataValidationModel } from '../models/sheet-data-validation-model';
import { DataValidationCacheService } from './dv-cache.service';
import type { IDataValidationResCache } from './dv-cache.service';
export class SheetsDataValidationValidatorService {
export class SheetsDataValidationValidatorService extends Disposable {
constructor(
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
@Inject(SheetDataValidationModel) private readonly _sheetDataValidationModel: SheetDataValidationModel,
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService,
@Inject(LifecycleService) private readonly _lifecycleService: LifecycleService
) {
super();
this._initRecalculate();
}
private _initRecalculate() {
const handleDirtyRanges = (ranges: { unitId: string; subUnitId: string; ranges: IRange[] }[]) => {
if (ranges.length === 0) {
return;
}
const workbook = this._univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
const worksheet = workbook?.getActiveSheet();
const map: Record<string, Record<string, IRange[]>> = {};
ranges.flat().forEach((range) => {
if (!map[range.unitId]) {
map[range.unitId] = {};
}
if (!map[range.unitId][range.subUnitId]) {
map[range.unitId][range.subUnitId] = [];
}
const workbook = this._univerInstanceService.getUnit<Workbook>(range.unitId, UniverInstanceType.UNIVER_SHEET);
const worksheet = workbook?.getSheetBySheetId(range.subUnitId);
if (!worksheet) {
return;
}
map[range.unitId][range.subUnitId].push(...range.ranges.map((range) => Range.transformRange(range, worksheet)));
});
Object.entries(map).forEach(([unitId, subUnitMap]) => {
Object.entries(subUnitMap).forEach(([subUnitId, ranges]) => {
if (workbook?.getUnitId() === unitId && worksheet?.getSheetId() === subUnitId) {
this.validatorRanges(unitId, subUnitId, ranges);
} else {
requestIdleCallback(() => {
this.validatorRanges(unitId, subUnitId, ranges);
});
}
});
});
};
this.disposeWithMe(this._dataValidationCacheService.dirtyRanges$.pipe(bufferWhen(() => this._lifecycleService.lifecycle$.pipe(filter((stage) => stage === LifecycleStages.Rendered)))).subscribe(handleDirtyRanges));
this.disposeWithMe(this._dataValidationCacheService.dirtyRanges$.pipe(filter(() => this._lifecycleService.stage >= LifecycleStages.Rendered), bufferDebounceTime(20)).subscribe(handleDirtyRanges));
}
async validatorCell(unitId: string, subUnitId: string, row: number, col: number) {
@@ -44,14 +90,15 @@ export class SheetsDataValidationValidatorService {
throw new Error(`row or col is not defined, row: ${row}, col: ${col}`);
}
const cell = worksheet.getCell(row, col);
const rule = this._sheetDataValidationModel.getRuleByLocation(unitId, subUnitId, row, col);
if (!rule) {
return DataValidationStatus.VALID;
}
return new Promise<DataValidationStatus>((resolve) => {
this._sheetDataValidationModel.validator(cell, rule, { unitId, subUnitId, row, col, worksheet, workbook }, resolve);
this._sheetDataValidationModel.validator(rule, { unitId, subUnitId, row, col, worksheet, workbook }, (status) => {
resolve(status);
});
});
}