feat(facade): add auto-fill api (#6204)

This commit is contained in:
wpxp123456
2025-11-26 14:18:10 +08:00
committed by GitHub
parent 344f773f52
commit 6127c4beda
3 changed files with 80 additions and 9 deletions
@@ -16,14 +16,15 @@
import type { IAccessor, ICommand, IRange } from '@univerjs/core';
import type { ISetRangeValuesMutationParams, ISetSelectionsOperationParams } from '@univerjs/sheets';
import type { APPLY_TYPE } from '../../services/auto-fill/type';
import { CommandType, ICommandService, IUndoRedoService, IUniverInstanceService, sequenceExecute } from '@univerjs/core';
import { generateNullCellValue, getSheetCommandTarget, SetRangeValuesMutation, SetRangeValuesUndoMutationFactory, SetSelectionsOperation, SheetInterceptorService } from '@univerjs/sheets';
import { IAutoFillService } from '../../services/auto-fill/auto-fill.service';
export interface IAutoFillCommandParams {
sourceRange: IRange;
targetRange: IRange;
applyType?: APPLY_TYPE; // manual apply type
}
export const AutoFillCommand: ICommand = {
@@ -34,13 +35,13 @@ export const AutoFillCommand: ICommand = {
const autoFillService = accessor.get(IAutoFillService);
const univerInstanceService = accessor.get(IUniverInstanceService);
const { sourceRange, targetRange } = params;
const { sourceRange, targetRange, applyType } = params;
const commandTarget = getSheetCommandTarget(univerInstanceService);
if (!commandTarget) return false;
const { subUnitId, unitId } = commandTarget;
return autoFillService.triggerAutoFill(unitId, subUnitId, sourceRange, targetRange);
return autoFillService.triggerAutoFill(unitId, subUnitId, sourceRange, targetRange, applyType);
},
};
+65 -3
View File
@@ -16,11 +16,11 @@
import type { ICellWithCoord, IDisposable, ISelectionCell, Nullable } from '@univerjs/core';
import type { ISelectionStyle, ISheetLocation } from '@univerjs/sheets';
import type { ICanvasPopup, ICellAlert, IDropdownParam } from '@univerjs/sheets-ui';
import type { APPLY_TYPE, ICanvasPopup, ICellAlert, IDropdownParam } from '@univerjs/sheets-ui';
import type { ComponentType } from '@univerjs/ui';
import { DisposableCollection, generateRandomId, ILogService, toDisposable } from '@univerjs/core';
import { DisposableCollection, generateRandomId, ILogService, Rectangle, toDisposable } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { CellAlertManagerService, IMarkSelectionService, ISheetCellDropdownManagerService, ISheetClipboardService, SheetCanvasPopManagerService, SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import { AutoFillCommand, CellAlertManagerService, IMarkSelectionService, ISheetCellDropdownManagerService, ISheetClipboardService, SheetCanvasPopManagerService, SheetSkeletonManagerService } from '@univerjs/sheets-ui';
import { FRange } from '@univerjs/sheets/facade';
import { ComponentManager } from '@univerjs/ui';
@@ -225,6 +225,27 @@ interface IFRangeSheetsUIMixin {
* ```
*/
showDropdown(param: IDropdownParam): IDisposable;
/**
* Fills the target range with data based on the data in the current range.
* @param {FRange} targetRange - The range to be filled with data.
* @param {APPLY_TYPE} [applyType] - The type of data fill to be applied.
* @returns {Promise<boolean>} A promise that resolves to true if the fill operation was successful, false otherwise.
* @example
* ```ts
* // Auto-fill the range D1:D10 based on the data in the range C1:C2
* const fWorkbook = univerAPI.getActiveWorkbook();
* const fWorksheet = fWorkbook.getActiveSheet();
* const fRange = fWorksheet.getRange('A1:A4');
*
* // Auto-fill with 'COPY' type
* await fRange.autoFill(fWorksheet.getRange('A1:A20'), 'COPY')
*
* // Auto-fill with 'SERIES' type
* await fRange.autoFill(fWorksheet.getRange('A1:A20'), 'SERIES')
* ```
*/
autoFill(targetRange: FRange, applyType?: APPLY_TYPE): Promise<boolean>;
}
class FRangeSheetsUIMixin extends FRange implements IFRangeSheetsUIMixin {
@@ -355,6 +376,47 @@ class FRangeSheetsUIMixin extends FRange implements IFRangeSheetsUIMixin {
const cellDropdownManagerService = this._injector.get(ISheetCellDropdownManagerService);
return cellDropdownManagerService.showDropdown(param);
}
override async autoFill(targetRange: FRange, applyType?: APPLY_TYPE): Promise<boolean> {
const _sourceRange = this.getRange();
const _targetRange = targetRange.getRange();
if (!Rectangle.contains(_targetRange, _sourceRange)) {
throw new Error('AutoFill target range must contain source range');
}
const { startRow: sourceStartRow, startColumn: sourceStartColumn, endRow: sourceEndRow, endColumn: sourceEndColumn } = _sourceRange;
const { startRow: targetStartRow, startColumn: targetStartColumn, endRow: targetEndRow, endColumn: targetEndColumn } = _targetRange;
// If both row and column count are different, throw error
if ((sourceEndRow - sourceStartRow) !== (targetEndRow - targetStartRow) && (sourceEndColumn - sourceStartColumn) !== (targetEndColumn - targetStartColumn)) {
throw new Error('AutoFill can only fill in one direction');
}
// If the direction includes both left and right, throw error
if (
(sourceEndRow - sourceStartRow) === (targetEndRow - targetStartRow) &&
sourceStartColumn !== targetStartColumn &&
sourceEndColumn !== targetEndColumn
) {
throw new Error('AutoFill can only fill in one direction');
}
// If the direction includes both up and down, throw error
if (
(sourceEndColumn - sourceStartColumn) === (targetEndColumn - targetStartColumn) &&
sourceStartRow !== targetStartRow &&
sourceEndRow !== targetEndRow
) {
throw new Error('AutoFill can only fill in one direction');
}
return this._commandService.executeCommand(AutoFillCommand.id, {
sourceRange: _sourceRange,
targetRange: _targetRange,
applyType,
});
}
}
FRange.extend(FRangeSheetsUIMixin);
@@ -68,7 +68,7 @@ export interface IAutoFillService {
addHook(hook: ISheetAutoFillHook): IDisposable;
fillData(applyType: APPLY_TYPE): boolean;
triggerAutoFill(unitId: string, subUnitId: string, source: IRange, target: IRange): Promise<boolean>;
triggerAutoFill(unitId: string, subUnitId: string, source: IRange, target: IRange, manualApplyType?: APPLY_TYPE): Promise<boolean>;
}
export interface IApplyMenuItem {
@@ -162,8 +162,8 @@ export class AutoFillService extends Disposable implements IAutoFillService {
this._prevUndos = [];
}
// eslint-disable-next-line max-lines-per-function
async triggerAutoFill(unitId: string, subUnitId: string, source: IRange, selection: IRange) {
// eslint-disable-next-line max-lines-per-function, complexity
async triggerAutoFill(unitId: string, subUnitId: string, source: IRange, selection: IRange, manualApplyType?: APPLY_TYPE) {
// if source range === dest range, do nothing;
if (
source.startColumn === selection.startColumn &&
@@ -231,6 +231,7 @@ export class AutoFillService extends Disposable implements IAutoFillService {
if (!autoFillSource || !autoFillTarget) {
return false;
}
this.autoFillLocation = {
source: autoFillSource,
target: autoFillTarget,
@@ -248,6 +249,12 @@ export class AutoFillService extends Disposable implements IAutoFillService {
});
this._initPrevUndo();
// If manual apply type is given, use it
if (manualApplyType) {
return this.fillData(manualApplyType);
}
// set apply type will trigger fillData
for (let i = 0; i < preferTypes.length; i++) {
const menuItem = this.menu.find((m) => m.value === preferTypes[i]);
@@ -255,6 +262,7 @@ export class AutoFillService extends Disposable implements IAutoFillService {
return this.fillData(preferTypes[i]);
}
}
// if no hook return apply type, use first available apply type
const first = this.menu.find((m) => m.disable === false)?.value;
if (first) {