mirror of
https://github.com/dream-num/univer.git
synced 2026-09-24 15:32:18 +08:00
refactor(data-validation): data validation validator (#4141)
This commit is contained in:
@@ -190,7 +190,7 @@ export * from './types/interfaces';
|
||||
export * from './types/interfaces';
|
||||
export type { ICellCustomRender, ICellRenderContext } from './types/interfaces/i-cell-custom-render';
|
||||
export type { IDataValidationRule, IDataValidationRuleBase, IDataValidationRuleInfo, IDataValidationRuleOptions, ISheetDataValidationRule } from './types/interfaces/i-data-validation';
|
||||
export { type IRTreeItem, RTree } from './shared/r-tree';
|
||||
export { type BBox, type IRTreeItem, RBush, RTree } from './shared/r-tree';
|
||||
|
||||
export { type IUniverConfig, Univer } from './univer';
|
||||
export { isNodeEnv } from './shared/tools';
|
||||
|
||||
@@ -361,3 +361,5 @@ export class RTree {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { type BBox, RBush };
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { DataValidationOperator } from '../enum/data-validation-operator';
|
||||
import type { DataValidationType } from '../enum/data-validation-type';
|
||||
import type { DataValidationImeMode } from '../enum/data-validation-ime-mode';
|
||||
import type { DataValidationErrorStyle } from '../enum/data-validation-error-style';
|
||||
import type { DataValidationRenderMode } from '../enum/data-validation-render-mode';
|
||||
import type { IRange } from '../../sheets/typedef';
|
||||
import type { DataValidationErrorStyle } from '../enum/data-validation-error-style';
|
||||
import type { DataValidationImeMode } from '../enum/data-validation-ime-mode';
|
||||
import type { DataValidationOperator } from '../enum/data-validation-operator';
|
||||
import type { DataValidationRenderMode } from '../enum/data-validation-render-mode';
|
||||
import type { DataValidationType } from '../enum/data-validation-type';
|
||||
|
||||
// TODO@weird94: should be moved outside of the or package
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface IDataValidationRuleBase {
|
||||
/**
|
||||
* data validation type
|
||||
*/
|
||||
type: DataValidationType;
|
||||
type: DataValidationType | string;
|
||||
allowBlank?: boolean;
|
||||
/**
|
||||
* data validation creteria
|
||||
|
||||
@@ -57,7 +57,7 @@ export class DataValidatorRegistryService {
|
||||
}
|
||||
}
|
||||
|
||||
register(validator: BaseDataValidator<any>) {
|
||||
register(validator: BaseDataValidator) {
|
||||
this._validatorMap.set(validator.id, validator);
|
||||
|
||||
if (Array.isArray(validator.scopes)) {
|
||||
|
||||
@@ -59,12 +59,14 @@ export interface IFormulaValidResult {
|
||||
formula2?: string;
|
||||
}
|
||||
|
||||
export abstract class BaseDataValidator<DataType = CellValue> {
|
||||
export abstract class BaseDataValidator {
|
||||
abstract id: string;
|
||||
abstract title: string;
|
||||
abstract operators: DataValidationOperator[];
|
||||
abstract scopes: string[] | string;
|
||||
|
||||
offsetFormulaByRange = true;
|
||||
|
||||
// #region UI related
|
||||
|
||||
formulaInput: string | undefined = undefined;
|
||||
@@ -127,7 +129,7 @@ export abstract class BaseDataValidator<DataType = CellValue> {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string, row: number, column: number): Promise<IFormulaResult>;
|
||||
abstract parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string, row: number, column: number): Promise<IFormulaResult<number | undefined>>;
|
||||
|
||||
abstract validatorFormula(rule: IDataValidationRule, unitId: string, subUnitId: string): IFormulaValidResult;
|
||||
|
||||
@@ -142,41 +144,82 @@ export abstract class BaseDataValidator<DataType = CellValue> {
|
||||
return true;
|
||||
};
|
||||
|
||||
transform(cellInfo: IValidatorCellInfo, formula: IFormulaResult, rule: IDataValidationRule): IValidatorCellInfo<DataType> {
|
||||
return cellInfo as IValidatorCellInfo<DataType>;
|
||||
transform(cellInfo: IValidatorCellInfo, formula: IFormulaResult, rule: IDataValidationRule): IValidatorCellInfo<number> {
|
||||
return cellInfo as IValidatorCellInfo<number>;
|
||||
};
|
||||
|
||||
async validatorIsEqual(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
async validatorIsEqual(cellInfo: IValidatorCellInfo<CellValue>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async validatorIsNotEqual(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
return cellValue === formula1;
|
||||
}
|
||||
|
||||
async validatorIsBetween(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
async validatorIsNotEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async validatorIsNotBetween(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
return cellInfo.value !== formula1;
|
||||
}
|
||||
|
||||
async validatorIsGreaterThan(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
async validatorIsBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async validatorIsGreaterThanOrEqual(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value >= start && cellInfo.value <= end;
|
||||
}
|
||||
|
||||
async validatorIsLessThan(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
async validatorIsNotBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value < start || cellInfo.value > end;
|
||||
}
|
||||
|
||||
async validatorIsLessThanOrEqual(cellInfo: IValidatorCellInfo<DataType>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
async validatorIsGreaterThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value > formula1;
|
||||
}
|
||||
|
||||
async validatorIsGreaterThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value >= formula1;
|
||||
}
|
||||
|
||||
async validatorIsLessThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value < formula1;
|
||||
}
|
||||
|
||||
async validatorIsLessThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellInfo.value <= formula1;
|
||||
}
|
||||
|
||||
async validator(cellInfo: IValidatorCellInfo, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { value: cellValue, unitId, subUnitId } = cellInfo;
|
||||
|
||||
@@ -101,9 +101,6 @@ export class DataValidationAutoFillController extends Disposable {
|
||||
redos: redoMutations,
|
||||
};
|
||||
};
|
||||
const disabledDataVallation = [
|
||||
DataValidationType.CHECKBOX,
|
||||
];
|
||||
const hook: ISheetAutoFillHook = {
|
||||
id: DATA_VALIDATION_PLUGIN_NAME,
|
||||
onBeforeFillData: (location) => {
|
||||
@@ -111,7 +108,7 @@ export class DataValidationAutoFillController extends Disposable {
|
||||
for (const row of sourceRange.rows) {
|
||||
for (const col of sourceRange.cols) {
|
||||
const dv = this._sheetDataValidationModel.getRuleByLocation(unitId, subUnitId, row, col);
|
||||
if (dv && disabledDataVallation.indexOf(dv.type) > -1) {
|
||||
if (dv && dv.type === DataValidationType.CHECKBOX) {
|
||||
this._autoFillService.setDisableApplyType(APPLY_TYPE.SERIES, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flatten-js/interval-tree": "^1.1.3",
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/data-validation": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
|
||||
@@ -23,7 +23,7 @@ import { AddDataValidationMutation, DataValidatorRegistryService, getRuleOptions
|
||||
import { LexerTreeBuilder } from '@univerjs/engine-formula';
|
||||
import { getSheetCommandTarget, SetRangeValuesMutation, SetRangeValuesUndoMutationFactory } from '@univerjs/sheets';
|
||||
import { SheetDataValidationModel } from '../../models/sheet-data-validation-model';
|
||||
import { isCustomFormulaType } from '../../utils/formula';
|
||||
import { shouldOffsetFormulaByRange } from '../../utils/formula';
|
||||
import { getStringCellValue } from '../../utils/get-cell-data-origin';
|
||||
import { CHECKBOX_FORMULA_1, CHECKBOX_FORMULA_2, type CheckboxValidator } from '../../validators';
|
||||
|
||||
@@ -57,6 +57,7 @@ export function getDataValidationDiffMutations(
|
||||
fillDefaultValue = true
|
||||
) {
|
||||
const lexerTreeBuilder = accessor.get(LexerTreeBuilder);
|
||||
const validatorRegistryService = accessor.get(DataValidatorRegistryService);
|
||||
const redoMutations: IMutationInfo[] = [];
|
||||
const undoMutations: IMutationInfo[] = [];
|
||||
const sheetDataValidationModel = accessor.get(SheetDataValidationModel);
|
||||
@@ -115,7 +116,7 @@ export function getDataValidationDiffMutations(
|
||||
});
|
||||
break;
|
||||
case 'update': {
|
||||
if (isCustomFormulaType(diff.rule.type)) {
|
||||
if (shouldOffsetFormulaByRange(diff.rule.type, validatorRegistryService)) {
|
||||
const originRow = diff.oldRanges[0].startRow;
|
||||
const originColumn = diff.oldRanges[0].startColumn;
|
||||
const newRow = diff.newRanges[0].startRow;
|
||||
|
||||
@@ -17,17 +17,18 @@
|
||||
import type { IDisposable, IMutationInfo, ISheetDataValidationRule } from '@univerjs/core';
|
||||
import type { IUpdateDataValidationMutationParams } from '@univerjs/data-validation';
|
||||
import { Disposable, generateRandomId, Inject, toDisposable } from '@univerjs/core';
|
||||
import { AddDataValidationMutation, RemoveDataValidationMutation, UpdateDataValidationMutation, UpdateRuleType } from '@univerjs/data-validation';
|
||||
import { AddDataValidationMutation, DataValidatorRegistryService, RemoveDataValidationMutation, UpdateDataValidationMutation, UpdateRuleType } from '@univerjs/data-validation';
|
||||
import { FormulaRefRangeService } from '@univerjs/sheets-formula';
|
||||
import { SheetDataValidationModel } from '../models/sheet-data-validation-model';
|
||||
import { isCustomFormulaType } from '../utils/formula';
|
||||
import { shouldOffsetFormulaByRange } from '../utils/formula';
|
||||
|
||||
export class DataValidationFormulaRefRangeController extends Disposable {
|
||||
private _disposableMap: Map<string, IDisposable> = new Map();
|
||||
|
||||
constructor(
|
||||
@Inject(SheetDataValidationModel) private _dataValidationModel: SheetDataValidationModel,
|
||||
@Inject(FormulaRefRangeService) private _formulaRefRangeService: FormulaRefRangeService
|
||||
@Inject(FormulaRefRangeService) private _formulaRefRangeService: FormulaRefRangeService,
|
||||
@Inject(DataValidatorRegistryService) private _validatorRegistryService: DataValidatorRegistryService
|
||||
) {
|
||||
super();
|
||||
this._initRefRange();
|
||||
@@ -38,7 +39,7 @@ export class DataValidationFormulaRefRangeController extends Disposable {
|
||||
}
|
||||
|
||||
registerRule = (unitId: string, subUnitId: string, rule: ISheetDataValidationRule) => {
|
||||
if (!isCustomFormulaType(rule.type)) {
|
||||
if (!shouldOffsetFormulaByRange(rule.type, this._validatorRegistryService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,13 @@ import type { ISheetDataValidationRule } from '@univerjs/core';
|
||||
import type { IRemoveDataValidationMutationParams, IUpdateDataValidationMutationParams } from '@univerjs/data-validation';
|
||||
import type { EffectRefRangeParams } from '@univerjs/sheets';
|
||||
import { Disposable, Inject, Injector, isRangesEqual, toDisposable } from '@univerjs/core';
|
||||
import { RemoveDataValidationMutation, UpdateDataValidationMutation, UpdateRuleType } from '@univerjs/data-validation';
|
||||
import { DataValidatorRegistryService, RemoveDataValidationMutation, UpdateDataValidationMutation, UpdateRuleType } from '@univerjs/data-validation';
|
||||
import { handleCommonDefaultRangeChangeWithEffectRefCommands, RefRangeService } from '@univerjs/sheets';
|
||||
import { FormulaRefRangeService } from '@univerjs/sheets-formula';
|
||||
import { removeDataValidationUndoFactory } from '../commands/commands/data-validation.command';
|
||||
import { SheetDataValidationModel } from '../models/sheet-data-validation-model';
|
||||
import { DataValidationCustomFormulaService } from '../services/dv-custom-formula.service';
|
||||
import { DataValidationFormulaService } from '../services/dv-formula.service';
|
||||
import { isCustomFormulaType } from '../utils/formula';
|
||||
import { shouldOffsetFormulaByRange } from '../utils/formula';
|
||||
|
||||
export class DataValidationRefRangeController extends Disposable {
|
||||
private _disposableMap: Map<string, Set<() => void>> = new Map();
|
||||
@@ -34,9 +33,9 @@ export class DataValidationRefRangeController extends Disposable {
|
||||
@Inject(SheetDataValidationModel) private _dataValidationModel: SheetDataValidationModel,
|
||||
@Inject(Injector) private _injector: Injector,
|
||||
@Inject(RefRangeService) private _refRangeService: RefRangeService,
|
||||
@Inject(DataValidationCustomFormulaService) private _dataValidationCustomFormulaService: DataValidationCustomFormulaService,
|
||||
@Inject(DataValidationFormulaService) private _dataValidationFormulaService: DataValidationFormulaService,
|
||||
@Inject(FormulaRefRangeService) private _formulaRefRangeService: FormulaRefRangeService
|
||||
@Inject(FormulaRefRangeService) private _formulaRefRangeService: FormulaRefRangeService,
|
||||
@Inject(DataValidatorRegistryService) private _validatorRegistryService: DataValidatorRegistryService
|
||||
) {
|
||||
super();
|
||||
this._initRefRange();
|
||||
@@ -47,7 +46,7 @@ export class DataValidationRefRangeController extends Disposable {
|
||||
}
|
||||
|
||||
registerRule = (unitId: string, subUnitId: string, rule: ISheetDataValidationRule) => {
|
||||
if (isCustomFormulaType(rule.type)) {
|
||||
if (shouldOffsetFormulaByRange(rule.type, this._validatorRegistryService)) {
|
||||
return;
|
||||
}
|
||||
this.register(unitId, subUnitId, rule);
|
||||
|
||||
@@ -73,7 +73,7 @@ export class FDataValidationBuilder {
|
||||
*
|
||||
* @returns {DataValidationType} The data validation type
|
||||
*/
|
||||
getCriteriaType(): DataValidationType {
|
||||
getCriteriaType(): DataValidationType | string {
|
||||
return this._rule.type;
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ export class FDataValidationBuilder {
|
||||
* operator is a DataValidationOperator enum value, formula1 is the first formula, and formula2 is the second formula.
|
||||
* @return The current instance of the FDataValidationBuilder class, allowing for method chaining.
|
||||
*/
|
||||
withCriteriaValues(type: DataValidationType, values: [DataValidationOperator, string, string]): this {
|
||||
withCriteriaValues(type: DataValidationType | string, values: [DataValidationOperator, string, string]): this {
|
||||
this._rule.type = type;
|
||||
this._rule.operator = values[0];
|
||||
this._rule.formula1 = values[1];
|
||||
|
||||
@@ -46,7 +46,7 @@ export class FDataValidation {
|
||||
*
|
||||
* @returns The data validation type
|
||||
*/
|
||||
getCriteriaType(): DataValidationType {
|
||||
getCriteriaType(): DataValidationType | string {
|
||||
return this.rule.type;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IRange, ISheetDataValidationRule, IUniverInstanceService, Workbook } from '@univerjs/core';
|
||||
import IntervalTree from '@flatten-js/interval-tree';
|
||||
import { debounce, Range, Rectangle, Tools, UniverInstanceType } from '@univerjs/core';
|
||||
import type { BBox, IRange, ISheetDataValidationRule, IUniverInstanceService, Workbook } from '@univerjs/core';
|
||||
|
||||
interface IRuleItem {
|
||||
import { debounce, Range, RBush, Rectangle, Tools, UniverInstanceType } from '@univerjs/core';
|
||||
|
||||
interface IRuleItem extends BBox {
|
||||
ruleId: string;
|
||||
startRow: number;
|
||||
endRow: number;
|
||||
|
||||
}
|
||||
|
||||
export type RangeMutation = {
|
||||
@@ -41,7 +40,7 @@ export type RangeMutation = {
|
||||
|
||||
export class RuleMatrix {
|
||||
private _map: Map<string, IRange[]>;
|
||||
private _tree = new Map<number, IntervalTree<string>>();
|
||||
private _tree = new RBush<IRuleItem>();
|
||||
private _dirty = true;
|
||||
|
||||
constructor(
|
||||
@@ -59,33 +58,20 @@ export class RuleMatrix {
|
||||
if (!this._dirty || this._disableTree) {
|
||||
return;
|
||||
}
|
||||
const map = new Map<number, IRuleItem[]>();
|
||||
this._tree.clear();
|
||||
const items: IRuleItem[] = [];
|
||||
this._map.forEach((ranges, ruleId) => {
|
||||
ranges.forEach((range) => {
|
||||
for (let col = range.startColumn; col <= range.endColumn; col++) {
|
||||
let items = map.get(col);
|
||||
if (!items) {
|
||||
items = [];
|
||||
map.set(col, items);
|
||||
}
|
||||
items.push({
|
||||
startRow: range.startRow,
|
||||
endRow: range.endRow,
|
||||
ruleId,
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
minX: range.startRow,
|
||||
maxX: range.endRow,
|
||||
minY: range.startColumn,
|
||||
maxY: range.endColumn,
|
||||
ruleId,
|
||||
});
|
||||
});
|
||||
});
|
||||
const treeMap = new Map<number, IntervalTree<string>>();
|
||||
map.forEach((items, col) => {
|
||||
const tree = new IntervalTree<string>();
|
||||
items.forEach((item) => {
|
||||
tree.insert([item.startRow, item.endRow], item.ruleId);
|
||||
});
|
||||
|
||||
treeMap.set(col, tree);
|
||||
});
|
||||
this._tree = treeMap;
|
||||
this._tree.load(items);
|
||||
this._dirty = false;
|
||||
};
|
||||
|
||||
@@ -95,13 +81,12 @@ export class RuleMatrix {
|
||||
return this._univerInstanceService.getUnit<Workbook>(this._unitId, UniverInstanceType.UNIVER_SHEET)?.getSheetBySheetId(this._subUnitId);
|
||||
}
|
||||
|
||||
addRule(rule: ISheetDataValidationRule) {
|
||||
private _addRule(ruleId: string, _ranges: IRange[]) {
|
||||
if (!this._worksheet) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ruleId = rule.uid;
|
||||
const ranges = rule.ranges.map((range) => Range.transformRange(range, this._worksheet!));
|
||||
const ranges = _ranges.map((range) => Range.transformRange(range, this._worksheet!));
|
||||
|
||||
this._map.forEach((value, key) => {
|
||||
const newRanges = Rectangle.subtractMulti(value, ranges);
|
||||
@@ -117,6 +102,10 @@ export class RuleMatrix {
|
||||
this._debonceBuildTree();
|
||||
}
|
||||
|
||||
addRule(rule: ISheetDataValidationRule) {
|
||||
this._addRule(rule.uid, rule.ranges);
|
||||
}
|
||||
|
||||
removeRange(_ranges: IRange[]) {
|
||||
if (!this._worksheet) {
|
||||
return;
|
||||
@@ -134,30 +123,19 @@ export class RuleMatrix {
|
||||
this._debonceBuildTree();
|
||||
}
|
||||
|
||||
removeRule(rule: ISheetDataValidationRule) {
|
||||
this._map.delete(rule.uid);
|
||||
private _removeRule(ruleId: string) {
|
||||
this._map.delete(ruleId);
|
||||
this._dirty = true;
|
||||
this._debonceBuildTree();
|
||||
}
|
||||
|
||||
updateRange(ruleId: string, _newRanges: IRange[]) {
|
||||
if (!this._worksheet) {
|
||||
return;
|
||||
}
|
||||
this._map.delete(ruleId);
|
||||
const ranges = _newRanges.map((range) => Range.transformRange(range, this._worksheet!));
|
||||
removeRule(rule: ISheetDataValidationRule) {
|
||||
this._removeRule(rule.uid);
|
||||
}
|
||||
|
||||
this._map.forEach((value, key) => {
|
||||
const newRanges = Rectangle.subtractMulti(value, ranges);
|
||||
if (newRanges.length === 0) {
|
||||
this._map.delete(key);
|
||||
} else {
|
||||
this._map.set(key, newRanges);
|
||||
}
|
||||
});
|
||||
this._map.set(ruleId, ranges);
|
||||
this._dirty = true;
|
||||
this._debonceBuildTree();
|
||||
updateRange(ruleId: string, _newRanges: IRange[]) {
|
||||
this._removeRule(ruleId);
|
||||
this._addRule(ruleId, _newRanges);
|
||||
}
|
||||
|
||||
addRangeRules(rules: { id: string;ranges: IRange[] }[]) {
|
||||
@@ -277,11 +255,13 @@ export class RuleMatrix {
|
||||
if (this._dirty) {
|
||||
this._buildTree();
|
||||
}
|
||||
const tree = this._tree.get(col);
|
||||
if (!tree) {
|
||||
return undefined;
|
||||
}
|
||||
const result = tree.search([row, row]);
|
||||
return result.length > 0 ? result[0] : undefined;
|
||||
|
||||
const result = this._tree.search({
|
||||
minX: row,
|
||||
maxX: row,
|
||||
minY: col,
|
||||
maxY: col,
|
||||
});
|
||||
return result.length > 0 ? result[0].ruleId : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ export class SheetDataValidationModel extends Disposable {
|
||||
this._dataValidationCustomFormulaService.deleteByRuleId(unitId, subUnitId, oldRule.uid);
|
||||
}
|
||||
|
||||
getValidator(type: DataValidationType) {
|
||||
getValidator(type: DataValidationType | string) {
|
||||
return this._dataValidatorRegistryService.getValidatorItem(type);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
|
||||
import type { DataValidationStatus, IRange, ISheetDataValidationRule, Nullable } from '@univerjs/core';
|
||||
import { Disposable, ICommandService, Inject, ObjectMatrix, Range } from '@univerjs/core';
|
||||
import { type ISetRangeValuesMutationParams, SetRangeValuesMutation } from '@univerjs/sheets';
|
||||
import type { IRemoveSheetMutationParams, ISetRangeValuesMutationParams } from '@univerjs/sheets';
|
||||
import { Disposable, ICommandService, Inject, IUniverInstanceService, ObjectMatrix, Range, UniverInstanceType } from '@univerjs/core';
|
||||
import { RemoveSheetMutation, SetRangeValuesMutation } from '@univerjs/sheets';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export class DataValidationCacheService extends Disposable {
|
||||
@@ -26,10 +27,12 @@ export class DataValidationCacheService extends Disposable {
|
||||
readonly dirtyRanges$ = this._dirtyRanges$.asObservable();
|
||||
|
||||
constructor(
|
||||
@Inject(ICommandService) private readonly _commandService: ICommandService
|
||||
@Inject(ICommandService) private readonly _commandService: ICommandService,
|
||||
@Inject(IUniverInstanceService) private readonly _univerInstanceService: IUniverInstanceService
|
||||
) {
|
||||
super();
|
||||
this._initDirtyRanges();
|
||||
this._initSheetRemove();
|
||||
}
|
||||
|
||||
private _initDirtyRanges() {
|
||||
@@ -45,6 +48,21 @@ export class DataValidationCacheService extends Disposable {
|
||||
}));
|
||||
}
|
||||
|
||||
private _initSheetRemove() {
|
||||
this.disposeWithMe(this._commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id === RemoveSheetMutation.id) {
|
||||
const { unitId, subUnitId } = commandInfo.params as IRemoveSheetMutationParams;
|
||||
this._cacheMatrix.get(unitId)?.delete(subUnitId);
|
||||
}
|
||||
}));
|
||||
|
||||
this.disposeWithMe(this._univerInstanceService.unitDisposed$.subscribe((univerInstance) => {
|
||||
if (univerInstance.type === UniverInstanceType.UNIVER_SHEET) {
|
||||
this._cacheMatrix.delete(univerInstance.getUnitId());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _ensureCache(unitId: string, subUnitId: string) {
|
||||
let unitMap = this._cacheMatrix.get(unitId);
|
||||
|
||||
@@ -86,12 +104,6 @@ export class DataValidationCacheService extends Disposable {
|
||||
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[]) {
|
||||
const cache = this._ensureCache(unitId, subUnitId);
|
||||
ranges.forEach((range) => {
|
||||
@@ -106,9 +118,4 @@ export class DataValidationCacheService extends Disposable {
|
||||
const cache = this._ensureCache(unitId, subUnitId);
|
||||
return cache.getValue(row, col);
|
||||
}
|
||||
|
||||
setValue(unitId: string, subUnitId: string, row: number, col: number, value: DataValidationStatus) {
|
||||
const cache = this._ensureCache(unitId, subUnitId);
|
||||
return cache.setValue(row, col, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
import type { IRange, ISheetDataValidationRule } from '@univerjs/core';
|
||||
import { Disposable, Inject, isFormulaString, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { DataValidationModel } from '@univerjs/data-validation';
|
||||
import { DataValidationModel, DataValidatorRegistryService } from '@univerjs/data-validation';
|
||||
import { RegisterOtherFormulaService } from '@univerjs/sheets-formula';
|
||||
import { getFormulaCellData, isCustomFormulaType } from '../utils/formula';
|
||||
import { getFormulaCellData, shouldOffsetFormulaByRange } from '../utils/formula';
|
||||
import { DataValidationCacheService } from './dv-cache.service';
|
||||
|
||||
interface IFormulaData {
|
||||
@@ -43,7 +43,8 @@ export class DataValidationCustomFormulaService extends Disposable {
|
||||
@IUniverInstanceService private readonly _instanceSrv: IUniverInstanceService,
|
||||
@Inject(RegisterOtherFormulaService) private _registerOtherFormulaService: RegisterOtherFormulaService,
|
||||
@Inject(DataValidationModel) private readonly _dataValidationModel: DataValidationModel,
|
||||
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService
|
||||
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService,
|
||||
@Inject(DataValidatorRegistryService) private readonly _validatorRegistryService: DataValidatorRegistryService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -156,7 +157,7 @@ export class DataValidationCustomFormulaService extends Disposable {
|
||||
}
|
||||
|
||||
addRule(unitId: string, subUnitId: string, rule: ISheetDataValidationRule) {
|
||||
if (isCustomFormulaType(rule.type)) {
|
||||
if (shouldOffsetFormulaByRange(rule.type, this._validatorRegistryService)) {
|
||||
const { ranges, formula1, formula2, uid: ruleId } = rule;
|
||||
this._addFormulaByRange(unitId, subUnitId, ruleId, formula1, formula2, ranges);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
import type { ISheetDataValidationRule, Nullable } from '@univerjs/core';
|
||||
import type { IFormulaInfo, IOtherFormulaResult } from '@univerjs/sheets-formula';
|
||||
import { DataValidationType, Disposable, Inject, isFormulaString, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { DataValidationModel } from '@univerjs/data-validation';
|
||||
import { DataValidationModel, DataValidatorRegistryService } from '@univerjs/data-validation';
|
||||
import { RegisterOtherFormulaService } from '@univerjs/sheets-formula';
|
||||
import { isCustomFormulaType } from '../utils/formula';
|
||||
import { shouldOffsetFormulaByRange } from '../utils/formula';
|
||||
import { DataValidationCacheService } from './dv-cache.service';
|
||||
|
||||
type RuleId = string;
|
||||
@@ -33,7 +33,8 @@ export class DataValidationFormulaService extends Disposable {
|
||||
@IUniverInstanceService private readonly _instanceService: IUniverInstanceService,
|
||||
@Inject(RegisterOtherFormulaService) private _registerOtherFormulaService: RegisterOtherFormulaService,
|
||||
@Inject(DataValidationCacheService) private readonly _dataValidationCacheService: DataValidationCacheService,
|
||||
@Inject(DataValidationModel) private readonly _dataValidationModel: DataValidationModel
|
||||
@Inject(DataValidationModel) private readonly _dataValidationModel: DataValidationModel,
|
||||
@Inject(DataValidatorRegistryService) private readonly _validatorRegistryService: DataValidatorRegistryService
|
||||
) {
|
||||
super();
|
||||
this._initFormulaResultHandler();
|
||||
@@ -87,7 +88,7 @@ export class DataValidationFormulaService extends Disposable {
|
||||
}
|
||||
|
||||
addRule(unitId: string, subUnitId: string, rule: ISheetDataValidationRule) {
|
||||
if (!isCustomFormulaType(rule.type) && rule.type !== DataValidationType.CHECKBOX) {
|
||||
if (!shouldOffsetFormulaByRange(rule.type, this._validatorRegistryService) && rule.type !== DataValidationType.CHECKBOX) {
|
||||
const { formula1, formula2, uid: ruleId } = rule;
|
||||
const isFormula1Legal = isFormulaString(formula1);
|
||||
const isFormula2Legal = isFormulaString(formula2);
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataValidationType, type ICellData, type Nullable } from '@univerjs/core';
|
||||
import type { DataValidationType, ICellData, Nullable } from '@univerjs/core';
|
||||
import type { DataValidatorRegistryService } from '@univerjs/data-validation';
|
||||
import { ERROR_TYPE_SET } from '@univerjs/engine-formula';
|
||||
|
||||
export function getFormulaResult(result: Nullable<Nullable<ICellData>[][]>) {
|
||||
@@ -32,14 +33,7 @@ export function isLegalFormulaResult(res: string) {
|
||||
/**
|
||||
* Judge if the data-validation's formula need to be offseted by ranges
|
||||
*/
|
||||
export function isCustomFormulaType(type: DataValidationType) {
|
||||
// types not in this list is formula type
|
||||
const invalidTypes = [
|
||||
DataValidationType.LIST,
|
||||
DataValidationType.LIST_MULTIPLE,
|
||||
DataValidationType.CHECKBOX,
|
||||
DataValidationType.ANY,
|
||||
];
|
||||
|
||||
return !invalidTypes.includes(type);
|
||||
export function shouldOffsetFormulaByRange(type: DataValidationType | string, validatorRegistryService: DataValidatorRegistryService) {
|
||||
const validator = validatorRegistryService.getValidatorItem(type);
|
||||
return validator?.offsetFormulaByRange ?? false;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ export class AnyValidator extends BaseDataValidator {
|
||||
override operators: DataValidationOperator[] = [];
|
||||
override scopes: string | string[] = ['sheet'];
|
||||
|
||||
override readonly offsetFormulaByRange = false;
|
||||
|
||||
override async parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string): Promise<IFormulaResult> {
|
||||
return {
|
||||
formula1: rule.formula1,
|
||||
|
||||
@@ -54,6 +54,8 @@ export class CheckboxValidator extends BaseDataValidator {
|
||||
override operators: DataValidationOperator[] = [];
|
||||
override scopes: string | string[] = ['sheet'];
|
||||
|
||||
override readonly offsetFormulaByRange = false;
|
||||
|
||||
private _formulaService = this.injector.get(DataValidationFormulaService);
|
||||
|
||||
override skipDefaultFontRender = (rule: ISheetDataValidationRule, cellValue: Nullable<CellValue>, pos: { unitId: string; subUnitId: string; row: number; column: number }) => {
|
||||
|
||||
@@ -46,7 +46,7 @@ const transformDate2SerialNumber = (value: Nullable<CellValue>) => {
|
||||
return numfmt.parseDate(dayjs(value).format('YYYY-MM-DD HH:mm:ss'))?.v as number | undefined;
|
||||
};
|
||||
|
||||
export class DateValidator extends BaseDataValidator<number> {
|
||||
export class DateValidator extends BaseDataValidator {
|
||||
id: string = DataValidationType.DATE;
|
||||
title: string = 'dataValidation.date.title';
|
||||
operators: DataValidationOperator[] = [
|
||||
@@ -156,78 +156,6 @@ export class DateValidator extends BaseDataValidator<number> {
|
||||
};
|
||||
}
|
||||
|
||||
override async validatorIsEqual(cellInfo: IValidatorCellInfo<CellValue>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellValue === formula1;
|
||||
}
|
||||
|
||||
override async validatorIsNotEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellInfo.value !== formula1;
|
||||
}
|
||||
|
||||
override async validatorIsBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value >= start && cellInfo.value <= end;
|
||||
}
|
||||
|
||||
override async validatorIsNotBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value < start || cellInfo.value > end;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value > formula1;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value >= formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value < formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value <= formula1;
|
||||
}
|
||||
|
||||
override get operatorNames() {
|
||||
return this.operators.map((operator) => this.localeService.t(DateOperatorNameMap[operator]));
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export function getCellValueNumber(cellValue: CellValue) {
|
||||
return +cellValue;
|
||||
}
|
||||
|
||||
export class DecimalValidator extends BaseDataValidator<number> {
|
||||
export class DecimalValidator extends BaseDataValidator {
|
||||
private readonly _customFormulaService = this.injector.get(DataValidationCustomFormulaService);
|
||||
id: string = DataValidationType.DECIMAL;
|
||||
private readonly _lexerTreeBuilder = this.injector.get(LexerTreeBuilder);
|
||||
@@ -126,79 +126,6 @@ export class DecimalValidator extends BaseDataValidator<number> {
|
||||
};
|
||||
}
|
||||
|
||||
override async validatorIsEqual(cellInfo: IValidatorCellInfo<CellValue>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellValue === formula1;
|
||||
}
|
||||
|
||||
override async validatorIsNotEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellInfo.value !== formula1;
|
||||
}
|
||||
|
||||
override async validatorIsBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value >= start && cellInfo.value <= end;
|
||||
}
|
||||
|
||||
override async validatorIsNotBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value < start || cellInfo.value > end;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value > formula1;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value >= formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value < formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellInfo.value <= formula1;
|
||||
}
|
||||
|
||||
override generateRuleErrorMessage(rule: IDataValidationRuleBase, position: ISheetLocationBase) {
|
||||
if (!rule.operator) {
|
||||
return this.titleStr;
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ListValidator } from './list-validator';
|
||||
export class ListMultipleValidator extends ListValidator {
|
||||
override id: string = DataValidationType.LIST_MULTIPLE;
|
||||
override title: string = 'dataValidation.listMultiple.title';
|
||||
override readonly offsetFormulaByRange = false;
|
||||
|
||||
override skipDefaultFontRender = () => {
|
||||
return true;
|
||||
|
||||
@@ -93,6 +93,8 @@ export class ListValidator extends BaseDataValidator {
|
||||
private _lexer = this.injector.get(LexerTreeBuilder);
|
||||
private _univerInstanceService = this.injector.get(IUniverInstanceService);
|
||||
|
||||
override readonly offsetFormulaByRange = false;
|
||||
|
||||
id: string = DataValidationType.LIST;
|
||||
title: string = 'dataValidation.list.title';
|
||||
operators: DataValidationOperator[] = [];
|
||||
@@ -146,24 +148,25 @@ export class ListValidator extends BaseDataValidator {
|
||||
return deserializeListOptions(cellString);
|
||||
}
|
||||
|
||||
override async parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string): Promise<IFormulaResult<string[] | undefined>> {
|
||||
const { formula1 = '' } = rule;
|
||||
override async parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string): Promise<IFormulaResult<number | undefined>> {
|
||||
const results = await this.formulaService.getRuleFormulaResult(unitId, subUnitId, rule.uid);
|
||||
const formulaResult1 = getFormulaResult(results?.[0]?.result?.[0][0]);
|
||||
const isFormulaValid = isLegalFormulaResult(String(formulaResult1));
|
||||
|
||||
return {
|
||||
formula1: isFormulaString(formula1) ? getRuleFormulaResultSet(results?.[0]?.result?.[0][0]) : deserializeListOptions(formula1),
|
||||
formula1: undefined,
|
||||
formula2: undefined,
|
||||
isFormulaValid,
|
||||
};
|
||||
}
|
||||
|
||||
override async isValidType(cellInfo: IValidatorCellInfo<Nullable<CellValue>>, formula: IFormulaResult<string[] | undefined>, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { value } = cellInfo;
|
||||
const { formula1 = [] } = formula;
|
||||
const { value, unitId, subUnitId } = cellInfo;
|
||||
const { formula1 = '' } = rule;
|
||||
const results = await this.formulaService.getRuleFormulaResult(unitId, subUnitId, rule.uid);
|
||||
const formula1Result = isFormulaString(formula1) ? getRuleFormulaResultSet(results?.[0]?.result?.[0][0]) : deserializeListOptions(formula1);
|
||||
const selected = this.parseCellValue(value!);
|
||||
return selected.every((i) => formula1.includes(i));
|
||||
return selected.every((i) => formula1Result.includes(i));
|
||||
}
|
||||
|
||||
override generateRuleName() {
|
||||
|
||||
@@ -26,7 +26,7 @@ import { isLegalFormulaResult } from '../utils/formula';
|
||||
import { FORMULA1, FORMULA2 } from './const';
|
||||
import { getTransformedFormula } from './util';
|
||||
|
||||
export class TextLengthValidator extends BaseDataValidator<number> {
|
||||
export class TextLengthValidator extends BaseDataValidator {
|
||||
id: string = DataValidationType.TEXT_LENGTH;
|
||||
title: string = 'dataValidation.textLength.title';
|
||||
private readonly _lexerTreeBuilder = this.injector.get(LexerTreeBuilder);
|
||||
@@ -50,7 +50,7 @@ export class TextLengthValidator extends BaseDataValidator<number> {
|
||||
return !Tools.isBlank(formula) && (isFormulaString(formula) || (!Number.isNaN(+formula) && Number.isInteger(+formula)));
|
||||
}
|
||||
|
||||
override validatorFormula(rule: IDataValidationRule, unitId: string, subUnitId: string): IFormulaValidResult {
|
||||
override validatorFormula(rule: IDataValidationRule, _unitId: string, _subUnitId: string): IFormulaValidResult {
|
||||
const operator = rule.operator;
|
||||
if (!operator) {
|
||||
return {
|
||||
@@ -83,11 +83,7 @@ export class TextLengthValidator extends BaseDataValidator<number> {
|
||||
return +formula;
|
||||
}
|
||||
|
||||
private _isValidFormula(formula: number) {
|
||||
return !Number.isNaN(formula);
|
||||
}
|
||||
|
||||
override async parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string, row: number, column: number): Promise<IFormulaResult<any>> {
|
||||
override async parseFormula(rule: IDataValidationRule, unitId: string, subUnitId: string, row: number, column: number): Promise<IFormulaResult<number | undefined>> {
|
||||
const formulaResult1 = await this._customFormulaService.getCellFormulaValue(unitId, subUnitId, rule.uid, row, column);
|
||||
const formulaResult2 = await this._customFormulaService.getCellFormula2Value(unitId, subUnitId, rule.uid, row, column);
|
||||
const { formula1, formula2 } = rule;
|
||||
@@ -108,95 +104,11 @@ export class TextLengthValidator extends BaseDataValidator<number> {
|
||||
};
|
||||
}
|
||||
|
||||
override async isValidType(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
override async isValidType(cellInfo: IValidatorCellInfo<number>, _formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { value: cellValue } = cellInfo;
|
||||
return typeof cellValue === 'string' || typeof cellValue === 'number';
|
||||
}
|
||||
|
||||
override async validatorIsEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1 } = formula;
|
||||
if (!Tools.isDefine(formula1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellInfo.value === formula1;
|
||||
}
|
||||
|
||||
override async validatorIsNotEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1 } = formula;
|
||||
if (!Tools.isDefine(formula1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellInfo.value !== formula1;
|
||||
}
|
||||
|
||||
override async validatorIsBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1, formula2 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (!this._isValidFormula(formula1) || !this._isValidFormula(formula2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const max = Math.max(formula1, formula2);
|
||||
const min = Math.min(formula1, formula2);
|
||||
|
||||
return cellValue >= min && cellValue <= max;
|
||||
}
|
||||
|
||||
override async validatorIsNotBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1, formula2 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (!this._isValidFormula(formula1) || !this._isValidFormula(formula2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const max = Math.max(formula1, formula2);
|
||||
const min = Math.min(formula1, formula2);
|
||||
|
||||
return cellValue < min || cellValue > max;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (!this._isValidFormula(formula1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellValue > formula1;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (!this._isValidFormula(formula1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellValue >= formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (!this._isValidFormula(formula1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellValue < formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, rule: IDataValidationRule): Promise<boolean> {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (!this._isValidFormula(formula1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellValue <= formula1;
|
||||
}
|
||||
|
||||
override generateRuleErrorMessage(rule: IDataValidationRuleBase, pos: ISheetLocationBase) {
|
||||
if (!rule.operator) {
|
||||
return this.titleStr;
|
||||
|
||||
@@ -28,7 +28,7 @@ import { FORMULA1, FORMULA2 } from './const';
|
||||
import { getCellValueNumber } from './decimal-validator';
|
||||
import { getTransformedFormula } from './util';
|
||||
|
||||
export class WholeValidator extends BaseDataValidator<number> {
|
||||
export class WholeValidator extends BaseDataValidator {
|
||||
private readonly _customFormulaService = this.injector.get(DataValidationCustomFormulaService);
|
||||
private readonly _lexerTreeBuilder = this.injector.get(LexerTreeBuilder);
|
||||
|
||||
@@ -52,13 +52,13 @@ export class WholeValidator extends BaseDataValidator<number> {
|
||||
return !Tools.isBlank(formula) && (isFormulaString(formula) || (!Number.isNaN(+formula) && Number.isInteger(+formula)));
|
||||
}
|
||||
|
||||
override async isValidType(cellInfo: IValidatorCellInfo<CellValue>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
override async isValidType(cellInfo: IValidatorCellInfo<CellValue>, _formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { value: cellValue } = cellInfo;
|
||||
const num = getCellValueNumber(cellValue);
|
||||
return !Number.isNaN(num) && Number.isInteger(num);
|
||||
}
|
||||
|
||||
override transform(cellInfo: IValidatorCellInfo<CellValue>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
override transform(cellInfo: IValidatorCellInfo<CellValue>, _formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { value: cellValue } = cellInfo;
|
||||
return {
|
||||
...cellInfo,
|
||||
@@ -92,7 +92,7 @@ export class WholeValidator extends BaseDataValidator<number> {
|
||||
return info;
|
||||
}
|
||||
|
||||
override validatorFormula(rule: IDataValidationRuleBase, unitId: string, subUnitId: string): IFormulaValidResult {
|
||||
override validatorFormula(rule: IDataValidationRuleBase, _unitId: string, _subUnitId: string): IFormulaValidResult {
|
||||
const operator = rule.operator;
|
||||
if (!operator) {
|
||||
return {
|
||||
@@ -117,80 +117,6 @@ export class WholeValidator extends BaseDataValidator<number> {
|
||||
};
|
||||
}
|
||||
|
||||
override async validatorIsEqual(cellInfo: IValidatorCellInfo<CellValue>, formula: IFormulaResult, rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
const { value: cellValue } = cellInfo;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellValue === formula1;
|
||||
}
|
||||
|
||||
override async validatorIsNotEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellInfo.value !== formula1;
|
||||
}
|
||||
|
||||
override async validatorIsBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value >= start && cellInfo.value <= end;
|
||||
}
|
||||
|
||||
override async validatorIsNotBetween(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1, formula2 } = formula;
|
||||
if (Number.isNaN(formula1) || Number.isNaN(formula2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const start = Math.min(formula1, formula2);
|
||||
const end = Math.max(formula1, formula2);
|
||||
return cellInfo.value < start || cellInfo.value > end;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value > formula1;
|
||||
}
|
||||
|
||||
override async validatorIsGreaterThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value >= formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThan(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
return cellInfo.value < formula1;
|
||||
}
|
||||
|
||||
override async validatorIsLessThanOrEqual(cellInfo: IValidatorCellInfo<number>, formula: IFormulaResult, _rule: IDataValidationRule) {
|
||||
const { formula1 } = formula;
|
||||
if (Number.isNaN(formula1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return cellInfo.value <= formula1;
|
||||
}
|
||||
|
||||
override generateRuleErrorMessage(rule: IDataValidationRuleBase, position: ISheetLocationBase) {
|
||||
if (!rule.operator) {
|
||||
return this.titleStr;
|
||||
|
||||
@@ -20,19 +20,21 @@ import { DocSelectionManagerService } from '@univerjs/docs';
|
||||
import { SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { SheetDataValidationModel } from '@univerjs/sheets-data-validation';
|
||||
|
||||
const disables = new Set<string>([
|
||||
DataValidationType.CHECKBOX,
|
||||
DataValidationType.LIST,
|
||||
DataValidationType.LIST_MULTIPLE,
|
||||
]);
|
||||
|
||||
export const getShouldDisableCellLink = (accessor: IAccessor, worksheet: Worksheet, row: number, col: number) => {
|
||||
const cell = worksheet.getCell(row, col);
|
||||
if (cell?.f || cell?.si) {
|
||||
return true;
|
||||
}
|
||||
const disables = [
|
||||
DataValidationType.CHECKBOX,
|
||||
DataValidationType.LIST,
|
||||
DataValidationType.LIST_MULTIPLE,
|
||||
];
|
||||
|
||||
const dataValidationModel = accessor.has(SheetDataValidationModel) ? accessor.get(SheetDataValidationModel) : null;
|
||||
const rule = dataValidationModel?.getRuleByLocation(worksheet.getUnitId(), worksheet.getSheetId(), row, col);
|
||||
if (rule && disables.includes(rule.type)) {
|
||||
if (rule && disables.has(rule.type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Generated
-3
@@ -2044,9 +2044,6 @@ importers:
|
||||
|
||||
packages/sheets-data-validation:
|
||||
dependencies:
|
||||
'@flatten-js/interval-tree':
|
||||
specifier: ^1.1.3
|
||||
version: 1.1.3
|
||||
'@univerjs/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
|
||||
Reference in New Issue
Block a user