mirror of
https://github.com/dream-num/univer.git
synced 2026-09-19 02:18:42 +08:00
feat(docs): move drawing commands and add image facade (#7278)
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { ITextRange, ITextRangeParam } from '../../../../sheets/typedef';
|
||||
import type { IDocumentBody } from '../../../../types/interfaces';
|
||||
import type { IDocumentBody, IDrawingParam } from '../../../../types/interfaces';
|
||||
import type { DocumentDataModel } from '../../document-data-model';
|
||||
import type { JSONXActions } from '../../json-x/json-x';
|
||||
import { createParagraphId } from '../../../paragraph-id';
|
||||
@@ -29,7 +29,7 @@ import { deleteSelectionTextX } from './text-x-utils';
|
||||
export interface IAddDrawingParam {
|
||||
selection: ITextRangeParam;
|
||||
documentDataModel: DocumentDataModel;
|
||||
drawings: any[];
|
||||
drawings: IDrawingParam[];
|
||||
}
|
||||
|
||||
export function getCustomBlockIdsInSelections(body: IDocumentBody, selections: ITextRange[]): string[] {
|
||||
@@ -147,7 +147,7 @@ function normalizeDrawingInsertOffset(body: IDocumentBody, offset: number): numb
|
||||
return offset === 0 && body.dataStream[0] === DataStreamTreeTokenType.PARAGRAPH ? 1 : offset;
|
||||
}
|
||||
|
||||
function buildDrawingInsertBody(body: IDocumentBody, drawings: any[], insertOffset: number): IDocumentBody {
|
||||
function buildDrawingInsertBody(body: IDocumentBody, drawings: IDrawingParam[], insertOffset: number): IDocumentBody {
|
||||
const placeholders = DataStreamTreeTokenType.CUSTOM_BLOCK.repeat(drawings.length);
|
||||
const needsTrailingParagraph = body.dataStream[insertOffset] === DataStreamTreeTokenType.SECTION_BREAK || body.dataStream[insertOffset] === undefined;
|
||||
const dataStream = needsTrailingParagraph ? `${placeholders}${DataStreamTreeTokenType.PARAGRAPH}` : placeholders;
|
||||
|
||||
@@ -81,6 +81,7 @@ export class BuildTextUtils {
|
||||
|
||||
export { getSingleDataStreamChange } from './data-stream-change';
|
||||
export type { IDataStreamChange } from './data-stream-change';
|
||||
export { getCustomBlockIdsInSelections } from './drawings';
|
||||
export { getParagraphContentStartOffset, getParagraphContentStartOffsets, getParagraphFollowingBlockOffset } from './paragraph';
|
||||
export {
|
||||
containsInteriorInsertionOffset,
|
||||
|
||||
+168
-33
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { Dependency, DependencyIdentifier, DocumentDataModel, ICommand, IDocumentData } from '@univerjs/core';
|
||||
import type { IInsertDocDrawingCommandParams, ISetDocDrawingArrangeCommandParams, IUpdateDrawingDocTransformCommandParams } from '@univerjs/docs-drawing';
|
||||
import {
|
||||
ArrangeTypeEnum,
|
||||
awaitTime,
|
||||
@@ -45,6 +46,12 @@ import {
|
||||
DocDrawingService,
|
||||
IDocDrawingAdapterService,
|
||||
IDocDrawingService,
|
||||
InsertDocDrawingCommand,
|
||||
RemoveDocDrawingCommand,
|
||||
SetDocDrawingArrangeCommand,
|
||||
TextWrappingStyle,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
} from '@univerjs/docs-drawing';
|
||||
import { DocSelectionRenderService } from '@univerjs/docs-ui';
|
||||
import { DrawingManagerService, IDrawingManagerService } from '@univerjs/drawing';
|
||||
@@ -58,20 +65,14 @@ import { DocRefreshDrawingsService } from '../../../services/doc-refresh-drawing
|
||||
import { ClearDocDrawingTransformerOperation } from '../../operations/clear-drawing-transformer.operation';
|
||||
import { DeleteDocDrawingsCommand } from '../delete-doc-drawing.command';
|
||||
import { GroupDocDrawingCommand } from '../group-doc-drawing.command';
|
||||
import { InsertDocDrawingCommand } from '../insert-doc-drawing.command';
|
||||
import { InsertDocImageCommand } from '../insert-image.command';
|
||||
import { MoveDocDrawingsCommand } from '../move-drawings.command';
|
||||
import { RemoveDocDrawingCommand } from '../remove-doc-drawing.command';
|
||||
import { SetDocDrawingArrangeCommand } from '../set-drawing-arrange.command';
|
||||
import { UngroupDocDrawingCommand } from '../ungroup-doc-drawing.command';
|
||||
import {
|
||||
IMoveInlineDrawingCommand,
|
||||
ITransformNonInlineDrawingCommand,
|
||||
TextWrappingStyle,
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
} from '../update-doc-drawing.command';
|
||||
|
||||
function createBaseDocData(): IDocumentData {
|
||||
@@ -133,6 +134,35 @@ function createDrawingDocData(): IDocumentData {
|
||||
};
|
||||
}
|
||||
|
||||
function createHeaderDrawingDocData(): IDocumentData {
|
||||
const docData = createBaseDocData();
|
||||
docData.headers = {
|
||||
'header-1': {
|
||||
headerId: 'header-1',
|
||||
body: {
|
||||
dataStream: '\b\r\n',
|
||||
customBlocks: [{ startIndex: 0, blockId: 'header-shape-1' }],
|
||||
},
|
||||
},
|
||||
};
|
||||
docData.drawings = {
|
||||
'header-shape-1': {
|
||||
drawingId: 'header-shape-1',
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
docTransform: {
|
||||
positionH: { posOffset: 1 },
|
||||
positionV: { posOffset: 2 },
|
||||
},
|
||||
} as never,
|
||||
};
|
||||
docData.drawingsOrder = ['header-shape-1'];
|
||||
|
||||
return docData;
|
||||
}
|
||||
|
||||
function createChartDrawingDocData(): IDocumentData {
|
||||
return {
|
||||
...createDrawingDocData(),
|
||||
@@ -336,21 +366,16 @@ function setupDrawingTestBed(docData: IDocumentData, dependencies: Dependency[]
|
||||
|
||||
const commandService = get(ICommandService);
|
||||
[
|
||||
InsertDocDrawingCommand,
|
||||
RemoveDocDrawingCommand,
|
||||
DeleteDocDrawingsCommand,
|
||||
MoveDocDrawingsCommand,
|
||||
GroupDocDrawingCommand,
|
||||
UngroupDocDrawingCommand,
|
||||
SetDocDrawingArrangeCommand,
|
||||
ClearDocDrawingTransformerOperation,
|
||||
InsertDocImageCommand,
|
||||
IMoveInlineDrawingCommand,
|
||||
ITransformNonInlineDrawingCommand,
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
RichTextEditingMutation as unknown as ICommand,
|
||||
].forEach((command) => commandService.registerCommand(command));
|
||||
|
||||
@@ -393,16 +418,19 @@ describe('docs drawing commands integration', () => {
|
||||
style: null as never,
|
||||
}]);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(InsertDocDrawingCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
drawings: [{
|
||||
drawingId: 'shape-1',
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingType: 'image',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
docTransform: {
|
||||
positionH: { posOffset: 1 },
|
||||
positionV: { posOffset: 2 },
|
||||
size: { width: 1, height: 1 },
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.PAGE, posOffset: 1 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 2 },
|
||||
angle: 0,
|
||||
},
|
||||
}],
|
||||
})).toBe(true);
|
||||
@@ -441,16 +469,19 @@ describe('docs drawing commands integration', () => {
|
||||
endOffset: insertOffset,
|
||||
});
|
||||
|
||||
expect(await testBed.commandService.executeCommand(InsertDocDrawingCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
drawings: [{
|
||||
drawingId: 'shape-1',
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingType: 'image',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
docTransform: {
|
||||
positionH: { posOffset: 1 },
|
||||
positionV: { posOffset: 2 },
|
||||
size: { width: 1, height: 1 },
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.PAGE, posOffset: 1 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 2 },
|
||||
angle: 0,
|
||||
},
|
||||
}],
|
||||
})).toBe(true);
|
||||
@@ -478,7 +509,8 @@ describe('docs drawing commands integration', () => {
|
||||
style: null as never,
|
||||
}]);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(InsertDocDrawingCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
textRange: {
|
||||
startOffset: insertOffset,
|
||||
endOffset: insertOffset,
|
||||
@@ -489,11 +521,13 @@ describe('docs drawing commands integration', () => {
|
||||
drawingId: 'shape-1',
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingType: 'image',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
docTransform: {
|
||||
positionH: { posOffset: 1 },
|
||||
positionV: { posOffset: 2 },
|
||||
size: { width: 1, height: 1 },
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.PAGE, posOffset: 1 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 2 },
|
||||
angle: 0,
|
||||
},
|
||||
}],
|
||||
})).toBe(true);
|
||||
@@ -508,6 +542,44 @@ describe('docs drawing commands integration', () => {
|
||||
testBed.univer.dispose();
|
||||
});
|
||||
|
||||
it('inserts into the document specified by the command params', async () => {
|
||||
const testBed = setupDrawingTestBed(createBaseDocData());
|
||||
const targetData = createBaseDocData();
|
||||
targetData.id = 'target-doc';
|
||||
const targetDoc = testBed.univer.createUnit<IDocumentData, DocumentDataModel>(UniverInstanceType.UNIVER_DOC, targetData);
|
||||
|
||||
testBed.injector.get(CoreDocDrawingController).loadDrawingDataForUnit('target-doc');
|
||||
testBed.get(IUniverInstanceService).setCurrentUnitForType('test-doc');
|
||||
|
||||
expect(await testBed.commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId: 'target-doc',
|
||||
textRange: {
|
||||
startOffset: 5,
|
||||
endOffset: 5,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
drawings: [{
|
||||
drawingId: 'target-shape-1',
|
||||
unitId: 'target-doc',
|
||||
subUnitId: 'target-doc',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
docTransform: {
|
||||
size: { width: 1, height: 1 },
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.PAGE, posOffset: 1 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 2 },
|
||||
angle: 0,
|
||||
},
|
||||
}],
|
||||
})).toBe(true);
|
||||
|
||||
expect(targetDoc.getBody()?.customBlocks).toEqual([{ startIndex: 5, blockId: 'target-shape-1' }]);
|
||||
expect(testBed.get(IUniverInstanceService).getUnit<DocumentDataModel>('test-doc', UniverInstanceType.UNIVER_DOC)?.getBody()?.customBlocks).toEqual([]);
|
||||
|
||||
testBed.univer.dispose();
|
||||
});
|
||||
|
||||
it('replaces the selected drawing block when inserting a new drawing over a selection', async () => {
|
||||
const testBed = setupDrawingTestBed(createDrawingDocData());
|
||||
|
||||
@@ -520,16 +592,19 @@ describe('docs drawing commands integration', () => {
|
||||
style: null as never,
|
||||
}]);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(InsertDocDrawingCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
drawings: [{
|
||||
drawingId: 'shape-2',
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingType: 'image',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
docTransform: {
|
||||
positionH: { posOffset: 10 },
|
||||
positionV: { posOffset: 20 },
|
||||
size: { width: 1, height: 1 },
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.PAGE, posOffset: 10 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 20 },
|
||||
angle: 0,
|
||||
},
|
||||
}],
|
||||
})).toBe(true);
|
||||
@@ -568,6 +643,62 @@ describe('docs drawing commands integration', () => {
|
||||
testBed.univer.dispose();
|
||||
});
|
||||
|
||||
it('deletes from the explicit text range segment instead of the active selection segment', async () => {
|
||||
const testBed = setupDrawingTestBed(createHeaderDrawingDocData());
|
||||
|
||||
expect(await testBed.commandService.executeCommand(RemoveDocDrawingCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
drawings: [{
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingId: 'header-shape-1',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
}],
|
||||
textRange: {
|
||||
startOffset: 0,
|
||||
endOffset: 0,
|
||||
collapsed: true,
|
||||
segmentId: 'header-1',
|
||||
},
|
||||
} as never)).toBe(true);
|
||||
|
||||
const doc = testBed.get(IUniverInstanceService)
|
||||
.getUnit<DocumentDataModel>('test-doc', UniverInstanceType.UNIVER_DOC)!;
|
||||
|
||||
expect(doc.getSelfOrHeaderFooterModel('header-1')?.getBody()?.customBlocks).toEqual([]);
|
||||
expect(doc.getSnapshot().drawings?.['header-shape-1']).toBeUndefined();
|
||||
|
||||
testBed.univer.dispose();
|
||||
});
|
||||
|
||||
it('deletes from the document specified by the command params', async () => {
|
||||
const testBed = setupDrawingTestBed(createBaseDocData());
|
||||
const targetData = createDrawingDocData();
|
||||
targetData.id = 'target-doc';
|
||||
targetData.drawings!['shape-1'].unitId = 'target-doc';
|
||||
targetData.drawings!['shape-1'].subUnitId = 'target-doc';
|
||||
const targetDoc = testBed.univer.createUnit<IDocumentData, DocumentDataModel>(UniverInstanceType.UNIVER_DOC, targetData);
|
||||
|
||||
testBed.injector.get(CoreDocDrawingController).loadDrawingDataForUnit('target-doc');
|
||||
testBed.get(IUniverInstanceService).setCurrentUnitForType('test-doc');
|
||||
|
||||
expect(await testBed.commandService.executeCommand(RemoveDocDrawingCommand.id, {
|
||||
unitId: 'target-doc',
|
||||
drawings: [{
|
||||
unitId: 'target-doc',
|
||||
subUnitId: 'target-doc',
|
||||
drawingId: 'shape-1',
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
}],
|
||||
})).toBe(true);
|
||||
|
||||
expect(targetDoc.getBody()?.customBlocks).toEqual([]);
|
||||
expect(targetDoc.getSnapshot().drawings).toEqual({});
|
||||
expect(testBed.get(IUniverInstanceService).getUnit<DocumentDataModel>('test-doc', UniverInstanceType.UNIVER_DOC)?.getBody()?.customBlocks).toEqual([]);
|
||||
|
||||
testBed.univer.dispose();
|
||||
});
|
||||
|
||||
it('includes drawing adapter resource mutations in the same undo item when deleting a drawing', async () => {
|
||||
const testBed = setupDrawingTestBed(createChartDrawingDocData());
|
||||
const removeResourceMutation = {
|
||||
@@ -877,13 +1008,14 @@ describe('docs drawing commands integration', () => {
|
||||
vi.spyOn(testBed.get(DocSkeletonManagerService), 'getSkeleton').mockReturnValue(skeleton);
|
||||
const refreshDrawings = vi.spyOn(testBed.get(DocRefreshDrawingsService), 'refreshDrawings');
|
||||
|
||||
expect(await testBed.commandService.executeCommand(UpdateDrawingDocTransformCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<IUpdateDrawingDocTransformCommandParams>(UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [{
|
||||
drawingId: 'shape-1',
|
||||
key: 'positionV',
|
||||
value: {
|
||||
relativeFrom: ObjectRelativeFromV.PAGE,
|
||||
posOffset: 18,
|
||||
},
|
||||
}],
|
||||
@@ -893,7 +1025,10 @@ describe('docs drawing commands integration', () => {
|
||||
const doc = testBed.get(IUniverInstanceService)
|
||||
.getUnit<DocumentDataModel>('test-doc', UniverInstanceType.UNIVER_DOC)!;
|
||||
|
||||
expect(doc.getSnapshot().drawings?.['shape-1'].docTransform.positionV).toEqual({ posOffset: 18 });
|
||||
expect(doc.getSnapshot().drawings?.['shape-1'].docTransform.positionV).toEqual({
|
||||
relativeFrom: ObjectRelativeFromV.PAGE,
|
||||
posOffset: 18,
|
||||
});
|
||||
expect(testBed.refreshControls).toHaveBeenCalled();
|
||||
expect(refreshDrawings).toHaveBeenCalledWith(skeleton);
|
||||
|
||||
@@ -1039,7 +1174,7 @@ describe('docs drawing commands integration', () => {
|
||||
|
||||
expect(doc.getSnapshot().drawingsOrder).toEqual(['drawing-a', 'drawing-b', 'drawing-c']);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(SetDocDrawingArrangeCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<ISetDocDrawingArrangeCommandParams>(SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: ['drawing-a'],
|
||||
@@ -1048,7 +1183,7 @@ describe('docs drawing commands integration', () => {
|
||||
await awaitTime(0);
|
||||
expect(doc.getSnapshot().drawingsOrder).toEqual(['drawing-b', 'drawing-c', 'drawing-a']);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(SetDocDrawingArrangeCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<ISetDocDrawingArrangeCommandParams>(SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: ['drawing-a'],
|
||||
@@ -1057,7 +1192,7 @@ describe('docs drawing commands integration', () => {
|
||||
await awaitTime(0);
|
||||
expect(doc.getSnapshot().drawingsOrder).toEqual(['drawing-b', 'drawing-a', 'drawing-c']);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(SetDocDrawingArrangeCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<ISetDocDrawingArrangeCommandParams>(SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: ['drawing-b'],
|
||||
@@ -1066,7 +1201,7 @@ describe('docs drawing commands integration', () => {
|
||||
await awaitTime(0);
|
||||
expect(doc.getSnapshot().drawingsOrder).toEqual(['drawing-a', 'drawing-b', 'drawing-c']);
|
||||
|
||||
expect(await testBed.commandService.executeCommand(SetDocDrawingArrangeCommand.id, {
|
||||
expect(await testBed.commandService.executeCommand<ISetDocDrawingArrangeCommandParams>(SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: ['drawing-c'],
|
||||
|
||||
+1
-1
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import { ICommandService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { InsertDocDrawingCommand } from '@univerjs/docs-drawing';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { InsertDocDrawingCommand } from '../insert-doc-drawing.command';
|
||||
import { InsertDocEllipseShapeCommand, InsertDocRectangleShapeCommand } from '../insert-shape.command';
|
||||
|
||||
function createAccessor() {
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
*/
|
||||
|
||||
import type { IAccessor, ICommand } from '@univerjs/core';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { IDeleteDrawingCommandParams } from './interfaces';
|
||||
import type { IDocDrawing, IRemoveDocDrawingCommandParams } from '@univerjs/docs-drawing';
|
||||
import { CommandType, ICommandService } from '@univerjs/core';
|
||||
import { IDocDrawingService } from '@univerjs/docs-drawing';
|
||||
import { RemoveDocDrawingCommand } from './remove-doc-drawing.command';
|
||||
import { IDocDrawingService, RemoveDocDrawingCommand } from '@univerjs/docs-drawing';
|
||||
|
||||
export const DeleteDocDrawingsCommand: ICommand = {
|
||||
id: 'doc.command.delete-drawing',
|
||||
@@ -46,7 +44,7 @@ export const DeleteDocDrawingsCommand: ICommand = {
|
||||
drawingType,
|
||||
};
|
||||
});
|
||||
return commandService.executeCommand<IDeleteDrawingCommandParams>(RemoveDocDrawingCommand.id, {
|
||||
return commandService.executeCommand<IRemoveDocDrawingCommandParams>(RemoveDocDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: newDrawings,
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { IAccessor, ICommand } from '@univerjs/core';
|
||||
import type { IInsertDrawingCommandParams } from './interfaces';
|
||||
import type { IInsertDocDrawingCommandParams } from '@univerjs/docs-drawing';
|
||||
import {
|
||||
BooleanNumber,
|
||||
CommandType,
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
WrapTextType,
|
||||
} from '@univerjs/core';
|
||||
import { buildDocTransform, docDrawingPositionToTransform } from '@univerjs/docs';
|
||||
import { InsertDocDrawingCommand } from './insert-doc-drawing.command';
|
||||
import { InsertDocDrawingCommand } from '@univerjs/docs-drawing';
|
||||
|
||||
type DocShapeKind = 'rectangle' | 'ellipse';
|
||||
|
||||
@@ -60,7 +60,7 @@ function createShapeInsertCommand(shape: DocShapeKind, width: number, height: nu
|
||||
|
||||
const docTransform = buildDocTransform(width, height);
|
||||
|
||||
return commandService.executeCommand(InsertDocDrawingCommand.id, {
|
||||
return commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: [{
|
||||
unitId,
|
||||
@@ -81,7 +81,7 @@ function createShapeInsertCommand(shape: DocShapeKind, width: number, height: nu
|
||||
distR: 0,
|
||||
distT: 0,
|
||||
}],
|
||||
} as IInsertDrawingCommandParams);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { DrawingTypeEnum, ITextRangeParam } from '@univerjs/core';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
|
||||
export interface IInsertDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: IDocDrawing[];
|
||||
textRange?: ITextRangeParam;
|
||||
}
|
||||
|
||||
export interface IDeleteDrawingCommandParam {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawingId: string;
|
||||
drawingType: DrawingTypeEnum;
|
||||
}
|
||||
|
||||
export interface IDeleteDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: IDeleteDrawingCommandParam[];
|
||||
}
|
||||
|
||||
export interface ISetDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: Partial<IDocDrawing>[];
|
||||
}
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, IAccessor, ICommand } from '@univerjs/core';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { IDrawingDocTransform, IUpdateDrawingDocTransformParams } from './update-doc-drawing.command';
|
||||
import type { DocumentDataModel, IAccessor, ICommand, IObjectPositionH, IObjectPositionV } from '@univerjs/core';
|
||||
import type { IDocDrawing, IDrawingDocTransform, IUpdateDrawingDocTransformCommandParams } from '@univerjs/docs-drawing';
|
||||
import {
|
||||
CommandType,
|
||||
Direction,
|
||||
@@ -25,9 +24,8 @@ import {
|
||||
PositionedObjectLayoutType,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { IDocDrawingService } from '@univerjs/docs-drawing';
|
||||
import { IDocDrawingService, UpdateDrawingDocTransformCommand } from '@univerjs/docs-drawing';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { UpdateDrawingDocTransformCommand } from './update-doc-drawing.command';
|
||||
|
||||
export interface IMoveDrawingsCommandParams {
|
||||
direction: Direction;
|
||||
@@ -74,8 +72,8 @@ export const MoveDocDrawingsCommand: ICommand = {
|
||||
|
||||
const { positionH, positionV } = drawingData.docTransform;
|
||||
|
||||
const newPositionH = { ...positionH };
|
||||
const newPositionV = { ...positionV };
|
||||
const newPositionH: IObjectPositionH = { ...positionH };
|
||||
const newPositionV: IObjectPositionV = { ...positionV };
|
||||
|
||||
if (direction === Direction.UP) {
|
||||
newPositionV.posOffset = (newPositionV.posOffset ?? 0) - 2;
|
||||
@@ -91,14 +89,14 @@ export const MoveDocDrawingsCommand: ICommand = {
|
||||
drawingId,
|
||||
key: direction === Direction.UP || direction === Direction.DOWN ? 'positionV' : 'positionH',
|
||||
value: direction === Direction.UP || direction === Direction.DOWN ? newPositionV : newPositionH,
|
||||
} as IDrawingDocTransform;
|
||||
};
|
||||
}).filter((drawing) => drawing != null) as IDrawingDocTransform[];
|
||||
|
||||
if (newDrawings.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = commandService.syncExecuteCommand<IUpdateDrawingDocTransformParams>(UpdateDrawingDocTransformCommand.id, {
|
||||
const result = commandService.syncExecuteCommand<IUpdateDrawingDocTransformCommandParams>(UpdateDrawingDocTransformCommand.id, {
|
||||
unitId,
|
||||
subUnitId: unitId,
|
||||
drawings: newDrawings,
|
||||
|
||||
@@ -14,20 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, IAccessor, ICommand, IDocDrawingBase, IDocDrawingPosition, IMutationInfo, IObjectPositionH, IObjectPositionV, ISize, JSONXActions, WrapTextType } from '@univerjs/core';
|
||||
import type { DocumentDataModel, IAccessor, ICommand, IDocDrawingBase, IDocDrawingPosition, IMutationInfo, JSONXActions, WrapTextType } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { IDocumentSkeletonDrawing, IDocumentSkeletonHeaderFooter, IDocumentSkeletonPage } from '@univerjs/engine-render';
|
||||
import {
|
||||
BooleanNumber,
|
||||
CommandType,
|
||||
getRichTextEditPath,
|
||||
ICommandService,
|
||||
IUniverInstanceService,
|
||||
JSONX,
|
||||
ObjectRelativeFromH,
|
||||
ObjectRelativeFromV,
|
||||
PositionedObjectLayoutType,
|
||||
TextX,
|
||||
TextXActionType,
|
||||
Tools,
|
||||
@@ -35,60 +30,9 @@ import {
|
||||
} from '@univerjs/core';
|
||||
import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { DocSelectionRenderService } from '@univerjs/docs-ui';
|
||||
import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { DocRefreshDrawingsService } from '../../services/doc-refresh-drawings.service';
|
||||
|
||||
export enum TextWrappingStyle {
|
||||
INLINE = 'inline',
|
||||
BEHIND_TEXT = 'behindText',
|
||||
IN_FRONT_OF_TEXT = 'inFrontOfText',
|
||||
WRAP_SQUARE = 'wrapSquare',
|
||||
WRAP_TOP_AND_BOTTOM = 'wrapTopAndBottom',
|
||||
}
|
||||
|
||||
const WRAPPING_STYLE_TO_LAYOUT_TYPE = {
|
||||
[TextWrappingStyle.INLINE]: PositionedObjectLayoutType.INLINE,
|
||||
[TextWrappingStyle.WRAP_SQUARE]: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
[TextWrappingStyle.WRAP_TOP_AND_BOTTOM]: PositionedObjectLayoutType.WRAP_TOP_AND_BOTTOM,
|
||||
[TextWrappingStyle.IN_FRONT_OF_TEXT]: PositionedObjectLayoutType.WRAP_NONE,
|
||||
[TextWrappingStyle.BEHIND_TEXT]: PositionedObjectLayoutType.WRAP_NONE,
|
||||
};
|
||||
|
||||
interface IDrawingAnchorInPage {
|
||||
skeDrawing: IDocumentSkeletonDrawing;
|
||||
pageMarginTop: number;
|
||||
pageMarginLeft: number;
|
||||
}
|
||||
|
||||
export function findDrawingAnchorInPage(
|
||||
page: IDocumentSkeletonPage | IDocumentSkeletonHeaderFooter,
|
||||
drawingId: string,
|
||||
pageMarginTop: number,
|
||||
pageMarginLeft: number
|
||||
): IDrawingAnchorInPage | null {
|
||||
const skeDrawing = page.skeDrawings.get(drawingId);
|
||||
if (skeDrawing) {
|
||||
return {
|
||||
skeDrawing,
|
||||
pageMarginTop,
|
||||
pageMarginLeft,
|
||||
};
|
||||
}
|
||||
|
||||
for (const table of page.skeTables.values()) {
|
||||
for (const row of table.rows) {
|
||||
for (const cell of row.cells) {
|
||||
const cellAnchor = findDrawingAnchorInPage(cell, drawingId, cell.marginTop, cell.marginLeft);
|
||||
if (cellAnchor) {
|
||||
return cellAnchor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
function getDeleteAndInsertCustomBlockActions(
|
||||
segmentId: string,
|
||||
@@ -238,191 +182,6 @@ function getDeleteAndInsertCustomBlockActions(
|
||||
return rawActions;
|
||||
}
|
||||
|
||||
interface IUpdateDocDrawingWrappingStyleParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawings: IDocDrawing[];
|
||||
wrappingStyle: TextWrappingStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command to update drawing wrapping style.
|
||||
*/
|
||||
export const UpdateDocDrawingWrappingStyleCommand: ICommand = {
|
||||
id: 'doc.command.update-doc-drawing-wrapping-style',
|
||||
|
||||
type: CommandType.COMMAND,
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function, complexity
|
||||
handler: (accessor: IAccessor, params?: IUpdateDocDrawingWrappingStyleParams) => {
|
||||
if (params == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { drawings, wrappingStyle, unitId } = params;
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const renderManagerService = accessor.get(IRenderManagerService);
|
||||
|
||||
const renderObject = renderManagerService.getRenderById(unitId);
|
||||
const skeletonData = renderObject?.with(DocSkeletonManagerService)
|
||||
.getSkeleton()
|
||||
.getSkeletonData();
|
||||
const viewModel = renderObject?.with(DocSkeletonManagerService).getViewModel();
|
||||
const scene = renderObject?.scene;
|
||||
const documentDataModel = univerInstanceService.getCurrentUnitOfType<DocumentDataModel>(UniverInstanceType.UNIVER_DOC);
|
||||
|
||||
if (documentDataModel == null || skeletonData == null || scene == null || viewModel == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const editArea = viewModel.getEditArea();
|
||||
const transformer = scene.getTransformerByCreate();
|
||||
|
||||
const { pages, skeHeaders, skeFooters } = skeletonData;
|
||||
|
||||
const jsonX = JSONX.getInstance();
|
||||
const rawActions: JSONXActions = [];
|
||||
|
||||
const { drawings: oldDrawings = {} } = documentDataModel.getSnapshot();
|
||||
|
||||
// Update drawing layoutType.
|
||||
for (const drawing of drawings) {
|
||||
const { drawingId } = drawing;
|
||||
|
||||
// Update layoutType.
|
||||
const oldLayoutType = oldDrawings[drawingId].layoutType;
|
||||
const newLayoutType = WRAPPING_STYLE_TO_LAYOUT_TYPE[wrappingStyle];
|
||||
|
||||
if (oldLayoutType !== newLayoutType) {
|
||||
const updateLayoutTypeAction = jsonX.replaceOp(['drawings', drawingId, 'layoutType'], oldLayoutType, newLayoutType);
|
||||
|
||||
rawActions.push(updateLayoutTypeAction!);
|
||||
}
|
||||
|
||||
// Update behindDoc if layoutType is WRAP_NONE.
|
||||
if (wrappingStyle === TextWrappingStyle.BEHIND_TEXT || wrappingStyle === TextWrappingStyle.IN_FRONT_OF_TEXT) {
|
||||
const oldBehindDoc = oldDrawings[drawingId].behindDoc;
|
||||
const newBehindDoc = wrappingStyle === TextWrappingStyle.BEHIND_TEXT ? BooleanNumber.TRUE : BooleanNumber.FALSE;
|
||||
|
||||
if (oldBehindDoc !== newBehindDoc) {
|
||||
const updateBehindDocAction = jsonX.replaceOp(['drawings', drawingId, 'behindDoc'], oldBehindDoc, newBehindDoc);
|
||||
|
||||
rawActions.push(updateBehindDocAction!);
|
||||
}
|
||||
}
|
||||
|
||||
if (wrappingStyle === TextWrappingStyle.INLINE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update positionH and positionV if layoutType is not inline.
|
||||
let drawingAnchor: IDrawingAnchorInPage | null = null;
|
||||
for (const page of pages) {
|
||||
const { headerId, footerId, marginTop, marginLeft, marginBottom, pageWidth, pageHeight } = page;
|
||||
|
||||
switch (editArea) {
|
||||
case DocumentEditArea.HEADER: {
|
||||
const headerSke = skeHeaders.get(headerId)?.get(pageWidth);
|
||||
|
||||
if (headerSke != null) {
|
||||
drawingAnchor = findDrawingAnchorInPage(headerSke, drawingId, headerSke.marginTop, marginLeft);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DocumentEditArea.FOOTER: {
|
||||
const footerSke = skeFooters.get(footerId)?.get(pageWidth);
|
||||
if (footerSke != null) {
|
||||
drawingAnchor = findDrawingAnchorInPage(footerSke, drawingId, pageHeight - marginBottom + footerSke.marginTop, marginLeft);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DocumentEditArea.BODY: {
|
||||
drawingAnchor = findDrawingAnchorInPage(page, drawingId, marginTop, marginLeft);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawingAnchor != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawingAnchor != null) {
|
||||
const { skeDrawing, pageMarginTop, pageMarginLeft } = drawingAnchor;
|
||||
const { aTop, aLeft } = skeDrawing;
|
||||
const oldPositionH = oldDrawings[drawingId].docTransform.positionH;
|
||||
let posOffsetH = aLeft;
|
||||
|
||||
if (oldPositionH.relativeFrom === ObjectRelativeFromH.MARGIN) {
|
||||
posOffsetH -= pageMarginLeft;
|
||||
} else if (oldPositionH.relativeFrom === ObjectRelativeFromH.COLUMN) {
|
||||
posOffsetH -= skeDrawing.columnLeft;
|
||||
}
|
||||
|
||||
const newPositionH = {
|
||||
relativeFrom: oldPositionH.relativeFrom,
|
||||
posOffset: posOffsetH,
|
||||
};
|
||||
|
||||
if (oldPositionH.posOffset !== newPositionH.posOffset) {
|
||||
const action = jsonX.replaceOp(['drawings', drawingId, 'docTransform', 'positionH'], oldPositionH, newPositionH);
|
||||
|
||||
rawActions.push(action!);
|
||||
}
|
||||
|
||||
const oldPositionV = oldDrawings[drawingId].docTransform.positionV;
|
||||
let posOffsetV = aTop;
|
||||
|
||||
if (oldPositionV.relativeFrom === ObjectRelativeFromV.PAGE) {
|
||||
posOffsetV += pageMarginTop;
|
||||
} else if (oldPositionV.relativeFrom === ObjectRelativeFromV.LINE) {
|
||||
posOffsetV -= skeDrawing.lineTop;
|
||||
} else if (oldPositionV.relativeFrom === ObjectRelativeFromV.PARAGRAPH) {
|
||||
posOffsetV -= skeDrawing.blockAnchorTop;
|
||||
}
|
||||
|
||||
const newPositionV = {
|
||||
relativeFrom: oldPositionV.relativeFrom,
|
||||
posOffset: posOffsetV,
|
||||
};
|
||||
|
||||
if (oldPositionV.posOffset !== newPositionV.posOffset) {
|
||||
const action = jsonX.replaceOp(['drawings', drawingId, 'docTransform', 'positionV'], oldPositionV, newPositionV);
|
||||
|
||||
rawActions.push(action!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const doMutation: IMutationInfo<IRichTextEditingMutationParams> = {
|
||||
id: RichTextEditingMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
actions: [],
|
||||
textRanges: null,
|
||||
},
|
||||
};
|
||||
|
||||
doMutation.params.actions = rawActions.reduce((acc, cur) => {
|
||||
return JSONX.compose(acc, cur as JSONXActions);
|
||||
}, null as JSONXActions);
|
||||
|
||||
const result = commandService.syncExecuteCommand<
|
||||
IRichTextEditingMutationParams,
|
||||
IRichTextEditingMutationParams
|
||||
>(doMutation.id, doMutation.params);
|
||||
|
||||
transformer.refreshControls();
|
||||
|
||||
return Boolean(result);
|
||||
},
|
||||
};
|
||||
|
||||
interface IDist {
|
||||
distT: number;
|
||||
distB: number;
|
||||
@@ -569,99 +328,6 @@ export const UpdateDocDrawingWrapTextCommand: ICommand = {
|
||||
},
|
||||
};
|
||||
|
||||
export interface IDrawingDocTransform {
|
||||
drawingId: string;
|
||||
key: 'size' | 'angle' | 'positionH' | 'positionV';
|
||||
value: ISize | number | IObjectPositionH | IObjectPositionV;
|
||||
}
|
||||
|
||||
export interface IUpdateDrawingDocTransformParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawings: IDrawingDocTransform[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The command to update drawing position.
|
||||
*/
|
||||
export const UpdateDrawingDocTransformCommand: ICommand = {
|
||||
id: 'doc.command.update-drawing-doc-transform',
|
||||
|
||||
type: CommandType.COMMAND,
|
||||
|
||||
handler: (accessor: IAccessor, params?: IUpdateDrawingDocTransformParams) => {
|
||||
if (params == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const renderManagerService = accessor.get(IRenderManagerService);
|
||||
|
||||
const { drawings, unitId } = params;
|
||||
const renderObject = renderManagerService.getRenderUnitById(unitId);
|
||||
const scene = renderObject?.scene;
|
||||
if (scene == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const transformer = scene.getTransformerByCreate();
|
||||
|
||||
const documentDataModel = univerInstanceService.getUnit<DocumentDataModel>(unitId, UniverInstanceType.UNIVER_DOC);
|
||||
if (documentDataModel == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const jsonX = JSONX.getInstance();
|
||||
const rawActions: JSONXActions = [];
|
||||
|
||||
const { drawings: oldDrawings = {} } = documentDataModel.getSnapshot();
|
||||
|
||||
// Update drawing layoutType.
|
||||
for (const drawing of drawings) {
|
||||
const { drawingId, key, value } = drawing;
|
||||
|
||||
const oldValue = oldDrawings[drawingId].docTransform[key];
|
||||
|
||||
if (!Tools.diffValue(oldValue, value)) {
|
||||
const action = jsonX.replaceOp(['drawings', drawingId, 'docTransform', key], oldValue, value);
|
||||
|
||||
rawActions.push(action!);
|
||||
}
|
||||
}
|
||||
|
||||
const doMutation: IMutationInfo<IRichTextEditingMutationParams> = {
|
||||
id: RichTextEditingMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
actions: [],
|
||||
textRanges: null,
|
||||
debounce: true,
|
||||
},
|
||||
};
|
||||
|
||||
doMutation.params.actions = rawActions.reduce((acc, cur) => {
|
||||
return JSONX.compose(acc, cur as JSONXActions);
|
||||
}, null as JSONXActions);
|
||||
|
||||
const result = commandService.syncExecuteCommand<
|
||||
IRichTextEditingMutationParams,
|
||||
IRichTextEditingMutationParams
|
||||
>(doMutation.id, doMutation.params);
|
||||
|
||||
// RichTextEditingMutation recalculates the document skeleton before the
|
||||
// synchronous command returns. Publish that fresh geometry so the drawing
|
||||
// manager, renderer, and transformer do not keep the pre-mutation size.
|
||||
if (accessor.has(DocRefreshDrawingsService)) {
|
||||
const skeleton = renderObject?.with(DocSkeletonManagerService).getSkeleton() ?? null;
|
||||
accessor.get(DocRefreshDrawingsService).refreshDrawings(skeleton);
|
||||
}
|
||||
transformer.refreshControls();
|
||||
|
||||
return Boolean(result);
|
||||
},
|
||||
};
|
||||
|
||||
export interface IMoveInlineDrawingParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
|
||||
+118
-10
@@ -14,8 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BooleanNumber, PositionedObjectLayoutType, UndoCommand } from '@univerjs/core';
|
||||
import {
|
||||
BooleanNumber,
|
||||
ObjectRelativeFromH,
|
||||
ObjectRelativeFromV,
|
||||
PositionedObjectLayoutType,
|
||||
UndoCommand,
|
||||
} from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { TextWrappingStyle, UpdateDocDrawingWrappingStyleCommand } from '@univerjs/docs-drawing';
|
||||
import { DocumentEditArea } from '@univerjs/engine-render';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { DocDrawingAddRemoveController } from '../doc-drawing-notification.controller';
|
||||
|
||||
@@ -23,6 +31,52 @@ function createController() {
|
||||
const beforeHandlers: Array<(command: { id: string; params?: unknown }) => void> = [];
|
||||
const executedHandlers: Array<(command: { id: string; params?: unknown }) => void> = [];
|
||||
const refreshControls = vi.fn();
|
||||
const refreshDrawings = vi.fn();
|
||||
const drawing = {
|
||||
unitId: 'doc-1',
|
||||
subUnitId: 'doc-1',
|
||||
drawingId: 'drawing-1',
|
||||
layoutType: PositionedObjectLayoutType.WRAP_NONE,
|
||||
behindDoc: BooleanNumber.TRUE,
|
||||
docTransform: {
|
||||
size: { width: 100, height: 60 },
|
||||
angle: 0,
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.MARGIN, posOffset: 1 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 2 },
|
||||
},
|
||||
};
|
||||
const skeleton = {
|
||||
getSkeletonData: () => ({
|
||||
pages: [{
|
||||
marginTop: 20,
|
||||
marginLeft: 30,
|
||||
marginBottom: 20,
|
||||
pageWidth: 600,
|
||||
pageHeight: 800,
|
||||
headerId: '',
|
||||
footerId: '',
|
||||
skeDrawings: new Map([['drawing-1', {
|
||||
drawingId: 'drawing-1',
|
||||
aLeft: 130,
|
||||
aTop: 180,
|
||||
columnLeft: 30,
|
||||
lineTop: 150,
|
||||
blockAnchorTop: 140,
|
||||
drawingOrigin: drawing,
|
||||
}]]),
|
||||
skeTables: new Map(),
|
||||
}],
|
||||
skeHeaders: new Map(),
|
||||
skeFooters: new Map(),
|
||||
}),
|
||||
};
|
||||
const renderObject = {
|
||||
scene: { getTransformerByCreate: () => ({ refreshControls }) },
|
||||
with: () => ({
|
||||
getSkeleton: () => skeleton,
|
||||
getViewModel: () => ({ getEditArea: () => DocumentEditArea.BODY }),
|
||||
}),
|
||||
};
|
||||
const drawingManagerService = {
|
||||
applyJson1: vi.fn(),
|
||||
addNotification: vi.fn(),
|
||||
@@ -44,12 +98,16 @@ function createController() {
|
||||
{
|
||||
getCurrentUnitOfType: vi.fn(() => ({ getUnitId: () => 'doc-1' })),
|
||||
getUnit: vi.fn(() => ({
|
||||
getDrawings: () => ({
|
||||
'drawing-1': drawing,
|
||||
'drawing-2': {
|
||||
layoutType: PositionedObjectLayoutType.WRAP_NONE,
|
||||
behindDoc: BooleanNumber.FALSE,
|
||||
},
|
||||
}),
|
||||
getSnapshot: () => ({
|
||||
drawings: {
|
||||
'drawing-1': {
|
||||
layoutType: PositionedObjectLayoutType.WRAP_NONE,
|
||||
behindDoc: BooleanNumber.TRUE,
|
||||
},
|
||||
'drawing-1': drawing,
|
||||
'drawing-2': {
|
||||
layoutType: PositionedObjectLayoutType.WRAP_NONE,
|
||||
behindDoc: BooleanNumber.FALSE,
|
||||
@@ -72,12 +130,23 @@ function createController() {
|
||||
drawingManagerService as never,
|
||||
docDrawingService as never,
|
||||
{
|
||||
getRenderById: vi.fn(() => ({ scene: { getTransformerByCreate: () => ({ refreshControls }) } })),
|
||||
getRenderUnitById: vi.fn(() => ({ scene: { getTransformerByCreate: () => ({ refreshControls }) } })),
|
||||
} as never
|
||||
getRenderById: vi.fn(() => renderObject),
|
||||
getRenderUnitById: vi.fn(() => renderObject),
|
||||
} as never,
|
||||
{ refreshDrawings } as never
|
||||
);
|
||||
|
||||
return { controller, beforeHandlers, executedHandlers, drawingManagerService, docDrawingService, refreshControls };
|
||||
return {
|
||||
controller,
|
||||
beforeHandlers,
|
||||
executedHandlers,
|
||||
drawingManagerService,
|
||||
docDrawingService,
|
||||
drawing,
|
||||
refreshControls,
|
||||
refreshDrawings,
|
||||
skeleton,
|
||||
};
|
||||
}
|
||||
|
||||
describe('DocDrawingAddRemoveController', () => {
|
||||
@@ -123,7 +192,7 @@ describe('DocDrawingAddRemoveController', () => {
|
||||
actions: ['drawingsOrder', [0, { d: 0 }], [1, { p: 0 }]],
|
||||
},
|
||||
});
|
||||
executedHandlers[1]({ id: UndoCommand.id });
|
||||
executedHandlers.forEach((handler) => handler({ id: UndoCommand.id }));
|
||||
|
||||
expect(drawingManagerService.setDrawingOrder).toHaveBeenCalledWith('doc-1', 'doc-1', ['drawing-1', 'drawing-2']);
|
||||
expect(docDrawingService.setDrawingOrder).toHaveBeenCalledWith('doc-1', 'doc-1', ['drawing-2', 'drawing-1']);
|
||||
@@ -141,4 +210,43 @@ describe('DocDrawingAddRemoveController', () => {
|
||||
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
it('preserves skeleton position before wrapping changes and refreshes drawings afterward', () => {
|
||||
const {
|
||||
controller,
|
||||
beforeHandlers,
|
||||
executedHandlers,
|
||||
drawing,
|
||||
refreshControls,
|
||||
refreshDrawings,
|
||||
skeleton,
|
||||
} = createController();
|
||||
const params = {
|
||||
unitId: 'doc-1',
|
||||
subUnitId: 'doc-1',
|
||||
drawings: [{ drawingId: 'drawing-1' }],
|
||||
wrappingStyle: TextWrappingStyle.WRAP_SQUARE,
|
||||
};
|
||||
const command = { id: UpdateDocDrawingWrappingStyleCommand.id, params };
|
||||
|
||||
beforeHandlers.forEach((handler) => handler(command));
|
||||
|
||||
expect(params.drawings[0]).toMatchObject({
|
||||
drawingId: 'drawing-1',
|
||||
docTransform: {
|
||||
positionH: { relativeFrom: ObjectRelativeFromH.MARGIN, posOffset: 100 },
|
||||
positionV: { relativeFrom: ObjectRelativeFromV.PARAGRAPH, posOffset: 40 },
|
||||
},
|
||||
});
|
||||
expect(drawing.docTransform).toMatchObject({
|
||||
positionH: { posOffset: 1 },
|
||||
positionV: { posOffset: 2 },
|
||||
});
|
||||
|
||||
executedHandlers.forEach((handler) => handler(command));
|
||||
|
||||
expect(refreshDrawings).toHaveBeenCalledWith(skeleton);
|
||||
expect(refreshControls).toHaveBeenCalled();
|
||||
controller.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
import { ObjectRelativeFromH, ObjectRelativeFromV, PositionedObjectLayoutType } from '@univerjs/core';
|
||||
import { UpdateDrawingDocTransformCommand } from '@univerjs/docs-drawing';
|
||||
import { Subject } from 'rxjs';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
IMoveInlineDrawingCommand,
|
||||
ITransformNonInlineDrawingCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
} from '../../commands/commands/update-doc-drawing.command';
|
||||
import { DocDrawingTransformerController, getDocsTableCellAnchorContext } from '../doc-drawing-transformer-update.controller';
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
import { DrawingTypeEnum } from '@univerjs/core';
|
||||
import { InsertDocDrawingCommand } from '@univerjs/docs-drawing';
|
||||
import { Rect } from '@univerjs/engine-render';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { InsertDocDrawingCommand } from '../../commands/commands/insert-doc-drawing.command';
|
||||
import { calcDocFloatDomPositionByRect, DocFloatDomController } from '../doc-float-dom.controller';
|
||||
|
||||
function createScene() {
|
||||
|
||||
@@ -18,21 +18,32 @@
|
||||
|
||||
import type { DocumentDataModel, ICommandInfo, IDrawingSearch, JSONXActions, Nullable } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { IDocDrawing, IUpdateDocDrawingWrappingStyleParams, IUpdateDrawingDocTransformCommandParams } from '@univerjs/docs-drawing';
|
||||
import type { IDrawingJsonUndo1, IDrawingMapItemData, IDrawingOrderMapParam } from '@univerjs/drawing';
|
||||
import type { IDocumentSkeletonDrawing, IDocumentSkeletonHeaderFooter, IDocumentSkeletonPage } from '@univerjs/engine-render';
|
||||
import {
|
||||
Disposable,
|
||||
ICommandService,
|
||||
Inject,
|
||||
IUniverInstanceService,
|
||||
JSONX,
|
||||
ObjectRelativeFromH,
|
||||
ObjectRelativeFromV,
|
||||
RedoCommand,
|
||||
UndoCommand,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { getDocDrawingRenderOrder, IDocDrawingService } from '@univerjs/docs-drawing';
|
||||
import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import {
|
||||
getDocDrawingRenderOrder,
|
||||
IDocDrawingService,
|
||||
TextWrappingStyle,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
} from '@univerjs/docs-drawing';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { DocRefreshDrawingsService } from '../services/doc-refresh-drawings.service';
|
||||
|
||||
interface IAddOrRemoveDrawing {
|
||||
type: 'add' | 'remove';
|
||||
@@ -40,6 +51,37 @@ interface IAddOrRemoveDrawing {
|
||||
drawing?: IDocDrawing;
|
||||
}
|
||||
|
||||
interface IDrawingAnchorInPage {
|
||||
skeDrawing: IDocumentSkeletonDrawing;
|
||||
pageMarginTop: number;
|
||||
pageMarginLeft: number;
|
||||
}
|
||||
|
||||
function findDrawingAnchorInPage(
|
||||
page: IDocumentSkeletonPage | IDocumentSkeletonHeaderFooter,
|
||||
drawingId: string,
|
||||
pageMarginTop: number,
|
||||
pageMarginLeft: number
|
||||
): IDrawingAnchorInPage | null {
|
||||
const skeDrawing = page.skeDrawings.get(drawingId);
|
||||
if (skeDrawing) {
|
||||
return { skeDrawing, pageMarginTop, pageMarginLeft };
|
||||
}
|
||||
|
||||
for (const table of page.skeTables.values()) {
|
||||
for (const row of table.rows) {
|
||||
for (const cell of row.cells) {
|
||||
const cellAnchor = findDrawingAnchorInPage(cell, drawingId, cell.marginTop, cell.marginLeft);
|
||||
if (cellAnchor) {
|
||||
return cellAnchor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check whether drawings are added or deleted from the mutation and obtain the drawing ID.
|
||||
// eslint-disable-next-line complexity
|
||||
function getAddOrRemoveDrawings(actions: JSONXActions): Nullable<IAddOrRemoveDrawing[]> {
|
||||
@@ -152,7 +194,8 @@ export class DocDrawingAddRemoveController extends Disposable {
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IDrawingManagerService private readonly _drawingManagerService: IDrawingManagerService,
|
||||
@IDocDrawingService private readonly _docDrawingService: IDocDrawingService,
|
||||
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService
|
||||
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
|
||||
@Inject(DocRefreshDrawingsService) private readonly _docRefreshDrawingsService: DocRefreshDrawingsService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -163,6 +206,7 @@ export class DocDrawingAddRemoveController extends Disposable {
|
||||
this._commandExecutedListener();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
private _commandExecutedListener() {
|
||||
this.disposeWithMe(
|
||||
this._commandService.beforeCommandExecuted((command: ICommandInfo) => {
|
||||
@@ -190,6 +234,16 @@ export class DocDrawingAddRemoveController extends Disposable {
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(
|
||||
this._commandService.beforeCommandExecuted((command: ICommandInfo) => {
|
||||
if (command.id !== UpdateDocDrawingWrappingStyleCommand.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._preserveWrappingStylePosition(command.params as IUpdateDocDrawingWrappingStyleParams);
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(
|
||||
this._commandService.onCommandExecuted((command: ICommandInfo) => {
|
||||
if (command.id !== RichTextEditingMutation.id) {
|
||||
@@ -211,6 +265,27 @@ export class DocDrawingAddRemoveController extends Disposable {
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(
|
||||
this._commandService.onCommandExecuted((command: ICommandInfo) => {
|
||||
if (
|
||||
command.id !== UpdateDrawingDocTransformCommand.id &&
|
||||
command.id !== UpdateDocDrawingWrappingStyleCommand.id
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { unitId } = command.params as IUpdateDrawingDocTransformCommandParams | IUpdateDocDrawingWrappingStyleParams;
|
||||
const renderObject = this._renderManagerService.getRenderById(unitId);
|
||||
const scene = renderObject?.scene;
|
||||
if (renderObject == null || scene == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._docRefreshDrawingsService.refreshDrawings(renderObject.with(DocSkeletonManagerService).getSkeleton());
|
||||
scene.getTransformerByCreate().refreshControls();
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(
|
||||
this._commandService.onCommandExecuted((command: ICommandInfo) => {
|
||||
if (command.id !== UndoCommand.id && command.id !== RedoCommand.id) {
|
||||
@@ -236,6 +311,99 @@ export class DocDrawingAddRemoveController extends Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
private _preserveWrappingStylePosition(params: IUpdateDocDrawingWrappingStyleParams): void {
|
||||
if (params.wrappingStyle === TextWrappingStyle.INLINE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { unitId } = params;
|
||||
const documentDataModel = this._univerInstanceService.getUnit<DocumentDataModel>(
|
||||
unitId,
|
||||
UniverInstanceType.UNIVER_DOC
|
||||
);
|
||||
const renderObject = this._renderManagerService.getRenderById(unitId);
|
||||
const skeletonManager = renderObject?.with(DocSkeletonManagerService);
|
||||
const skeletonData = skeletonManager?.getSkeleton().getSkeletonData();
|
||||
const viewModel = skeletonManager?.getViewModel();
|
||||
if (!documentDataModel || !skeletonData || !viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editArea = viewModel.getEditArea();
|
||||
const { pages, skeHeaders, skeFooters } = skeletonData;
|
||||
const oldDrawings = documentDataModel.getDrawings() ?? {};
|
||||
|
||||
params.drawings = params.drawings.map((drawing) => {
|
||||
const oldDrawing = oldDrawings[drawing.drawingId] as IDocDrawing | undefined;
|
||||
if (!oldDrawing) {
|
||||
return drawing;
|
||||
}
|
||||
|
||||
let drawingAnchor: IDrawingAnchorInPage | null = null;
|
||||
for (const page of pages) {
|
||||
const { headerId, footerId, marginTop, marginLeft, marginBottom, pageWidth, pageHeight } = page;
|
||||
if (editArea === DocumentEditArea.HEADER) {
|
||||
const header = skeHeaders.get(headerId)?.get(pageWidth);
|
||||
if (header) {
|
||||
drawingAnchor = findDrawingAnchorInPage(header, drawing.drawingId, header.marginTop, marginLeft);
|
||||
}
|
||||
} else if (editArea === DocumentEditArea.FOOTER) {
|
||||
const footer = skeFooters.get(footerId)?.get(pageWidth);
|
||||
if (footer) {
|
||||
drawingAnchor = findDrawingAnchorInPage(
|
||||
footer,
|
||||
drawing.drawingId,
|
||||
pageHeight - marginBottom + footer.marginTop,
|
||||
marginLeft
|
||||
);
|
||||
}
|
||||
} else {
|
||||
drawingAnchor = findDrawingAnchorInPage(page, drawing.drawingId, marginTop, marginLeft);
|
||||
}
|
||||
|
||||
if (drawingAnchor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!drawingAnchor) {
|
||||
return drawing;
|
||||
}
|
||||
|
||||
const { skeDrawing, pageMarginTop, pageMarginLeft } = drawingAnchor;
|
||||
const oldPositionH = oldDrawing.docTransform.positionH;
|
||||
const oldPositionV = oldDrawing.docTransform.positionV;
|
||||
let posOffsetH = skeDrawing.aLeft;
|
||||
let posOffsetV = skeDrawing.aTop;
|
||||
|
||||
if (oldPositionH.relativeFrom === ObjectRelativeFromH.MARGIN) {
|
||||
posOffsetH -= pageMarginLeft;
|
||||
} else if (oldPositionH.relativeFrom === ObjectRelativeFromH.COLUMN) {
|
||||
posOffsetH -= skeDrawing.columnLeft;
|
||||
}
|
||||
|
||||
if (oldPositionV.relativeFrom === ObjectRelativeFromV.PAGE) {
|
||||
posOffsetV += pageMarginTop;
|
||||
} else if (oldPositionV.relativeFrom === ObjectRelativeFromV.LINE) {
|
||||
posOffsetV -= skeDrawing.lineTop;
|
||||
} else if (oldPositionV.relativeFrom === ObjectRelativeFromV.PARAGRAPH) {
|
||||
posOffsetV -= skeDrawing.blockAnchorTop;
|
||||
}
|
||||
|
||||
return {
|
||||
...oldDrawing,
|
||||
...drawing,
|
||||
docTransform: {
|
||||
...oldDrawing.docTransform,
|
||||
...drawing.docTransform,
|
||||
positionH: { relativeFrom: oldPositionH.relativeFrom, posOffset: posOffsetH },
|
||||
positionV: { relativeFrom: oldPositionV.relativeFrom, posOffset: posOffsetV },
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private _addDrawings(unitId: string, drawings: IDocDrawing[]) {
|
||||
const drawingManagerService = this._drawingManagerService;
|
||||
const docDrawingService = this._docDrawingService;
|
||||
|
||||
+5
-4
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, IDocDrawingBase, IDocDrawingPosition, Nullable } from '@univerjs/core';
|
||||
import type { IDrawingDocTransform, IUpdateDrawingDocTransformCommandParams } from '@univerjs/docs-drawing';
|
||||
import type { BaseObject, Documents, IDocumentSkeletonGlyph, IDocumentSkeletonPage, IDocumentSkeletonRow, IDocumentSkeletonTable, Image, IPoint, Viewport } from '@univerjs/engine-render';
|
||||
import type { IDrawingDocTransform } from '../commands/commands/update-doc-drawing.command';
|
||||
import {
|
||||
BooleanNumber,
|
||||
COLORS,
|
||||
@@ -33,10 +33,11 @@ import {
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { DocSkeletonManagerService } from '@univerjs/docs';
|
||||
import { UpdateDrawingDocTransformCommand } from '@univerjs/docs-drawing';
|
||||
import { DocSelectionRenderService, getAnchorBounding, getDocObject, getOneTextSelectionRange, NodePositionConvertToCursor, TEXT_RANGE_LAYER_INDEX } from '@univerjs/docs-ui';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import { DocumentSkeletonPageType, getColor, IRenderManagerService, Liquid, PageLayoutType, Rect, Vector2 } from '@univerjs/engine-render';
|
||||
import { IMoveInlineDrawingCommand, ITransformNonInlineDrawingCommand, UpdateDrawingDocTransformCommand } from '../commands/commands/update-doc-drawing.command';
|
||||
import { IMoveInlineDrawingCommand, ITransformNonInlineDrawingCommand } from '../commands/commands/update-doc-drawing.command';
|
||||
import { getDocsTableCellDrawingOffset } from './render-controllers/doc-drawing-transform-update.controller';
|
||||
|
||||
const INLINE_DRAWING_ANCHOR_KEY_PREFIX = '__InlineDrawingAnchor__';
|
||||
@@ -368,7 +369,7 @@ export class DocDrawingTransformerController extends Disposable {
|
||||
}
|
||||
|
||||
if (drawings.length > 0 && unitId && subUnitId) {
|
||||
this._commandService.executeCommand(UpdateDrawingDocTransformCommand.id, {
|
||||
this._commandService.executeCommand<IUpdateDrawingDocTransformCommandParams>(UpdateDrawingDocTransformCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
drawings,
|
||||
@@ -718,7 +719,7 @@ export class DocDrawingTransformerController extends Disposable {
|
||||
}
|
||||
|
||||
if (drawings.length > 0 && unitId && subUnitId) {
|
||||
this._commandService.executeCommand(UpdateDrawingDocTransformCommand.id, {
|
||||
this._commandService.executeCommand<IUpdateDrawingDocTransformCommandParams>(UpdateDrawingDocTransformCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
drawings,
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, IDisposable, IDrawingSearch, ITransformState, Nullable } from '@univerjs/core';
|
||||
import type { IDocFloatDom } from '@univerjs/docs-drawing';
|
||||
import type { IDocFloatDom, IInsertDocDrawingCommandParams } from '@univerjs/docs-drawing';
|
||||
import type { ISetDocZoomRatioOperationParams } from '@univerjs/docs-ui';
|
||||
import type { IDocFloatDomDataBase } from '@univerjs/drawing';
|
||||
import type { IBoundRectNoAngle, IDocsCustomBlockRenderViewport, IRender, Rect, Scene } from '@univerjs/engine-render';
|
||||
import type { IFloatDomLayout } from '@univerjs/ui';
|
||||
import type { IInsertDrawingCommandParams } from '../commands/commands/interfaces';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
@@ -37,13 +36,13 @@ import {
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { docDrawingPositionToTransform, DocSkeletonManagerService } from '@univerjs/docs';
|
||||
import { InsertDocDrawingCommand } from '@univerjs/docs-drawing';
|
||||
import { SetDocZoomRatioOperation, VIEWPORT_KEY } from '@univerjs/docs-ui';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import { DrawingRenderService } from '@univerjs/drawing-ui';
|
||||
import { CURSOR_TYPE, IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { CanvasFloatDomService } from '@univerjs/ui';
|
||||
import { BehaviorSubject, map, of, switchMap } from 'rxjs';
|
||||
import { InsertDocDrawingCommand } from '../commands/commands/insert-doc-drawing.command';
|
||||
|
||||
export function calcDocFloatDomPositionByRect(
|
||||
rect: IBoundRectNoAngle,
|
||||
@@ -444,7 +443,7 @@ export class DocFloatDomController extends Disposable {
|
||||
angle: 0,
|
||||
};
|
||||
const drawingId = opts.drawingId ?? generateRandomId();
|
||||
const params: IInsertDrawingCommandParams = {
|
||||
const params: IInsertDocDrawingCommandParams = {
|
||||
unitId: currentDoc.getUnitId(),
|
||||
drawings: [
|
||||
{
|
||||
@@ -461,7 +460,7 @@ export class DocFloatDomController extends Disposable {
|
||||
},
|
||||
],
|
||||
};
|
||||
this._commandService.syncExecuteCommand(InsertDocDrawingCommand.id, params);
|
||||
this._commandService.syncExecuteCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, params);
|
||||
|
||||
return drawingId;
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,11 +16,11 @@
|
||||
|
||||
import { BooleanNumber, FOCUSING_COMMON_DRAWINGS } from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { SetDocDrawingArrangeCommand } from '@univerjs/docs-drawing';
|
||||
import { DocumentEditArea } from '@univerjs/engine-render';
|
||||
import { Subject } from 'rxjs';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { GroupDocDrawingCommand } from '../../../commands/commands/group-doc-drawing.command';
|
||||
import { SetDocDrawingArrangeCommand } from '../../../commands/commands/set-drawing-arrange.command';
|
||||
import { UngroupDocDrawingCommand } from '../../../commands/commands/ungroup-doc-drawing.command';
|
||||
import { DocDrawingUpdateRenderController } from '../doc-drawing-update.render-controller';
|
||||
|
||||
|
||||
+6
-10
@@ -16,10 +16,8 @@
|
||||
|
||||
import type { DocumentDataModel, ICommandInfo, IDocDrawingPosition, IDrawingParam, IImageIoServiceParam, Nullable } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { IDocDrawing, IInsertDocDrawingCommandParams, ISetDocDrawingArrangeCommandParams } from '@univerjs/docs-drawing';
|
||||
import type { Documents, Image, IRenderContext, IRenderModule } from '@univerjs/engine-render';
|
||||
import type { IInsertDrawingCommandParams } from '../../commands/commands/interfaces';
|
||||
import type { ISetDrawingArrangeCommandParams } from '../../commands/commands/set-drawing-arrange.command';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import {
|
||||
BooleanNumber,
|
||||
@@ -37,7 +35,7 @@ import {
|
||||
} from '@univerjs/core';
|
||||
import { MessageType } from '@univerjs/design';
|
||||
import { buildDocTransform, docDrawingPositionToTransform, DocSelectionManagerService, DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { IDocDrawingService } from '@univerjs/docs-drawing';
|
||||
import { IDocDrawingService, InsertDocDrawingCommand, SetDocDrawingArrangeCommand } from '@univerjs/docs-drawing';
|
||||
import { DocSelectionRenderService } from '@univerjs/docs-ui';
|
||||
import {
|
||||
DRAWING_IMAGE_ALLOW_IMAGE_LIST,
|
||||
@@ -53,8 +51,6 @@ import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render
|
||||
import { ILocalFileService, IMessageService } from '@univerjs/ui';
|
||||
import { debounceTime } from 'rxjs';
|
||||
import { GroupDocDrawingCommand } from '../../commands/commands/group-doc-drawing.command';
|
||||
import { InsertDocDrawingCommand } from '../../commands/commands/insert-doc-drawing.command';
|
||||
import { SetDocDrawingArrangeCommand } from '../../commands/commands/set-drawing-arrange.command';
|
||||
import { UngroupDocDrawingCommand } from '../../commands/commands/ungroup-doc-drawing.command';
|
||||
import { DocRefreshDrawingsService } from '../../services/doc-refresh-drawings.service';
|
||||
|
||||
@@ -211,10 +207,10 @@ export class DocDrawingUpdateRenderController extends Disposable implements IRen
|
||||
docDrawingParams.push(docDrawingParam);
|
||||
}
|
||||
|
||||
this._commandService.executeCommand(InsertDocDrawingCommand.id, {
|
||||
this._commandService.executeCommand<IInsertDocDrawingCommandParams>(InsertDocDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: docDrawingParams,
|
||||
} as IInsertDrawingCommandParams);
|
||||
});
|
||||
}
|
||||
|
||||
private _isInsertInHeaderFooter() {
|
||||
@@ -262,12 +258,12 @@ export class DocDrawingUpdateRenderController extends Disposable implements IRen
|
||||
this._drawingManagerService.featurePluginOrderUpdate$.subscribe((params) => {
|
||||
const { unitId, subUnitId, drawingIds, arrangeType } = params;
|
||||
|
||||
this._commandService.executeCommand(SetDocDrawingArrangeCommand.id, {
|
||||
this._commandService.executeCommand<ISetDocDrawingArrangeCommandParams>(SetDocDrawingArrangeCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
drawingIds,
|
||||
arrangeType,
|
||||
} as ISetDrawingArrangeCommandParams);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,20 +18,15 @@ import { Disposable, ICommandService } from '@univerjs/core';
|
||||
import { IMenuManagerService, IShortcutService } from '@univerjs/ui';
|
||||
import { DeleteDocDrawingsCommand } from '../commands/commands/delete-doc-drawing.command';
|
||||
import { GroupDocDrawingCommand } from '../commands/commands/group-doc-drawing.command';
|
||||
import { InsertDocDrawingCommand } from '../commands/commands/insert-doc-drawing.command';
|
||||
import { InsertDocImageCommand } from '../commands/commands/insert-image.command';
|
||||
import { InsertDocEllipseShapeCommand, InsertDocRectangleShapeCommand } from '../commands/commands/insert-shape.command';
|
||||
import { MoveDocDrawingsCommand } from '../commands/commands/move-drawings.command';
|
||||
import { RemoveDocDrawingCommand } from '../commands/commands/remove-doc-drawing.command';
|
||||
import { SetDocDrawingArrangeCommand } from '../commands/commands/set-drawing-arrange.command';
|
||||
import { UngroupDocDrawingCommand } from '../commands/commands/ungroup-doc-drawing.command';
|
||||
import {
|
||||
IMoveInlineDrawingCommand,
|
||||
ITransformNonInlineDrawingCommand,
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
} from '../commands/commands/update-doc-drawing.command';
|
||||
import { ClearDocDrawingTransformerOperation } from '../commands/operations/clear-drawing-transformer.operation';
|
||||
import { EditDocDrawingOperation } from '../commands/operations/edit-doc-drawing.operation';
|
||||
@@ -68,14 +63,10 @@ export class DocDrawingUIController extends Disposable {
|
||||
InsertDocImageCommand,
|
||||
InsertDocRectangleShapeCommand,
|
||||
InsertDocEllipseShapeCommand,
|
||||
InsertDocDrawingCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
IMoveInlineDrawingCommand,
|
||||
ITransformNonInlineDrawingCommand,
|
||||
RemoveDocDrawingCommand,
|
||||
SidebarDocDrawingOperation,
|
||||
ClearDocDrawingTransformerOperation,
|
||||
EditDocDrawingOperation,
|
||||
@@ -83,7 +74,6 @@ export class DocDrawingUIController extends Disposable {
|
||||
UngroupDocDrawingCommand,
|
||||
MoveDocDrawingsCommand,
|
||||
DeleteDocDrawingsCommand,
|
||||
SetDocDrawingArrangeCommand,
|
||||
].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command)));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,10 @@ import './global.css';
|
||||
|
||||
export { DeleteDocDrawingsCommand } from './commands/commands/delete-doc-drawing.command';
|
||||
export { GroupDocDrawingCommand } from './commands/commands/group-doc-drawing.command';
|
||||
export { InsertDocDrawingCommand } from './commands/commands/insert-doc-drawing.command';
|
||||
export { InsertDocImageCommand } from './commands/commands/insert-image.command';
|
||||
export { InsertDocEllipseShapeCommand, InsertDocRectangleShapeCommand } from './commands/commands/insert-shape.command';
|
||||
export { MoveDocDrawingsCommand } from './commands/commands/move-drawings.command';
|
||||
export { RemoveDocDrawingCommand } from './commands/commands/remove-doc-drawing.command';
|
||||
export { SetDocDrawingArrangeCommand } from './commands/commands/set-drawing-arrange.command';
|
||||
export { UngroupDocDrawingCommand } from './commands/commands/ungroup-doc-drawing.command';
|
||||
export { UpdateDrawingDocTransformCommand } from './commands/commands/update-doc-drawing.command';
|
||||
export { ClearDocDrawingTransformerOperation } from './commands/operations/clear-drawing-transformer.operation';
|
||||
export { EditDocDrawingOperation } from './commands/operations/edit-doc-drawing.operation';
|
||||
export { SidebarDocDrawingOperation } from './commands/operations/open-drawing-panel.operation';
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
RxDisposable,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { IDocDrawingAdapterService } from '@univerjs/docs-drawing';
|
||||
import { IDocDrawingAdapterService, RemoveDocDrawingCommand } from '@univerjs/docs-drawing';
|
||||
import { DocCanvasPopManagerService } from '@univerjs/docs-ui';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import {
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
} from '@univerjs/drawing-ui';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { takeUntil } from 'rxjs';
|
||||
import { RemoveDocDrawingCommand } from '../commands/commands/remove-doc-drawing.command';
|
||||
import { EditDocDrawingOperation } from '../commands/operations/edit-doc-drawing.operation';
|
||||
import { SidebarDocDrawingOperation } from '../commands/operations/open-drawing-panel.operation';
|
||||
import { DocDrawingFloatingToolbarAdapterService } from '../services/doc-drawing-floating-toolbar-adapter.service';
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, ICommandInfo, IDrawingParam, IObjectPositionH, IObjectPositionV, Nullable } from '@univerjs/core';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { IDocDrawing, IUpdateDrawingDocTransformCommandParams } from '@univerjs/docs-drawing';
|
||||
import type { IDocumentSkeletonDrawing } from '@univerjs/engine-render';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import {
|
||||
@@ -30,12 +30,12 @@ import {
|
||||
} from '@univerjs/core';
|
||||
import { Checkbox, clsx, InputNumber, Select } from '@univerjs/design';
|
||||
import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { UpdateDrawingDocTransformCommand } from '@univerjs/docs-drawing';
|
||||
import { DocSelectionRenderService } from '@univerjs/docs-ui';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { UpdateDrawingDocTransformCommand } from '../../commands/commands/update-doc-drawing.command';
|
||||
|
||||
const MIN_OFFSET = -1000;
|
||||
const MAX_OFFSET = 1000;
|
||||
@@ -135,7 +135,7 @@ export const DocDrawingPosition = (props: IDocDrawingPositionProps) => {
|
||||
};
|
||||
});
|
||||
|
||||
commandService.executeCommand(UpdateDrawingDocTransformCommand.id, {
|
||||
commandService.executeCommand<IUpdateDrawingDocTransformCommandParams>(UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: focusDrawings[0].unitId,
|
||||
subUnitId: focusDrawings[0].unitId,
|
||||
drawings: drawings.map((drawing) => ({
|
||||
|
||||
@@ -28,14 +28,13 @@ import {
|
||||
} from '@univerjs/core';
|
||||
import { clsx, InputNumber, Radio, RadioGroup } from '@univerjs/design';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { TextWrappingStyle, UpdateDocDrawingWrappingStyleCommand } from '@univerjs/docs-drawing';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
TextWrappingStyle,
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
} from '../../commands/commands/update-doc-drawing.command';
|
||||
|
||||
|
||||
-2
@@ -37,7 +37,6 @@ import { act } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { createDocUiTestBed } from '../../../__tests__/create-doc-ui-test-bed';
|
||||
import { UpdateDrawingDocTransformCommand } from '../../../commands/commands/update-doc-drawing.command';
|
||||
import locale from '../../../locale/en-US';
|
||||
import { DocDrawingPosition } from '../DocDrawingPosition';
|
||||
|
||||
@@ -239,7 +238,6 @@ function createPositionTestBed() {
|
||||
|
||||
const commandService = injector.get(ICommandService);
|
||||
[
|
||||
UpdateDrawingDocTransformCommand,
|
||||
RichTextEditingMutation as unknown as ICommand,
|
||||
].forEach((command) => commandService.registerCommand(command));
|
||||
|
||||
|
||||
+5
-2
@@ -40,10 +40,11 @@ import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { createDocUiTestBed } from '../../../__tests__/create-doc-ui-test-bed';
|
||||
import {
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
} from '../../../commands/commands/update-doc-drawing.command';
|
||||
import { DocDrawingAddRemoveController } from '../../../controllers/doc-drawing-notification.controller';
|
||||
import locale from '../../../locale/en-US';
|
||||
import { DocRefreshDrawingsService } from '../../../services/doc-refresh-drawings.service';
|
||||
import { DocDrawingTextWrap } from '../DocDrawingTextWrap';
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
@@ -215,12 +216,13 @@ function createPanelTestBed() {
|
||||
injector.add([DocDrawingService]);
|
||||
injector.add([IDocDrawingService, { useClass: DocDrawingService }]);
|
||||
injector.add([IDrawingManagerService, { useClass: DrawingManagerService }]);
|
||||
injector.add([DocRefreshDrawingsService]);
|
||||
injector.add([DocDrawingController]);
|
||||
injector.add([DocDrawingAddRemoveController]);
|
||||
|
||||
const commandService = injector.get(ICommandService);
|
||||
[
|
||||
UpdateDocDrawingDistanceCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDocDrawingWrapTextCommand,
|
||||
RichTextEditingMutation as unknown as ICommand,
|
||||
].forEach((command) => commandService.registerCommand(command));
|
||||
@@ -231,6 +233,7 @@ function createPanelTestBed() {
|
||||
injector.get(LocaleService).setLocale(LocaleType.EN_US);
|
||||
|
||||
injector.get(DocDrawingController).loadDrawingDataForUnit(UNIT_ID);
|
||||
injector.get(DocDrawingAddRemoveController);
|
||||
injector.get(IDrawingManagerService).focusDrawing([{
|
||||
unitId: UNIT_ID,
|
||||
subUnitId: UNIT_ID,
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*"
|
||||
"./*": "./src/*",
|
||||
"./facade": "./src/facade/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
@@ -45,6 +46,16 @@
|
||||
"require": "./lib/cjs/*",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./facade": {
|
||||
"import": "./lib/es/facade.js",
|
||||
"require": "./lib/cjs/facade.js",
|
||||
"types": "./lib/types/facade/index.d.ts"
|
||||
},
|
||||
"./lib/facade": {
|
||||
"import": "./lib/es/facade.js",
|
||||
"require": "./lib/cjs/facade.js",
|
||||
"types": "./lib/types/facade/index.d.ts"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
@@ -65,10 +76,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/drawing": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs-infra/shared": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^4.1.10"
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { DocumentDataModel, IDocumentData } from '@univerjs/core';
|
||||
import type { IUpdateDocDrawingWrappingStyleParams } from '@univerjs/docs-drawing';
|
||||
import {
|
||||
BooleanNumber,
|
||||
ICommandService,
|
||||
ImageSourceType,
|
||||
IUniverInstanceService,
|
||||
ObjectRelativeFromH,
|
||||
ObjectRelativeFromV,
|
||||
PositionedObjectLayoutType,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { TextWrappingStyle, UpdateDocDrawingWrappingStyleCommand } from '@univerjs/docs-drawing';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createFacadeTestBed } from '../../../facade/__tests__/create-test-bed';
|
||||
|
||||
class MockImage {
|
||||
width = 800;
|
||||
height = 400;
|
||||
onload: (() => void) | null = null;
|
||||
onerror: (() => void) | null = null;
|
||||
|
||||
get src(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
set src(_value: string) {
|
||||
queueMicrotask(() => this.onload?.());
|
||||
}
|
||||
}
|
||||
|
||||
describe('UpdateDocDrawingWrappingStyleCommand', () => {
|
||||
let testBed: ReturnType<typeof createFacadeTestBed>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('Image', MockImage);
|
||||
testBed = createFacadeTestBed();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
testBed.univer.dispose();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('updates the requested document without render services or current-unit dependence', async () => {
|
||||
const image = await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
width: 160,
|
||||
height: 90,
|
||||
textRange: {
|
||||
startOffset: 3,
|
||||
endOffset: 3,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
});
|
||||
const positionH = { relativeFrom: ObjectRelativeFromH.MARGIN, posOffset: 80 };
|
||||
const positionV = { relativeFrom: ObjectRelativeFromV.PAGE, posOffset: 120 };
|
||||
const drawing = image!.getImageData()!;
|
||||
const otherDocument = testBed.univer.createUnit<IDocumentData, DocumentDataModel>(
|
||||
UniverInstanceType.UNIVER_DOC,
|
||||
{
|
||||
id: 'other-doc',
|
||||
documentStyle: {},
|
||||
body: { dataStream: '\r\n', paragraphs: [{ startIndex: 0, paragraphId: 'other-paragraph' }], customBlocks: [] },
|
||||
drawings: {},
|
||||
drawingsOrder: [],
|
||||
}
|
||||
);
|
||||
testBed.injector.get(IUniverInstanceService).focusUnit(otherDocument.getUnitId());
|
||||
|
||||
const result = testBed.injector.get(ICommandService).syncExecuteCommand<IUpdateDocDrawingWrappingStyleParams>(
|
||||
UpdateDocDrawingWrappingStyleCommand.id,
|
||||
{
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [{
|
||||
...drawing,
|
||||
docTransform: { ...drawing.docTransform, positionH, positionV },
|
||||
}],
|
||||
wrappingStyle: TextWrappingStyle.BEHIND_TEXT,
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(testBed.document.getImage(image!.getId())?.getImageData()).toMatchObject({
|
||||
layoutType: PositionedObjectLayoutType.WRAP_NONE,
|
||||
behindDoc: BooleanNumber.TRUE,
|
||||
docTransform: { positionH, positionV },
|
||||
});
|
||||
expect(otherDocument.getSnapshot().drawings).toEqual({});
|
||||
});
|
||||
});
|
||||
+48
-52
@@ -16,10 +16,12 @@
|
||||
|
||||
import type { DocumentDataModel, IAccessor, ICommand, IMutationInfo, ITextRangeParam, JSONXActions } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { IInsertDrawingCommandParams } from './interfaces';
|
||||
import type { IDocDrawing } from '../../services/doc-drawing.service';
|
||||
import {
|
||||
BooleanNumber,
|
||||
BuildTextUtils,
|
||||
CommandType,
|
||||
getCustomBlockIdsInSelections,
|
||||
getRichTextEditPath,
|
||||
ICommandService,
|
||||
IUniverInstanceService,
|
||||
@@ -28,8 +30,13 @@ import {
|
||||
TextXActionType,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { DocContentInsertService, DocSelectionManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { getCustomBlockIdsInSelections } from '@univerjs/docs-ui';
|
||||
import { DocSelectionManagerService, getContentInsertRange, normalizeTextRange, RichTextEditingMutation } from '@univerjs/docs';
|
||||
|
||||
export interface IInsertDocDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: IDocDrawing[];
|
||||
textRange?: ITextRangeParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command to insert new drawings
|
||||
@@ -40,49 +47,46 @@ export const InsertDocDrawingCommand: ICommand = {
|
||||
type: CommandType.COMMAND,
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
handler: (accessor: IAccessor, params?: IInsertDrawingCommandParams) => {
|
||||
if (params == null) {
|
||||
handler: (accessor: IAccessor, params?: IInsertDocDrawingCommandParams) => {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const docSelectionManagerService = accessor.get(DocSelectionManagerService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
|
||||
const activeTextRange = docSelectionManagerService.getActiveTextRange();
|
||||
const documentDataModel = univerInstanceService.getCurrentUnitOfType<DocumentDataModel>(UniverInstanceType.UNIVER_DOC);
|
||||
if (documentDataModel == null) {
|
||||
const { unitId, drawings, textRange } = params;
|
||||
const documentDataModel = univerInstanceService.getUnit<DocumentDataModel>(unitId, UniverInstanceType.UNIVER_DOC);
|
||||
if (!documentDataModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const unitId = documentDataModel.getUnitId();
|
||||
const explicitTextRange = params.textRange == null ? null : normalizeTextRange(params.textRange);
|
||||
const contentInsertRange = explicitTextRange ?? getContentInsertRange(accessor, unitId);
|
||||
const targetTextRange = contentInsertRange
|
||||
? {
|
||||
...activeTextRange,
|
||||
startOffset: contentInsertRange.startOffset,
|
||||
endOffset: contentInsertRange.endOffset,
|
||||
collapsed: contentInsertRange.startOffset === contentInsertRange.endOffset,
|
||||
segmentId: contentInsertRange.segmentId ?? activeTextRange?.segmentId ?? '',
|
||||
}
|
||||
: activeTextRange;
|
||||
const targetTextRange = resolveDocDrawingInsertTextRange(accessor, unitId, textRange);
|
||||
|
||||
if (targetTextRange == null) {
|
||||
if (!targetTextRange) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { drawings } = params;
|
||||
const { collapsed, startOffset, segmentId = '' } = targetTextRange;
|
||||
const body = documentDataModel.getSelfOrHeaderFooterModel(segmentId)?.getBody();
|
||||
|
||||
if (body == null) {
|
||||
if (!body) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const textX = new TextX();
|
||||
const jsonX = JSONX.getInstance();
|
||||
const rawActions: JSONXActions = [];
|
||||
const drawingOrderLength = documentDataModel.getSnapshot().drawingsOrder?.length ?? 0;
|
||||
const snapshot = documentDataModel.getSnapshot();
|
||||
const isHeaderFooter = !!snapshot.headers?.[segmentId] || !!snapshot.footers?.[segmentId];
|
||||
const targetDrawings = isHeaderFooter
|
||||
? drawings.map((drawing) => ({
|
||||
...drawing,
|
||||
isMultiTransform: BooleanNumber.TRUE,
|
||||
transforms: drawing.transforms ?? (drawing.transform ? [drawing.transform] : null),
|
||||
}))
|
||||
: drawings;
|
||||
const drawingOrderLength = snapshot.drawingsOrder?.length ?? 0;
|
||||
let removeDrawingLen = 0;
|
||||
|
||||
// Step 1: Insert placeholder `\b` in dataStream and add drawing to customBlocks.
|
||||
@@ -132,13 +136,13 @@ export const InsertDocDrawingCommand: ICommand = {
|
||||
textX.push({
|
||||
t: TextXActionType.INSERT,
|
||||
body: {
|
||||
dataStream: '\b'.repeat(drawings.length),
|
||||
customBlocks: drawings.map((drawing, i) => ({
|
||||
dataStream: '\b'.repeat(targetDrawings.length),
|
||||
customBlocks: targetDrawings.map((drawing, i) => ({
|
||||
startIndex: i,
|
||||
blockId: drawing.drawingId,
|
||||
})),
|
||||
},
|
||||
len: drawings.length,
|
||||
len: targetDrawings.length,
|
||||
});
|
||||
|
||||
const path = getRichTextEditPath(documentDataModel, segmentId);
|
||||
@@ -147,7 +151,7 @@ export const InsertDocDrawingCommand: ICommand = {
|
||||
rawActions.push(placeHolderAction!);
|
||||
|
||||
// Step 2: add drawing to drawings and drawingsOrder fields.
|
||||
for (const drawing of drawings) {
|
||||
for (const drawing of targetDrawings) {
|
||||
const { drawingId } = drawing;
|
||||
const addDrawingAction = jsonX.insertOp(['drawings', drawingId], drawing);
|
||||
const addDrawingOrderAction = jsonX.insertOp(['drawingsOrder', drawingOrderLength - removeDrawingLen], drawingId);
|
||||
@@ -178,31 +182,23 @@ export const InsertDocDrawingCommand: ICommand = {
|
||||
},
|
||||
};
|
||||
|
||||
function getContentInsertRange(accessor: IAccessor, unitId: string): ITextRangeParam | null {
|
||||
try {
|
||||
const range = accessor.get(DocContentInsertService).consumeInsertRange(unitId);
|
||||
if (range == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
startOffset: range.startOffset,
|
||||
endOffset: range.endOffset,
|
||||
collapsed: range.startOffset === range.endOffset,
|
||||
segmentId: range.segmentId,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
function resolveDocDrawingInsertTextRange(
|
||||
accessor: IAccessor,
|
||||
unitId: string,
|
||||
textRange?: ITextRangeParam
|
||||
): ITextRangeParam | null {
|
||||
const activeTextRange = accessor.get(DocSelectionManagerService).getActiveTextRange();
|
||||
const explicitTextRange = textRange ? normalizeTextRange(textRange) : null;
|
||||
const contentInsertRange = explicitTextRange ?? getContentInsertRange(accessor, unitId);
|
||||
if (!contentInsertRange) {
|
||||
return activeTextRange ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeTextRange(textRange: ITextRangeParam): ITextRangeParam {
|
||||
const endOffset = textRange.endOffset ?? textRange.startOffset;
|
||||
|
||||
return {
|
||||
...textRange,
|
||||
endOffset,
|
||||
collapsed: textRange.collapsed ?? textRange.startOffset === endOffset,
|
||||
segmentId: textRange.segmentId ?? '',
|
||||
...activeTextRange,
|
||||
startOffset: contentInsertRange.startOffset,
|
||||
endOffset: contentInsertRange.endOffset,
|
||||
collapsed: contentInsertRange.startOffset === contentInsertRange.endOffset,
|
||||
segmentId: contentInsertRange.segmentId ?? activeTextRange?.segmentId ?? '',
|
||||
};
|
||||
}
|
||||
+43
-74
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, IAccessor, ICommand, IDisposable, IMutationInfo, JSONXActions } from '@univerjs/core';
|
||||
import type { DocumentDataModel, DrawingTypeEnum, IAccessor, ICommand, IDisposable, IMutationInfo, ITextRangeParam, JSONXActions } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { IDocDrawing } from '@univerjs/docs-drawing';
|
||||
import type { ITextRangeWithStyle } from '@univerjs/engine-render';
|
||||
import type { IDeleteDrawingCommandParams } from './interfaces';
|
||||
import type { IDocDrawing } from '../../services/doc-drawing.service';
|
||||
import {
|
||||
CommandType,
|
||||
getRichTextEditPath,
|
||||
@@ -31,35 +29,47 @@ import {
|
||||
TextXActionType,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { IDocDrawingAdapterService } from '@univerjs/docs-drawing';
|
||||
import { DocSelectionRenderService } from '@univerjs/docs-ui';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { DocSelectionManagerService, getContentInsertRange, normalizeTextRange, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { IDocDrawingAdapterService } from '../../services/doc-drawing-adapter.service';
|
||||
|
||||
export interface IRemoveDocDrawingCommandParam {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawingId: string;
|
||||
drawingType: DrawingTypeEnum;
|
||||
}
|
||||
|
||||
export interface IRemoveDocDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: IRemoveDocDrawingCommandParam[];
|
||||
textRange?: ITextRangeParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command to remove new sheet image
|
||||
*/
|
||||
export const RemoveDocDrawingCommand: ICommand = {
|
||||
id: 'doc.command.remove-doc-image',
|
||||
type: CommandType.COMMAND,
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
handler: (accessor: IAccessor, params?: IDeleteDrawingCommandParams) => {
|
||||
handler: (accessor: IAccessor, params?: IRemoveDocDrawingCommandParams) => {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const undoRedoService = accessor.get(IUndoRedoService);
|
||||
const drawingAdapterService = accessor.get(IDocDrawingAdapterService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const renderManagerService = accessor.get(IRenderManagerService);
|
||||
const documentDataModel = univerInstanceService.getCurrentUnitOfType<DocumentDataModel>(UniverInstanceType.UNIVER_DOC);
|
||||
const docSelectionManagerService = accessor.get(DocSelectionManagerService);
|
||||
|
||||
if (params == null || documentDataModel == null) {
|
||||
const { unitId, drawings: removeDrawings, textRange } = params;
|
||||
const documentDataModel = univerInstanceService.getUnit<DocumentDataModel>(unitId, UniverInstanceType.UNIVER_DOC);
|
||||
if (!documentDataModel || removeDrawings.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const docSelectionRenderService = renderManagerService.getRenderById(params.unitId)!.with(DocSelectionRenderService)!;
|
||||
|
||||
const { drawings: removeDrawings } = params;
|
||||
|
||||
const segmentId = docSelectionRenderService.getSegment() ?? '';
|
||||
const activeTextRange = docSelectionManagerService.getActiveTextRange();
|
||||
const explicitTextRange = !textRange ? null : normalizeTextRange(textRange);
|
||||
const contentInsertRange = explicitTextRange ?? getContentInsertRange(accessor, unitId);
|
||||
const segmentId = contentInsertRange?.segmentId ?? activeTextRange?.segmentId ?? '';
|
||||
|
||||
const textX = new TextX();
|
||||
const jsonX = JSONX.getInstance();
|
||||
@@ -69,8 +79,7 @@ export const RemoveDocDrawingCommand: ICommand = {
|
||||
.filter((block) => !!block)
|
||||
.sort((a, b) => a!.startIndex > b!.startIndex ? 1 : -1);
|
||||
|
||||
const unitId = removeDrawings[0]?.unitId;
|
||||
if (unitId == null || removeCustomBlocks.length === 0) {
|
||||
if (removeCustomBlocks.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -125,70 +134,34 @@ export const RemoveDocDrawingCommand: ICommand = {
|
||||
}
|
||||
|
||||
const memoryCursor = new MemoryCursor();
|
||||
|
||||
memoryCursor.reset();
|
||||
|
||||
const cursorIndex = removeCustomBlocks[0]!.startIndex;
|
||||
const textRanges = [
|
||||
{
|
||||
startOffset: cursorIndex,
|
||||
endOffset: cursorIndex,
|
||||
},
|
||||
] as ITextRangeWithStyle[];
|
||||
|
||||
const textRanges = [{ startOffset: cursorIndex, endOffset: cursorIndex }] as IRichTextEditingMutationParams['textRanges'];
|
||||
const doMutation: IMutationInfo<IRichTextEditingMutationParams> = {
|
||||
id: RichTextEditingMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
actions: [],
|
||||
textRanges,
|
||||
},
|
||||
params: { unitId, actions: [], textRanges },
|
||||
};
|
||||
|
||||
const rawActions: JSONXActions = [];
|
||||
|
||||
for (const block of removeCustomBlocks) {
|
||||
const { startIndex } = block!;
|
||||
|
||||
if (startIndex > memoryCursor.cursor) {
|
||||
textX.push({
|
||||
t: TextXActionType.RETAIN,
|
||||
len: startIndex - memoryCursor.cursor,
|
||||
});
|
||||
textX.push({ t: TextXActionType.RETAIN, len: startIndex - memoryCursor.cursor });
|
||||
}
|
||||
|
||||
textX.push({
|
||||
t: TextXActionType.DELETE,
|
||||
len: 1,
|
||||
});
|
||||
|
||||
textX.push({ t: TextXActionType.DELETE, len: 1 });
|
||||
memoryCursor.moveCursorTo(startIndex + 1);
|
||||
}
|
||||
|
||||
const path = getRichTextEditPath(documentDataModel, segmentId);
|
||||
rawActions.push(jsonX.editOp(textX.serialize(), path)!);
|
||||
rawActions.push(jsonX.editOp(textX.serialize(), getRichTextEditPath(documentDataModel, segmentId))!);
|
||||
|
||||
for (const block of removeCustomBlocks) {
|
||||
const { blockId } = block!;
|
||||
|
||||
const drawingOrder = documentDataModel.getDrawingsOrder();
|
||||
const drawingIndex = drawingOrder!.indexOf(blockId);
|
||||
|
||||
const removeDrawingAction = jsonX.removeOp(['drawings', blockId], drawings[blockId]);
|
||||
const removeDrawingOrderAction = jsonX.removeOp(['drawingsOrder', drawingIndex], blockId);
|
||||
|
||||
rawActions.push(removeDrawingAction!);
|
||||
rawActions.push(removeDrawingOrderAction!);
|
||||
const drawingIndex = documentDataModel.getDrawingsOrder()!.indexOf(blockId);
|
||||
rawActions.push(jsonX.removeOp(['drawings', blockId], drawings[blockId])!);
|
||||
rawActions.push(jsonX.removeOp(['drawingsOrder', drawingIndex], blockId)!);
|
||||
}
|
||||
|
||||
doMutation.params.actions = rawActions.reduce((acc, cur) => {
|
||||
return JSONX.compose(acc, cur as JSONXActions);
|
||||
}, null as JSONXActions);
|
||||
|
||||
const result = commandService.syncExecuteCommand<
|
||||
IRichTextEditingMutationParams,
|
||||
IRichTextEditingMutationParams
|
||||
>(doMutation.id, doMutation.params);
|
||||
doMutation.params.actions = rawActions.reduce((acc, cur) => JSONX.compose(acc, cur as JSONXActions), null as JSONXActions);
|
||||
const result = commandService.syncExecuteCommand<IRichTextEditingMutationParams, IRichTextEditingMutationParams>(doMutation.id, doMutation.params);
|
||||
|
||||
if (!result && batchingDisposable != null) {
|
||||
batchingDisposable.dispose();
|
||||
@@ -208,12 +181,10 @@ function executeResourceMutationGroups(
|
||||
const executedUndoGroups: IMutationInfo[][] = [];
|
||||
|
||||
for (const mutationGroup of mutationGroups) {
|
||||
const result = executeMutations(mutationGroup.redoMutations, commandService);
|
||||
if (!result) {
|
||||
if (!executeMutations(mutationGroup.redoMutations, commandService)) {
|
||||
executeMutationGroups([...executedUndoGroups].reverse(), commandService);
|
||||
return false;
|
||||
}
|
||||
|
||||
executedUndoGroups.push(mutationGroup.undoMutations);
|
||||
}
|
||||
|
||||
@@ -221,9 +192,7 @@ function executeResourceMutationGroups(
|
||||
}
|
||||
|
||||
function executeMutationGroups(mutationGroups: IMutationInfo[][], commandService: ICommandService): void {
|
||||
mutationGroups.forEach((mutations) => {
|
||||
executeMutations(mutations, commandService);
|
||||
});
|
||||
mutationGroups.forEach((mutations) => executeMutations(mutations, commandService));
|
||||
}
|
||||
|
||||
function executeMutations(mutations: IMutationInfo[], commandService: ICommandService): boolean {
|
||||
+3
-3
@@ -25,9 +25,9 @@ import {
|
||||
Tools,
|
||||
} from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { IDocDrawingService } from '@univerjs/docs-drawing';
|
||||
import { IDocDrawingService } from '../../services/doc-drawing.service';
|
||||
|
||||
export interface ISetDrawingArrangeCommandParams extends IDrawingOrderMapParam {
|
||||
export interface ISetDocDrawingArrangeCommandParams extends IDrawingOrderMapParam {
|
||||
arrangeType: ArrangeTypeEnum;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export const SetDocDrawingArrangeCommand: ICommand = {
|
||||
|
||||
type: CommandType.COMMAND,
|
||||
|
||||
handler: (accessor: IAccessor, params?: ISetDrawingArrangeCommandParams) => {
|
||||
handler: (accessor: IAccessor, params?: ISetDocDrawingArrangeCommandParams) => {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const docDrawingService = accessor.get(IDocDrawingService);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { DocumentDataModel, IAccessor, ICommand, IObjectPositionH, IObjectPositionV, ISize, JSONXActions } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import {
|
||||
CommandType,
|
||||
ICommandService,
|
||||
IUniverInstanceService,
|
||||
JSONX,
|
||||
Tools,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
|
||||
export interface IDrawingDocTransform {
|
||||
drawingId: string;
|
||||
key: 'size' | 'angle' | 'positionH' | 'positionV';
|
||||
value: ISize | number | IObjectPositionH | IObjectPositionV;
|
||||
}
|
||||
|
||||
export interface IUpdateDrawingDocTransformCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawings: IDrawingDocTransform[];
|
||||
}
|
||||
|
||||
export const UpdateDrawingDocTransformCommand: ICommand = {
|
||||
id: 'doc.command.update-drawing-doc-transform',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor: IAccessor, params?: IUpdateDrawingDocTransformCommandParams) => {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
|
||||
const { unitId, drawings } = params;
|
||||
const documentDataModel = univerInstanceService.getUnit<DocumentDataModel>(unitId, UniverInstanceType.UNIVER_DOC);
|
||||
if (!documentDataModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const oldDrawings = documentDataModel.getSnapshot().drawings ?? {};
|
||||
const jsonX = JSONX.getInstance();
|
||||
const actions: JSONXActions = [];
|
||||
|
||||
for (const { drawingId, key, value } of drawings) {
|
||||
const oldValue = oldDrawings[drawingId]?.docTransform?.[key];
|
||||
if (oldValue == null || !Tools.diffValue(oldValue, value)) {
|
||||
actions.push(jsonX.replaceOp(['drawings', drawingId, 'docTransform', key], oldValue, value)!);
|
||||
}
|
||||
}
|
||||
|
||||
return Boolean(commandService.syncExecuteCommand<IRichTextEditingMutationParams, IRichTextEditingMutationParams>(RichTextEditingMutation.id, {
|
||||
unitId,
|
||||
actions: actions.reduce((acc, action) => JSONX.compose(acc, action as JSONXActions), null as JSONXActions),
|
||||
textRanges: null,
|
||||
debounce: true,
|
||||
}));
|
||||
},
|
||||
};
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { DocumentDataModel, IAccessor, ICommand, IMutationInfo, JSONXActions } from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { IDocDrawing } from '../../services/doc-drawing.service';
|
||||
import {
|
||||
BooleanNumber,
|
||||
CommandType,
|
||||
ICommandService,
|
||||
IUniverInstanceService,
|
||||
JSONX,
|
||||
PositionedObjectLayoutType,
|
||||
Tools,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
|
||||
export enum TextWrappingStyle {
|
||||
INLINE = 'inline',
|
||||
BEHIND_TEXT = 'behindText',
|
||||
IN_FRONT_OF_TEXT = 'inFrontOfText',
|
||||
WRAP_SQUARE = 'wrapSquare',
|
||||
WRAP_TOP_AND_BOTTOM = 'wrapTopAndBottom',
|
||||
}
|
||||
|
||||
export const WRAPPING_STYLE_TO_LAYOUT_TYPE: Record<TextWrappingStyle, PositionedObjectLayoutType> = {
|
||||
[TextWrappingStyle.INLINE]: PositionedObjectLayoutType.INLINE,
|
||||
[TextWrappingStyle.WRAP_SQUARE]: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
[TextWrappingStyle.WRAP_TOP_AND_BOTTOM]: PositionedObjectLayoutType.WRAP_TOP_AND_BOTTOM,
|
||||
[TextWrappingStyle.IN_FRONT_OF_TEXT]: PositionedObjectLayoutType.WRAP_NONE,
|
||||
[TextWrappingStyle.BEHIND_TEXT]: PositionedObjectLayoutType.WRAP_NONE,
|
||||
};
|
||||
|
||||
export interface IUpdateDocDrawingWrappingStyleParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawings: IDocDrawing[];
|
||||
wrappingStyle: TextWrappingStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates document drawing wrapping styles and optional persisted positions.
|
||||
*/
|
||||
export const UpdateDocDrawingWrappingStyleCommand: ICommand = {
|
||||
id: 'doc.command.update-doc-drawing-wrapping-style',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor: IAccessor, params?: IUpdateDocDrawingWrappingStyleParams) => {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { drawings, wrappingStyle, unitId } = params;
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const documentDataModel = accessor.get(IUniverInstanceService).getUnit<DocumentDataModel>(
|
||||
unitId,
|
||||
UniverInstanceType.UNIVER_DOC
|
||||
);
|
||||
if (!documentDataModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const oldDrawings = documentDataModel.getDrawings() ?? {};
|
||||
const jsonX = JSONX.getInstance();
|
||||
const rawActions: JSONXActions = [];
|
||||
|
||||
for (const drawing of drawings) {
|
||||
const oldDrawing = oldDrawings[drawing.drawingId] as IDocDrawing | undefined;
|
||||
if (!oldDrawing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const layoutType = WRAPPING_STYLE_TO_LAYOUT_TYPE[wrappingStyle];
|
||||
if (oldDrawing.layoutType !== layoutType) {
|
||||
rawActions.push(jsonX.replaceOp(['drawings', drawing.drawingId, 'layoutType'], oldDrawing.layoutType, layoutType)!);
|
||||
}
|
||||
|
||||
if (wrappingStyle === TextWrappingStyle.BEHIND_TEXT || wrappingStyle === TextWrappingStyle.IN_FRONT_OF_TEXT) {
|
||||
const behindDoc = wrappingStyle === TextWrappingStyle.BEHIND_TEXT ? BooleanNumber.TRUE : BooleanNumber.FALSE;
|
||||
if (oldDrawing.behindDoc !== behindDoc) {
|
||||
rawActions.push(jsonX.replaceOp(['drawings', drawing.drawingId, 'behindDoc'], oldDrawing.behindDoc, behindDoc)!);
|
||||
}
|
||||
}
|
||||
|
||||
if (wrappingStyle !== TextWrappingStyle.INLINE) {
|
||||
for (const key of ['positionH', 'positionV'] as const) {
|
||||
const value = drawing.docTransform?.[key];
|
||||
const oldValue = oldDrawing.docTransform[key];
|
||||
if (value && !Tools.diffValue(oldValue, value)) {
|
||||
rawActions.push(jsonX.replaceOp(['drawings', drawing.drawingId, 'docTransform', key], oldValue, value)!);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mutation: IMutationInfo<IRichTextEditingMutationParams> = {
|
||||
id: RichTextEditingMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
actions: rawActions.reduce(
|
||||
(actions, action) => JSONX.compose(actions, action as JSONXActions),
|
||||
null as JSONXActions
|
||||
),
|
||||
textRanges: null,
|
||||
},
|
||||
};
|
||||
|
||||
return Boolean(commandService.syncExecuteCommand(mutation.id, mutation.params));
|
||||
},
|
||||
};
|
||||
@@ -53,7 +53,8 @@ describe('DocDrawingController', () => {
|
||||
{ registerDrawingData } as any,
|
||||
{ registerDrawingData: registerDrawingDataForManager } as any,
|
||||
resourceManagerService as any,
|
||||
univerInstanceService as any
|
||||
univerInstanceService as any,
|
||||
{ registerCommand: vi.fn(() => ({ dispose: vi.fn() })) } as any
|
||||
);
|
||||
|
||||
expect(resourceManagerService.registerPluginResource).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -17,8 +17,13 @@
|
||||
import type { DocumentDataModel, IDocumentData } from '@univerjs/core';
|
||||
import type { IDrawingMapItem, IDrawingMapItemData } from '@univerjs/drawing';
|
||||
import type { IDocDrawing } from '../services/doc-drawing.service';
|
||||
import { BooleanNumber, Disposable, IResourceManagerService, IUniverInstanceService, PositionedObjectLayoutType, UniverInstanceType } from '@univerjs/core';
|
||||
import { BooleanNumber, Disposable, ICommandService, IResourceManagerService, IUniverInstanceService, PositionedObjectLayoutType, UniverInstanceType } from '@univerjs/core';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
import { InsertDocDrawingCommand } from '../commands/commands/insert-doc-drawing.command';
|
||||
import { RemoveDocDrawingCommand } from '../commands/commands/remove-doc-drawing.command';
|
||||
import { SetDocDrawingArrangeCommand } from '../commands/commands/set-drawing-arrange.command';
|
||||
import { UpdateDrawingDocTransformCommand } from '../commands/commands/update-doc-drawing-transform.command';
|
||||
import { UpdateDocDrawingWrappingStyleCommand } from '../commands/commands/update-doc-drawing-wrapping-style.command';
|
||||
import { IDocDrawingService } from '../services/doc-drawing.service';
|
||||
|
||||
export const DOCS_DRAWING_PLUGIN = 'DOC_DRAWING_PLUGIN';
|
||||
@@ -52,7 +57,8 @@ export class DocDrawingController extends Disposable {
|
||||
@IDocDrawingService private readonly _docDrawingService: IDocDrawingService,
|
||||
@IDrawingManagerService private readonly _drawingManagerService: IDrawingManagerService,
|
||||
@IResourceManagerService private _resourceManagerService: IResourceManagerService,
|
||||
@IUniverInstanceService private _univerInstanceService: IUniverInstanceService
|
||||
@IUniverInstanceService private _univerInstanceService: IUniverInstanceService,
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -61,6 +67,7 @@ export class DocDrawingController extends Disposable {
|
||||
|
||||
private _init(): void {
|
||||
this._initSnapshot();
|
||||
this._initCommands();
|
||||
}
|
||||
|
||||
private _initSnapshot() {
|
||||
@@ -151,4 +158,14 @@ export class DocDrawingController extends Disposable {
|
||||
this._drawingManagerService.registerDrawingData(unitId, renderSubDrawings);
|
||||
return true;
|
||||
}
|
||||
|
||||
private _initCommands() {
|
||||
[
|
||||
InsertDocDrawingCommand,
|
||||
RemoveDocDrawingCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
SetDocDrawingArrangeCommand,
|
||||
].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { DocumentDataModel, IDocumentData } from '@univerjs/core';
|
||||
import { DocumentFlavor, ILogService, IUniverInstanceService, LogLevel, Univer, UniverInstanceType } from '@univerjs/core';
|
||||
import { FUniver } from '@univerjs/core/facade';
|
||||
import { UniverDocsPlugin } from '@univerjs/docs';
|
||||
import { UniverDrawingPlugin } from '@univerjs/drawing';
|
||||
import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
|
||||
import { UniverDocsDrawingPlugin } from '../../plugin';
|
||||
import '../index';
|
||||
|
||||
const DEFAULT_DOC_DATA: IDocumentData = {
|
||||
id: 'test-doc',
|
||||
documentStyle: { documentFlavor: DocumentFlavor.TRADITIONAL },
|
||||
body: {
|
||||
dataStream: 'Hello world\r\n',
|
||||
paragraphs: [{ startIndex: 11, paragraphId: 'paragraph-1' }],
|
||||
sectionBreaks: [],
|
||||
customBlocks: [],
|
||||
},
|
||||
drawings: {},
|
||||
drawingsOrder: [],
|
||||
};
|
||||
|
||||
export function createFacadeTestBed() {
|
||||
const univer = new Univer();
|
||||
const injector = univer.__getInjector();
|
||||
|
||||
injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
|
||||
univer.registerPlugin(UniverDocsPlugin);
|
||||
univer.registerPlugin(UniverDrawingPlugin);
|
||||
univer.registerPlugin(UniverDocsDrawingPlugin);
|
||||
|
||||
const documentDataModel = univer.createUnit<IDocumentData, DocumentDataModel>(
|
||||
UniverInstanceType.UNIVER_DOC,
|
||||
DEFAULT_DOC_DATA
|
||||
);
|
||||
injector.get(IUniverInstanceService).focusUnit(documentDataModel.getUnitId());
|
||||
injector.get(ILogService).setLogLevel(LogLevel.SILENT);
|
||||
|
||||
return {
|
||||
univer,
|
||||
injector,
|
||||
documentDataModel,
|
||||
document: FUniver.newAPI(injector).getActiveDocument()!,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { IObjectPositionH, IObjectPositionV } from '@univerjs/core';
|
||||
import {
|
||||
ArrangeTypeEnum,
|
||||
BooleanNumber,
|
||||
ICommandService,
|
||||
ImageSourceType,
|
||||
ObjectRelativeFromH,
|
||||
ObjectRelativeFromV,
|
||||
PositionedObjectLayoutType,
|
||||
} from '@univerjs/core';
|
||||
import { DocSelectionManagerService } from '@univerjs/docs';
|
||||
import { TextWrappingStyle, UpdateDocDrawingWrappingStyleCommand } from '@univerjs/docs-drawing';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { RemoveDocDrawingCommand } from '../../commands/commands/remove-doc-drawing.command';
|
||||
import { SetDocDrawingArrangeCommand } from '../../commands/commands/set-drawing-arrange.command';
|
||||
import { UpdateDrawingDocTransformCommand } from '../../commands/commands/update-doc-drawing-transform.command';
|
||||
import { createFacadeTestBed } from './create-test-bed';
|
||||
|
||||
class MockImage {
|
||||
width = 800;
|
||||
height = 400;
|
||||
onload: (() => void) | null = null;
|
||||
onerror: (() => void) | null = null;
|
||||
|
||||
get src(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
set src(_value: string) {
|
||||
queueMicrotask(() => this.onload?.());
|
||||
}
|
||||
}
|
||||
|
||||
describe('FDocument image facade', () => {
|
||||
let testBed: ReturnType<typeof createFacadeTestBed>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('Image', MockImage);
|
||||
testBed = createFacadeTestBed();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
testBed.univer.dispose();
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('inserts an image with optional transform and text range options', async () => {
|
||||
const positionH: IObjectPositionH = {
|
||||
relativeFrom: ObjectRelativeFromH.MARGIN,
|
||||
posOffset: 12,
|
||||
};
|
||||
const positionV: IObjectPositionV = {
|
||||
relativeFrom: ObjectRelativeFromV.PARAGRAPH,
|
||||
posOffset: 34,
|
||||
};
|
||||
|
||||
const image = await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
width: 200,
|
||||
angle: 15,
|
||||
positionH,
|
||||
positionV,
|
||||
textRange: {
|
||||
startOffset: 5,
|
||||
endOffset: 5,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(image).not.toBeNull();
|
||||
expect(image?.getSource()).toBe('data:image/png;base64,image');
|
||||
expect(image?.getSourceType()).toBe(ImageSourceType.BASE64);
|
||||
expect(image?.getSize()).toEqual({ width: 200, height: 100 });
|
||||
expect(image?.getAngle()).toBe(15);
|
||||
expect(image?.getPositionH()).toEqual(positionH);
|
||||
expect(image?.getPositionV()).toEqual(positionV);
|
||||
expect(image?.getImageData()).toMatchObject({
|
||||
drawingId: image?.getId(),
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
});
|
||||
expect(testBed.document.save().body?.dataStream).toBe('Hello\b world\r\n');
|
||||
expect(testBed.document.getImage(image!.getId())).not.toBeNull();
|
||||
expect(testBed.document.getImages().map((item) => item.getId())).toEqual([image!.getId()]);
|
||||
});
|
||||
|
||||
it('resolves the insertion range only once', async () => {
|
||||
const selectionManager = testBed.injector.get(DocSelectionManagerService);
|
||||
const getActiveTextRange = vi.spyOn(selectionManager, 'getActiveTextRange');
|
||||
|
||||
await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
textRange: {
|
||||
startOffset: 0,
|
||||
endOffset: 0,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(getActiveTextRange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('uses a capped intrinsic size when width and height are omitted', async () => {
|
||||
const image = await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
textRange: {
|
||||
startOffset: 0,
|
||||
endOffset: 0,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(image?.getSize()).toEqual({ width: 500, height: 250 });
|
||||
});
|
||||
|
||||
it('inserts an image with a non-inline wrapping style', async () => {
|
||||
const image = await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
wrappingStyle: TextWrappingStyle.BEHIND_TEXT,
|
||||
textRange: {
|
||||
startOffset: 2,
|
||||
endOffset: 2,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(image?.getImageData()).toMatchObject({
|
||||
layoutType: PositionedObjectLayoutType.WRAP_NONE,
|
||||
behindDoc: BooleanNumber.TRUE,
|
||||
});
|
||||
});
|
||||
|
||||
it('inserts a header image with multi-page transforms', async () => {
|
||||
const segmentId = testBed.document.ensurePageHeader();
|
||||
const image = await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
width: 160,
|
||||
height: 90,
|
||||
textRange: {
|
||||
startOffset: 0,
|
||||
endOffset: 0,
|
||||
collapsed: true,
|
||||
segmentId,
|
||||
},
|
||||
});
|
||||
const imageData = image?.getImageData();
|
||||
|
||||
expect(testBed.document.save().headers?.[segmentId].body.dataStream).toBe('\b\r\n');
|
||||
expect(imageData?.isMultiTransform).toBe(BooleanNumber.TRUE);
|
||||
expect(imageData?.transforms).toEqual(imageData?.transform ? [imageData.transform] : null);
|
||||
});
|
||||
|
||||
it('routes image updates, arranging, and removal through docs-drawing commands', async () => {
|
||||
const image = await testBed.document.insertImage({
|
||||
source: 'data:image/png;base64,image',
|
||||
imageSourceType: ImageSourceType.BASE64,
|
||||
width: 160,
|
||||
height: 90,
|
||||
textRange: {
|
||||
startOffset: 3,
|
||||
endOffset: 3,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
});
|
||||
const commandService = testBed.injector.get(ICommandService);
|
||||
const execute = vi.spyOn(commandService, 'syncExecuteCommand').mockReturnValue(true);
|
||||
const positionH: IObjectPositionH = { relativeFrom: ObjectRelativeFromH.PAGE, posOffset: 20 };
|
||||
const positionV: IObjectPositionV = { relativeFrom: ObjectRelativeFromV.PAGE, posOffset: 30 };
|
||||
const drawingId = image!.getId();
|
||||
|
||||
expect(image!.setSize(320, 180)).toBe(true);
|
||||
expect(image!.setRotate(25)).toBe(true);
|
||||
expect(image!.setPositionH(positionH)).toBe(true);
|
||||
expect(image!.setPositionV(positionV)).toBe(true);
|
||||
expect(image!.setWrappingStyle(TextWrappingStyle.WRAP_SQUARE)).toBe(true);
|
||||
expect(image!.setForward()).toBe(true);
|
||||
expect(image!.setBackward()).toBe(true);
|
||||
expect(image!.setBack()).toBe(true);
|
||||
expect(image!.setFront()).toBe(true);
|
||||
expect(image!.remove()).toBe(true);
|
||||
|
||||
expect(execute.mock.calls).toEqual([
|
||||
[UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [{ drawingId, key: 'size', value: { width: 320, height: 180 } }],
|
||||
}],
|
||||
[UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [{ drawingId, key: 'angle', value: 25 }],
|
||||
}],
|
||||
[UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [{ drawingId, key: 'positionH', value: positionH }],
|
||||
}],
|
||||
[UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [{ drawingId, key: 'positionV', value: positionV }],
|
||||
}],
|
||||
[UpdateDocDrawingWrappingStyleCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawings: [image!.getImageData()],
|
||||
wrappingStyle: TextWrappingStyle.WRAP_SQUARE,
|
||||
}],
|
||||
[SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: [drawingId],
|
||||
arrangeType: ArrangeTypeEnum.forward,
|
||||
}],
|
||||
[SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: [drawingId],
|
||||
arrangeType: ArrangeTypeEnum.backward,
|
||||
}],
|
||||
[SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: [drawingId],
|
||||
arrangeType: ArrangeTypeEnum.back,
|
||||
}],
|
||||
[SetDocDrawingArrangeCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingIds: [drawingId],
|
||||
arrangeType: ArrangeTypeEnum.front,
|
||||
}],
|
||||
[RemoveDocDrawingCommand.id, {
|
||||
unitId: 'test-doc',
|
||||
drawings: [{
|
||||
unitId: 'test-doc',
|
||||
subUnitId: 'test-doc',
|
||||
drawingId,
|
||||
drawingType: 0,
|
||||
}],
|
||||
textRange: {
|
||||
startOffset: 3,
|
||||
endOffset: 3,
|
||||
collapsed: true,
|
||||
segmentId: '',
|
||||
},
|
||||
}],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,468 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { ImageSourceType, Injector, IObjectPositionH, IObjectPositionV, ISize, ITextRangeParam } from '@univerjs/core';
|
||||
import type { IDocImage, IUpdateDocDrawingWrappingStyleParams, TextWrappingStyle } from '@univerjs/docs-drawing';
|
||||
import type { FDocument } from '@univerjs/docs/facade';
|
||||
import {
|
||||
ArrangeTypeEnum,
|
||||
DrawingTypeEnum,
|
||||
ICommandService,
|
||||
} from '@univerjs/core';
|
||||
import {
|
||||
RemoveDocDrawingCommand,
|
||||
SetDocDrawingArrangeCommand,
|
||||
UpdateDocDrawingWrappingStyleCommand,
|
||||
UpdateDrawingDocTransformCommand,
|
||||
} from '@univerjs/docs-drawing';
|
||||
|
||||
/**
|
||||
* Facade API for an image in a document.
|
||||
* @hideconstructor
|
||||
*/
|
||||
export class FDocumentImage {
|
||||
constructor(
|
||||
private readonly _document: FDocument,
|
||||
private readonly _imageId: string,
|
||||
private readonly _injector: Injector
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Gets the id of the document containing the image.
|
||||
* @returns {string} The document unit id.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getUnitId());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getUnitId(): string {
|
||||
return this._document.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the drawing id of the image.
|
||||
* @returns {string} The drawing id.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getId());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getId(): string {
|
||||
return this._imageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image source supplied at insertion time.
|
||||
* @returns {string | undefined} The image source, or `undefined` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getSource());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getSource(): string | undefined {
|
||||
return this.getImageData()?.source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image source type supplied at insertion time.
|
||||
* @returns {ImageSourceType | undefined} The image source type, or `undefined` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getSourceType());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getSourceType(): ImageSourceType | undefined {
|
||||
return this.getImageData()?.imageSourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current image size.
|
||||
* @returns {ISize | null} The width and height in pixels, or `null` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getSize());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getSize(): ISize | null {
|
||||
return this.getImageData()?.docTransform.size ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current clockwise rotation angle.
|
||||
* @returns {number | undefined} The rotation angle in degrees, or `undefined` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getAngle());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getAngle(): number | undefined {
|
||||
return this.getImageData()?.docTransform.angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current horizontal position.
|
||||
* @returns {IObjectPositionH | null} The horizontal position, or `null` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getPositionH());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getPositionH(): IObjectPositionH | null {
|
||||
return this.getImageData()?.docTransform.positionH ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current vertical position.
|
||||
* @returns {IObjectPositionV | null} The vertical position, or `null` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getPositionV());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getPositionV(): IObjectPositionV | null {
|
||||
return this.getImageData()?.docTransform.positionV ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current raw document image data.
|
||||
* @returns {IDocImage | null} The image data, or `null` when the image no longer exists.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* console.log(image.getImageData());
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getImageData(): IDocImage | null {
|
||||
const drawing = this._document.getDocumentDataModel().getDrawings()?.[this._imageId];
|
||||
if (!drawing || drawing.drawingType !== DrawingTypeEnum.DRAWING_IMAGE) {
|
||||
return null;
|
||||
}
|
||||
return drawing as IDocImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the image size.
|
||||
* @param {number} width The width in pixels.
|
||||
* @param {number} height The height in pixels.
|
||||
* @returns {boolean} `true` when the update command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setSize(400, 300);
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setSize(width: number, height: number): boolean {
|
||||
return this._updateTransform('size', { width, height });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the clockwise rotation angle.
|
||||
* @param {number} angle The rotation angle in degrees.
|
||||
* @returns {boolean} `true` when the update command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setRotate(45);
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setRotate(angle: number): boolean {
|
||||
return this._updateTransform('angle', angle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the horizontal position of the image.
|
||||
* Inline images are positioned by their document placeholder, so this has a visible effect only when the image
|
||||
* wrapping style is not `TextWrappingStyle.INLINE`.
|
||||
* @param {IObjectPositionH} positionH The horizontal position relative to the document.
|
||||
* @returns {boolean} `true` when the update command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setPositionH({
|
||||
* relativeFrom: univerAPI.Enum.DocsImageRelativeFromH.MARGIN,
|
||||
* posOffset: 100
|
||||
* });
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setPositionH(positionH: IObjectPositionH): boolean {
|
||||
return this._updateTransform('positionH', positionH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the vertical position of the image.
|
||||
* Inline images are positioned by their document placeholder, so this has a visible effect only when the image
|
||||
* wrapping style is not `TextWrappingStyle.INLINE`.
|
||||
* @param {IObjectPositionV} positionV The vertical position relative to the document.
|
||||
* @returns {boolean} `true` when the update command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setPositionV({
|
||||
* relativeFrom: univerAPI.Enum.DocsImageRelativeFromV.MARGIN,
|
||||
* posOffset: 100
|
||||
* });
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setPositionV(positionV: IObjectPositionV): boolean {
|
||||
return this._updateTransform('positionV', positionV);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the image wrapping style.
|
||||
* When switching from inline to a floating style in a UI environment, the current visual position is preserved.
|
||||
* @param {TextWrappingStyle} wrappingStyle The wrapping style to apply.
|
||||
* @returns {boolean} `true` when the update command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setWrappingStyle(univerAPI.Enum.DocsImageWrappingStyle.WRAP_SQUARE);
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setWrappingStyle(wrappingStyle: TextWrappingStyle): boolean {
|
||||
const image = this.getImageData();
|
||||
if (!image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._injector.get(ICommandService).syncExecuteCommand<IUpdateDocDrawingWrappingStyleParams>(
|
||||
UpdateDocDrawingWrappingStyleCommand.id,
|
||||
{
|
||||
unitId: this.getUnitId(),
|
||||
subUnitId: this.getUnitId(),
|
||||
drawings: [image],
|
||||
wrappingStyle,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the image forward by one level in the drawing order.
|
||||
* @returns {boolean} `true` when the arrange command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setForward();
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setForward(): boolean {
|
||||
return this._arrange(ArrangeTypeEnum.forward);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the image backward by one level in the drawing order.
|
||||
* @returns {boolean} `true` when the arrange command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setBackward();
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setBackward(): boolean {
|
||||
return this._arrange(ArrangeTypeEnum.backward);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the image to the back of the drawing order.
|
||||
* @returns {boolean} `true` when the arrange command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setBack();
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setBack(): boolean {
|
||||
return this._arrange(ArrangeTypeEnum.back);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the image to the front of the drawing order.
|
||||
* @returns {boolean} `true` when the arrange command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setFront();
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
setFront(): boolean {
|
||||
return this._arrange(ArrangeTypeEnum.front);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the image and its document placeholder.
|
||||
* @returns {boolean} `true` when the remove command succeeds; otherwise, `false`.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.remove();
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
remove(): boolean {
|
||||
const image = this.getImageData();
|
||||
const textRange = this._getTextRange();
|
||||
if (!image || !textRange) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._injector.get(ICommandService).syncExecuteCommand(RemoveDocDrawingCommand.id, {
|
||||
unitId: this.getUnitId(),
|
||||
drawings: [{
|
||||
unitId: this.getUnitId(),
|
||||
subUnitId: this.getUnitId(),
|
||||
drawingId: this._imageId,
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
}],
|
||||
textRange,
|
||||
});
|
||||
}
|
||||
|
||||
private _updateTransform(
|
||||
key: 'size' | 'angle' | 'positionH' | 'positionV',
|
||||
value: ISize | number | IObjectPositionH | IObjectPositionV
|
||||
): boolean {
|
||||
return this._injector.get(ICommandService).syncExecuteCommand(UpdateDrawingDocTransformCommand.id, {
|
||||
unitId: this.getUnitId(),
|
||||
subUnitId: this.getUnitId(),
|
||||
drawings: [{ drawingId: this._imageId, key, value }],
|
||||
});
|
||||
}
|
||||
|
||||
private _arrange(arrangeType: ArrangeTypeEnum): boolean {
|
||||
return this._injector.get(ICommandService).syncExecuteCommand(SetDocDrawingArrangeCommand.id, {
|
||||
unitId: this.getUnitId(),
|
||||
subUnitId: this.getUnitId(),
|
||||
drawingIds: [this._imageId],
|
||||
arrangeType,
|
||||
});
|
||||
}
|
||||
|
||||
private _getTextRange(): ITextRangeParam | null {
|
||||
const snapshot = this._document.getDocumentDataModel().getSnapshot();
|
||||
const { body, headers = {}, footers = {} } = snapshot;
|
||||
const segments = [
|
||||
{ segmentId: '', body },
|
||||
...Object.entries(headers).map(([segmentId, header]) => ({ segmentId, body: header.body })),
|
||||
...Object.entries(footers).map(([segmentId, footer]) => ({ segmentId, body: footer.body })),
|
||||
];
|
||||
|
||||
for (const { segmentId, body } of segments) {
|
||||
const customBlock = body?.customBlocks?.find((block) => block.blockId === this._imageId);
|
||||
if (customBlock) {
|
||||
return {
|
||||
startOffset: customBlock.startIndex,
|
||||
endOffset: customBlock.startIndex,
|
||||
collapsed: true,
|
||||
segmentId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { IObjectPositionH, IObjectPositionV, ISize, ITextRangeParam } from '@univerjs/core';
|
||||
import type { IDocImage } from '@univerjs/docs-drawing';
|
||||
import {
|
||||
BooleanNumber,
|
||||
DrawingTypeEnum,
|
||||
generateRandomId,
|
||||
ICommandService,
|
||||
IImageIoService,
|
||||
ImageSourceType,
|
||||
IURLImageService,
|
||||
WrapTextType,
|
||||
} from '@univerjs/core';
|
||||
import { buildDocTransform, docDrawingPositionToTransform } from '@univerjs/docs';
|
||||
import {
|
||||
InsertDocDrawingCommand,
|
||||
TextWrappingStyle,
|
||||
WRAPPING_STYLE_TO_LAYOUT_TYPE,
|
||||
} from '@univerjs/docs-drawing';
|
||||
import { FDocument } from '@univerjs/docs/facade';
|
||||
import { DRAWING_IMAGE_HEIGHT_LIMIT, DRAWING_IMAGE_WIDTH_LIMIT, getImageSize } from '@univerjs/drawing';
|
||||
import { FDocumentImage } from './f-document-image';
|
||||
|
||||
/** Options for inserting an image into a document. */
|
||||
export interface IFDocumentInsertImageOptions {
|
||||
/** The image source. It cannot be changed after insertion. */
|
||||
source: string;
|
||||
/** The image source type. It cannot be changed after insertion. */
|
||||
imageSourceType: ImageSourceType;
|
||||
/** The width in pixels. When only width is provided, height is calculated from the intrinsic aspect ratio. */
|
||||
width?: number;
|
||||
/** The height in pixels. When only height is provided, width is calculated from the intrinsic aspect ratio. */
|
||||
height?: number;
|
||||
/** The clockwise rotation angle in degrees. Defaults to `0`. */
|
||||
angle?: number;
|
||||
/**
|
||||
* The horizontal position relative to the document. Defaults to the page with an offset of `0`.
|
||||
* It has a visible positioning effect only when `wrappingStyle` is not `INLINE`.
|
||||
*/
|
||||
positionH?: IObjectPositionH;
|
||||
/**
|
||||
* The vertical position relative to the document. Defaults to the paragraph with an offset of `0`.
|
||||
* It has a visible positioning effect only when `wrappingStyle` is not `INLINE`.
|
||||
*/
|
||||
positionV?: IObjectPositionV;
|
||||
/** The image wrapping style. Defaults to `TextWrappingStyle.INLINE`. */
|
||||
wrappingStyle?: TextWrappingStyle;
|
||||
/** The document range at which to insert the image. The current selection is used when omitted. */
|
||||
textRange?: ITextRangeParam;
|
||||
}
|
||||
|
||||
/** Image facade methods mixed into `FDocument`. */
|
||||
export interface IFDocumentImageMixin {
|
||||
/**
|
||||
* Inserts an image into the document.
|
||||
*
|
||||
* When width and height are both omitted, the intrinsic size is proportionally limited to 500 by 500 pixels.
|
||||
*
|
||||
* @param {IFDocumentInsertImageOptions} options The image source, optional transform, and insertion range.
|
||||
* @returns {Promise<FDocumentImage | null>} The inserted image facade, or `null` when the insertion command fails.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = await fDocument.insertImage({
|
||||
* source: 'https://avatars.githubusercontent.com/u/61444807?s=48&v=4',
|
||||
* imageSourceType: univerAPI.Enum.ImageSourceType.URL,
|
||||
* width: 320,
|
||||
* textRange: {
|
||||
* startOffset: 30,
|
||||
* },
|
||||
* });
|
||||
* console.log(image);
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = await fDocument.insertImage({
|
||||
* source: 'https://avatars.githubusercontent.com/u/61444807?s=48&v=4',
|
||||
* imageSourceType: univerAPI.Enum.ImageSourceType.URL,
|
||||
* width: 320,
|
||||
* wrappingStyle: univerAPI.Enum.DocsImageWrappingStyle.WRAP_SQUARE,
|
||||
* textRange: {
|
||||
* startOffset: 30,
|
||||
* },
|
||||
* });
|
||||
* console.log(image);
|
||||
* ```
|
||||
*/
|
||||
insertImage(options: IFDocumentInsertImageOptions): Promise<FDocumentImage | null>;
|
||||
|
||||
/**
|
||||
* Gets an image by its drawing id.
|
||||
* @param {string} imageId The drawing id of the image.
|
||||
* @returns {FDocumentImage | null} The image facade, or `null` when the image does not exist.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImage('image-1');
|
||||
* console.log(image);
|
||||
* ```
|
||||
*/
|
||||
getImage(imageId: string): FDocumentImage | null;
|
||||
|
||||
/**
|
||||
* Gets all images in the document in drawing order.
|
||||
* @returns {FDocumentImage[]} The image facades in drawing order.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const images = fDocument.getImages();
|
||||
* console.log(images);
|
||||
* ```
|
||||
*/
|
||||
getImages(): FDocumentImage[];
|
||||
}
|
||||
|
||||
export class FDocumentImageMixin extends FDocument implements IFDocumentImageMixin {
|
||||
override async insertImage(options: IFDocumentInsertImageOptions): Promise<FDocumentImage | null> {
|
||||
const unitId = this.getId();
|
||||
const imageId = generateRandomId(6);
|
||||
const intrinsicSize = await this._getIntrinsicSize(options.source, options.imageSourceType);
|
||||
const size = resolveImageSize(intrinsicSize, options);
|
||||
const defaultTransform = buildDocTransform(size.width, size.height);
|
||||
const wrappingStyle = options.wrappingStyle ?? TextWrappingStyle.INLINE;
|
||||
const docTransform = {
|
||||
...defaultTransform,
|
||||
angle: options.angle ?? defaultTransform.angle,
|
||||
positionH: options.positionH ?? defaultTransform.positionH,
|
||||
positionV: options.positionV ?? defaultTransform.positionV,
|
||||
};
|
||||
const transform = docDrawingPositionToTransform(docTransform);
|
||||
const drawing: IDocImage = {
|
||||
unitId,
|
||||
subUnitId: unitId,
|
||||
drawingId: imageId,
|
||||
drawingType: DrawingTypeEnum.DRAWING_IMAGE,
|
||||
imageSourceType: options.imageSourceType,
|
||||
source: options.source,
|
||||
transform,
|
||||
docTransform,
|
||||
behindDoc: wrappingStyle === TextWrappingStyle.BEHIND_TEXT ? BooleanNumber.TRUE : BooleanNumber.FALSE,
|
||||
title: '',
|
||||
description: '',
|
||||
layoutType: WRAPPING_STYLE_TO_LAYOUT_TYPE[wrappingStyle],
|
||||
wrapText: WrapTextType.BOTH_SIDES,
|
||||
distB: 0,
|
||||
distL: 0,
|
||||
distR: 0,
|
||||
distT: 0,
|
||||
};
|
||||
|
||||
const inserted = this._injector.get(ICommandService).syncExecuteCommand(InsertDocDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: [drawing],
|
||||
textRange: options.textRange,
|
||||
});
|
||||
|
||||
if (!inserted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._injector.createInstance(FDocumentImage, this, imageId, this._injector);
|
||||
}
|
||||
|
||||
override getImage(imageId: string): FDocumentImage | null {
|
||||
const drawing = this.getDocumentDataModel().getDrawings()?.[imageId];
|
||||
if (!drawing || drawing.drawingType !== DrawingTypeEnum.DRAWING_IMAGE) {
|
||||
return null;
|
||||
}
|
||||
return this._injector.createInstance(FDocumentImage, this, imageId, this._injector);
|
||||
}
|
||||
|
||||
override getImages(): FDocumentImage[] {
|
||||
const documentDataModel = this.getDocumentDataModel();
|
||||
const drawings = documentDataModel.getDrawings() ?? {};
|
||||
const drawingIds = documentDataModel.getDrawingsOrder() ?? Object.keys(drawings);
|
||||
return drawingIds
|
||||
.filter((drawingId) => drawings[drawingId]?.drawingType === DrawingTypeEnum.DRAWING_IMAGE)
|
||||
.map((drawingId) => this._injector.createInstance(FDocumentImage, this, drawingId, this._injector));
|
||||
}
|
||||
|
||||
private async _getIntrinsicSize(source: string, imageSourceType: ImageSourceType): Promise<Required<ISize>> {
|
||||
const imageIoService = this._injector.has(IImageIoService) ? this._injector.get(IImageIoService) : null;
|
||||
let resolvedSource = source;
|
||||
|
||||
if (imageSourceType === ImageSourceType.UUID && imageIoService) {
|
||||
resolvedSource = await imageIoService.getImage(source);
|
||||
} else if (imageSourceType === ImageSourceType.URL && this._injector.has(IURLImageService)) {
|
||||
try {
|
||||
resolvedSource = await this._injector.get(IURLImageService).getImage(source);
|
||||
} catch {
|
||||
resolvedSource = source;
|
||||
}
|
||||
}
|
||||
|
||||
const { width, height, image } = await getImageSize(resolvedSource);
|
||||
imageIoService?.addImageSourceCache(source, imageSourceType, image);
|
||||
return { width, height };
|
||||
}
|
||||
}
|
||||
|
||||
function resolveImageSize(
|
||||
intrinsicSize: Required<ISize>,
|
||||
options: Pick<IFDocumentInsertImageOptions, 'width' | 'height'>
|
||||
): Required<ISize> {
|
||||
const { width: intrinsicWidth, height: intrinsicHeight } = intrinsicSize;
|
||||
if (options.width != null && options.height != null) {
|
||||
return { width: options.width, height: options.height };
|
||||
}
|
||||
|
||||
if (options.width != null) {
|
||||
return { width: options.width, height: intrinsicHeight * options.width / intrinsicWidth };
|
||||
}
|
||||
|
||||
if (options.height != null) {
|
||||
return { width: intrinsicWidth * options.height / intrinsicHeight, height: options.height };
|
||||
}
|
||||
|
||||
const scale = Math.min(
|
||||
1,
|
||||
DRAWING_IMAGE_WIDTH_LIMIT / intrinsicWidth,
|
||||
DRAWING_IMAGE_HEIGHT_LIMIT / intrinsicHeight
|
||||
);
|
||||
return { width: intrinsicWidth * scale, height: intrinsicHeight * scale };
|
||||
}
|
||||
|
||||
FDocument.extend(FDocumentImageMixin);
|
||||
declare module '@univerjs/docs/facade' {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
interface FDocument extends IFDocumentImageMixin {}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { ObjectRelativeFromH, ObjectRelativeFromV } from '@univerjs/core';
|
||||
import { FEnum } from '@univerjs/core/facade';
|
||||
import { TextWrappingStyle } from '@univerjs/docs-drawing';
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export interface IFDocumentImageEnumMixin {
|
||||
/**
|
||||
* Represents the wrapping styles used by docs image operations.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = await fDocument.insertImage({
|
||||
* source: 'https://avatars.githubusercontent.com/u/61444807?s=48&v=4',
|
||||
* imageSourceType: univerAPI.Enum.ImageSourceType.URL,
|
||||
* width: 320,
|
||||
* wrappingStyle: univerAPI.Enum.DocsImageWrappingStyle.WRAP_SQUARE,
|
||||
* textRange: {
|
||||
* startOffset: 30,
|
||||
* },
|
||||
* });
|
||||
* console.log(image);
|
||||
* ```
|
||||
*/
|
||||
DocsImageWrappingStyle: typeof TextWrappingStyle;
|
||||
|
||||
/**
|
||||
* Represents the horizontal position types used by docs image operations.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setPositionH({
|
||||
* relativeFrom: univerAPI.Enum.DocsImageRelativeFromH.MARGIN,
|
||||
* posOffset: 100
|
||||
* });
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
DocsImageRelativeFromH: typeof ObjectRelativeFromH;
|
||||
|
||||
/**
|
||||
* Represents the vertical position types used by docs image operations.
|
||||
* @example
|
||||
* ```ts
|
||||
* const fDocument = univerAPI.getActiveDocument();
|
||||
* const image = fDocument.getImages()[0];
|
||||
*
|
||||
* if (image) {
|
||||
* const success = image.setPositionV({
|
||||
* relativeFrom: univerAPI.Enum.DocsImageRelativeFromV.MARGIN,
|
||||
* posOffset: 100
|
||||
* });
|
||||
* console.log(success);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
DocsImageRelativeFromV: typeof ObjectRelativeFromV;
|
||||
}
|
||||
|
||||
export class FDocumentImageEnumMixin extends FEnum implements IFDocumentImageEnumMixin {
|
||||
override get DocsImageWrappingStyle(): typeof TextWrappingStyle {
|
||||
return TextWrappingStyle;
|
||||
}
|
||||
|
||||
override get DocsImageRelativeFromH(): typeof ObjectRelativeFromH {
|
||||
return ObjectRelativeFromH;
|
||||
}
|
||||
|
||||
override get DocsImageRelativeFromV(): typeof ObjectRelativeFromV {
|
||||
return ObjectRelativeFromV;
|
||||
}
|
||||
}
|
||||
|
||||
FEnum.extend(FDocumentImageEnumMixin);
|
||||
declare module '@univerjs/core/facade' {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
interface FEnum extends IFDocumentImageEnumMixin {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 './f-document';
|
||||
import './f-enum';
|
||||
|
||||
export type { IFDocumentInsertImageOptions } from './f-document';
|
||||
export { FDocumentImage } from './f-document-image';
|
||||
@@ -14,6 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { InsertDocDrawingCommand } from './commands/commands/insert-doc-drawing.command';
|
||||
export type { IInsertDocDrawingCommandParams } from './commands/commands/insert-doc-drawing.command';
|
||||
export { RemoveDocDrawingCommand } from './commands/commands/remove-doc-drawing.command';
|
||||
export type { IRemoveDocDrawingCommandParam, IRemoveDocDrawingCommandParams } from './commands/commands/remove-doc-drawing.command';
|
||||
export { SetDocDrawingArrangeCommand } from './commands/commands/set-drawing-arrange.command';
|
||||
export type { ISetDocDrawingArrangeCommandParams } from './commands/commands/set-drawing-arrange.command';
|
||||
export { UpdateDrawingDocTransformCommand } from './commands/commands/update-doc-drawing-transform.command';
|
||||
export type { IDrawingDocTransform, IUpdateDrawingDocTransformCommandParams } from './commands/commands/update-doc-drawing-transform.command';
|
||||
export { TextWrappingStyle, UpdateDocDrawingWrappingStyleCommand, WRAPPING_STYLE_TO_LAYOUT_TYPE } from './commands/commands/update-doc-drawing-wrapping-style.command';
|
||||
export type { IUpdateDocDrawingWrappingStyleParams } from './commands/commands/update-doc-drawing-wrapping-style.command';
|
||||
export type { IUniverDocsDrawingConfig } from './config/config';
|
||||
export { DOCS_DRAWING_PLUGIN, getDocDrawingRenderOrder } from './controllers/doc-drawing.controller';
|
||||
export type { IDocDrawingModel } from './controllers/doc-drawing.controller';
|
||||
|
||||
@@ -20,7 +20,6 @@ import type {
|
||||
ICommand,
|
||||
ICustomTable,
|
||||
IDisposable,
|
||||
IDocumentBody,
|
||||
IDocumentData,
|
||||
IDrawingParam,
|
||||
IMutationInfo,
|
||||
@@ -30,10 +29,12 @@ import type {
|
||||
} from '@univerjs/core';
|
||||
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
|
||||
import type { DocumentViewModel, IRectRangeWithStyle, ITextRangeWithStyle } from '@univerjs/engine-render';
|
||||
import type { IDocClipboardPasteCustomBlockMapping } from '../../services/clipboard/doc-paste-mutation-adapter.service';
|
||||
import {
|
||||
BuildTextUtils,
|
||||
CommandType,
|
||||
generateRandomId,
|
||||
getCustomBlockIdsInSelections,
|
||||
getRichTextEditPath,
|
||||
ICommandService,
|
||||
IUndoRedoService,
|
||||
@@ -48,34 +49,10 @@ import {
|
||||
} from '@univerjs/core';
|
||||
import { DocSelectionManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { getCustomDecorationAtPosition, getCustomRangeAtPosition } from '../../basics/paragraph';
|
||||
import type { IDocClipboardPasteCustomBlockMapping } from '../../services/clipboard/doc-paste-mutation-adapter.service';
|
||||
import { IDocClipboardPasteAdapterService } from '../../services/clipboard/doc-paste-mutation-adapter.service';
|
||||
import { getCommandSkeleton } from '../util';
|
||||
import { getDeleteRowContentActionParams, getDeleteRowsActionsParams, getDeleteTableActionParams } from './table/table';
|
||||
|
||||
export function getCustomBlockIdsInSelections(body: IDocumentBody, selections: ITextRange[]): string[] {
|
||||
const customBlockIds: string[] = [];
|
||||
const { customBlocks = [] } = body;
|
||||
|
||||
for (const selection of selections) {
|
||||
const { startOffset, endOffset } = selection;
|
||||
|
||||
if (startOffset == null || endOffset == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const customBlock of customBlocks) {
|
||||
const { startIndex } = customBlock;
|
||||
|
||||
if (startIndex >= startOffset && startIndex < endOffset) {
|
||||
customBlockIds.push(customBlock.blockId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return customBlockIds;
|
||||
}
|
||||
|
||||
function hasRangeInTable(ranges: ITextRangeWithStyle[]): boolean {
|
||||
return ranges.some((range) => {
|
||||
const { startNodePosition } = range;
|
||||
|
||||
@@ -33,7 +33,7 @@ export { DocCopyCommand, DocCutCommand, DocPasteCommand } from './commands/comma
|
||||
export { CutContentCommand, InnerPasteCommand } from './commands/commands/clipboard.inner.command';
|
||||
export type { IInnerPasteCommandParams } from './commands/commands/clipboard.inner.command';
|
||||
export type { IInnerCutCommandParams } from './commands/commands/clipboard.inner.command';
|
||||
export { getCustomBlockIdsInSelections, getCutActionsFromDocRanges } from './commands/commands/clipboard.inner.command';
|
||||
export { getCutActionsFromDocRanges } from './commands/commands/clipboard.inner.command';
|
||||
export { buildMoveDocBlockActions, MoveDocBlockCommand } from './commands/commands/doc-block-move.command';
|
||||
export type { IMoveDocBlockCommandParams } from './commands/commands/doc-block-move.command';
|
||||
export {
|
||||
|
||||
@@ -89,4 +89,4 @@ export { replaceSelectionFactory } from './utils/replace-selection-factory';
|
||||
export { createSectionColumnProperties } from './utils/section-columns';
|
||||
export { getTopLevelSectionBreaks } from './utils/sections';
|
||||
export { buildDocTransform, docDrawingPositionToTransform, transformToDocDrawingPosition } from './utils/transform-position';
|
||||
export { consumeContentInsertRange, isHeaderFooterSelection } from './utils/util';
|
||||
export { consumeContentInsertRange, getContentInsertRange, isHeaderFooterSelection, normalizeTextRange } from './utils/util';
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IAccessor } from '@univerjs/core';
|
||||
import type { DocumentDataModel, IAccessor, ITextRangeParam } from '@univerjs/core';
|
||||
import type { ITextRangeWithStyle } from '@univerjs/engine-render';
|
||||
import type { IDocContentInsertRange } from '../services/doc-content-insert.service';
|
||||
import { IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { DocContentInsertService } from '../services/doc-content-insert.service';
|
||||
|
||||
export function consumeContentInsertRange(accessor: IAccessor, unitId: string) {
|
||||
export function consumeContentInsertRange(accessor: IAccessor, unitId: string): IDocContentInsertRange | null {
|
||||
try {
|
||||
return accessor.get(DocContentInsertService).consumeInsertRange(unitId);
|
||||
} catch {
|
||||
@@ -26,6 +28,36 @@ export function consumeContentInsertRange(accessor: IAccessor, unitId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getContentInsertRange(accessor: IAccessor, unitId?: string): (IDocContentInsertRange & {
|
||||
collapsed: boolean;
|
||||
}) | null {
|
||||
const _unitId = unitId ?? accessor.get(IUniverInstanceService).getCurrentUnitOfType<DocumentDataModel>(UniverInstanceType.UNIVER_DOC)?.getUnitId();
|
||||
if (!_unitId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const insertRange = consumeContentInsertRange(accessor, _unitId);
|
||||
if (!insertRange) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...insertRange,
|
||||
collapsed: insertRange?.startOffset === insertRange?.endOffset,
|
||||
};
|
||||
}
|
||||
|
||||
export function isHeaderFooterSelection(range?: ITextRangeWithStyle): boolean {
|
||||
return Boolean(range?.segmentId);
|
||||
}
|
||||
|
||||
export function normalizeTextRange(textRange: ITextRangeParam): ITextRangeParam {
|
||||
const endOffset = textRange.endOffset ?? textRange.startOffset;
|
||||
|
||||
return {
|
||||
...textRange,
|
||||
endOffset,
|
||||
collapsed: textRange.collapsed ?? textRange.startOffset === endOffset,
|
||||
segmentId: textRange.segmentId ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { IAccessor, ICommand } from '@univerjs/core';
|
||||
import type { IDeleteDrawingCommandParams, ISheetDrawing } from '@univerjs/sheets-drawing';
|
||||
import type { IRemoveSheetDrawingCommandParams, ISheetDrawing } from '@univerjs/sheets-drawing';
|
||||
import { CommandType, ICommandService } from '@univerjs/core';
|
||||
import { ISheetDrawingService, RemoveSheetDrawingCommand } from '@univerjs/sheets-drawing';
|
||||
|
||||
@@ -44,7 +44,7 @@ export const DeleteDrawingsCommand: ICommand = {
|
||||
drawingType,
|
||||
};
|
||||
});
|
||||
return commandService.executeCommand<IDeleteDrawingCommandParams>(RemoveSheetDrawingCommand.id, {
|
||||
return commandService.executeCommand<IRemoveSheetDrawingCommandParams>(RemoveSheetDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: newDrawings,
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import type { IMutationInfo, IRange, Nullable } from '@univerjs/core';
|
||||
import type { IDrawingJsonUndo1 } from '@univerjs/drawing';
|
||||
import type { IDiscreteRange } from '@univerjs/sheets';
|
||||
import type { IDeleteDrawingCommandParams, ISheetDrawing, ISheetImage } from '@univerjs/sheets-drawing';
|
||||
import type { IRemoveSheetDrawingCommandParams, ISheetDrawing, ISheetImage } from '@univerjs/sheets-drawing';
|
||||
import type { IPasteHookValueType, ISheetDiscreteRangeLocation } from '@univerjs/sheets-ui';
|
||||
import { Disposable, DrawingTypeEnum, generateRandomId, ICommandService, ImageSourceType, Inject } from '@univerjs/core';
|
||||
import { IDrawingManagerService } from '@univerjs/drawing';
|
||||
@@ -129,12 +129,12 @@ export class SheetsDrawingCopyPasteController extends Disposable {
|
||||
}
|
||||
|
||||
if (copyType === COPY_TYPE.CUT) {
|
||||
const params: IDeleteDrawingCommandParams = {
|
||||
const params: IRemoveSheetDrawingCommandParams = {
|
||||
unitId,
|
||||
drawings: [drawing],
|
||||
};
|
||||
// Delete the drawing when it is cut
|
||||
this._commandService.executeCommand(RemoveSheetDrawingCommand.id, params);
|
||||
this._commandService.executeCommand<IRemoveSheetDrawingCommandParams>(RemoveSheetDrawingCommand.id, params);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
import type { Workbook, Worksheet } from '@univerjs/core';
|
||||
import type {
|
||||
IDeleteDrawingCommandParams,
|
||||
IInsertDrawingCommandParams,
|
||||
IInsertSheetDrawingCommandParams,
|
||||
IRemoveSheetDrawingCommandParams,
|
||||
ISetDrawingArrangeCommandParams,
|
||||
ISetDrawingCommandParams,
|
||||
} from '@univerjs/sheets-drawing';
|
||||
@@ -438,7 +438,7 @@ export class SheetDrawingPermissionController extends Disposable {
|
||||
let subUnitId: string | undefined;
|
||||
|
||||
if (command.id === InsertSheetDrawingCommand.id || command.id === RemoveSheetDrawingCommand.id || command.id === SetSheetDrawingCommand.id) {
|
||||
const params = command.params as IInsertDrawingCommandParams | IDeleteDrawingCommandParams | ISetDrawingCommandParams;
|
||||
const params = command.params as IInsertSheetDrawingCommandParams | IRemoveSheetDrawingCommandParams | ISetDrawingCommandParams;
|
||||
const { drawings } = params;
|
||||
unitId = drawings?.[0]?.unitId;
|
||||
subUnitId = drawings?.[0]?.subUnitId;
|
||||
|
||||
@@ -19,7 +19,7 @@ import type { IImageData } from '@univerjs/drawing';
|
||||
import type { IRenderContext, IRenderModule, SpreadsheetSkeleton } from '@univerjs/engine-render';
|
||||
import type { ISheetLocationBase, WorkbookSelectionModel } from '@univerjs/sheets';
|
||||
import type {
|
||||
IInsertDrawingCommandParams,
|
||||
IInsertSheetDrawingCommandParams,
|
||||
ISetDrawingArrangeCommandParams,
|
||||
ISetDrawingCommandParams,
|
||||
ISheetDrawing,
|
||||
@@ -269,10 +269,10 @@ export class SheetDrawingUpdateController extends Disposable implements IRenderM
|
||||
axisAlignSheetTransform: transformToAxisAlignPosition(newTransform, skeleton) ?? sheetTransform,
|
||||
};
|
||||
|
||||
return this._commandService.executeCommand(InsertSheetDrawingCommand.id, {
|
||||
return this._commandService.executeCommand<IInsertSheetDrawingCommandParams>(InsertSheetDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: [sheetDrawingParam],
|
||||
} as IInsertDrawingCommandParams);
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { IDisposable, IDrawingSearch, Injector } from '@univerjs/core';
|
||||
import type { IDeleteDrawingCommandParams, IInsertDrawingCommandParams, ISetDrawingCommandParams, ISheetFloatDom } from '@univerjs/sheets-drawing';
|
||||
import type { IInsertSheetDrawingCommandParams, IRemoveSheetDrawingCommandParams, ISetDrawingCommandParams, ISheetFloatDom } from '@univerjs/sheets-drawing';
|
||||
import type { IBeforeFloatDomAddEventParams, IBeforeFloatDomDeleteEventParams, IBeforeFloatDomUpdateEventParams, IFloatDomAddedEventParams, IFloatDomDeletedEventParams, IFloatDomUpdatedEventParams } from './f-event';
|
||||
import { CanceledError, DrawingTypeEnum, ICommandService, IURLImageService } from '@univerjs/core';
|
||||
import { FUniver } from '@univerjs/core/facade';
|
||||
@@ -62,7 +62,7 @@ export class FUniverSheetsDrawingUIMixin extends FUniver implements IFUniverShee
|
||||
() => commandService.beforeCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== InsertSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IInsertDrawingCommandParams;
|
||||
const params = commandInfo.params as IInsertSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
@@ -97,7 +97,7 @@ export class FUniverSheetsDrawingUIMixin extends FUniver implements IFUniverShee
|
||||
() => commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== InsertSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IInsertDrawingCommandParams;
|
||||
const params = commandInfo.params as IInsertSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
@@ -208,7 +208,7 @@ export class FUniverSheetsDrawingUIMixin extends FUniver implements IFUniverShee
|
||||
() => commandService.beforeCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== RemoveSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IDeleteDrawingCommandParams;
|
||||
const params = commandInfo.params as IRemoveSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
@@ -247,7 +247,7 @@ export class FUniverSheetsDrawingUIMixin extends FUniver implements IFUniverShee
|
||||
() => commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== RemoveSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IDeleteDrawingCommandParams;
|
||||
const params = commandInfo.params as IRemoveSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
|
||||
@@ -42,7 +42,7 @@ import type {
|
||||
} from '@univerjs/sheets';
|
||||
import type {
|
||||
IFloatDomData,
|
||||
IInsertDrawingCommandParams,
|
||||
IInsertSheetDrawingCommandParams,
|
||||
ISetDrawingCommandParams,
|
||||
ISheetDrawing,
|
||||
ISheetDrawingPosition,
|
||||
@@ -1809,10 +1809,10 @@ export class SheetCanvasFloatDomManagerService extends Disposable {
|
||||
|
||||
// mutation
|
||||
// ---> this._drawingManagerService.add$.subscribe
|
||||
this._commandService.executeCommand(InsertSheetDrawingCommand.id, {
|
||||
this._commandService.executeCommand<IInsertSheetDrawingCommandParams>(InsertSheetDrawingCommand.id, {
|
||||
unitId,
|
||||
drawings: [sheetDrawingParam],
|
||||
} as IInsertDrawingCommandParams);
|
||||
});
|
||||
|
||||
this._add$.next({ unitId, subUnitId, id });
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import { ISheetDrawingService } from '../../services/sheet-drawing.service';
|
||||
import { DrawingApplyType, SetDrawingApplyMutation } from '../mutations/set-drawing-apply.mutation';
|
||||
import { ClearSheetDrawingTransformerOperation } from '../operations/clear-drawing-transformer.operation';
|
||||
|
||||
export interface IInsertDrawingCommandParams {
|
||||
export interface IInsertSheetDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: ISheetDrawing[];
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export interface IInsertDrawingCommandParams {
|
||||
export const InsertSheetDrawingCommand: ICommand = {
|
||||
id: 'sheet.command.insert-sheet-image',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor: IAccessor, params?: IInsertDrawingCommandParams) => {
|
||||
handler: (accessor: IAccessor, params?: IInsertSheetDrawingCommandParams) => {
|
||||
if (!params) return false;
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
@@ -27,22 +27,22 @@ import { ISheetDrawingService } from '../../services/sheet-drawing.service';
|
||||
import { DrawingApplyType, SetDrawingApplyMutation } from '../mutations/set-drawing-apply.mutation';
|
||||
import { ClearSheetDrawingTransformerOperation } from '../operations/clear-drawing-transformer.operation';
|
||||
|
||||
export interface IDeleteDrawingCommandParam {
|
||||
export interface IRemoveSheetDrawingCommandParam {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
drawingId: string;
|
||||
drawingType: DrawingTypeEnum;
|
||||
}
|
||||
|
||||
export interface IDeleteDrawingCommandParams {
|
||||
export interface IRemoveSheetDrawingCommandParams {
|
||||
unitId: string;
|
||||
drawings: IDeleteDrawingCommandParam[];
|
||||
drawings: IRemoveSheetDrawingCommandParam[];
|
||||
}
|
||||
|
||||
export const RemoveSheetDrawingCommand: ICommand = {
|
||||
id: 'sheet.command.remove-sheet-image',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor: IAccessor, params?: IDeleteDrawingCommandParams) => {
|
||||
handler: (accessor: IAccessor, params?: IRemoveSheetDrawingCommandParams) => {
|
||||
if (!params) return false;
|
||||
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { IDrawingSearch, Injector } from '@univerjs/core';
|
||||
import type { IDeleteDrawingCommandParams, IInsertDrawingCommandParams, ISetDrawingCommandParams, ISheetImage } from '@univerjs/sheets-drawing';
|
||||
import type { IInsertSheetDrawingCommandParams, IRemoveSheetDrawingCommandParams, ISetDrawingCommandParams, ISheetImage } from '@univerjs/sheets-drawing';
|
||||
import type {
|
||||
IBeforeOverGridImageChangeEventParams,
|
||||
IBeforeOverGridImageChangeParamObject,
|
||||
@@ -50,7 +50,7 @@ export class FUniverSheetsDrawingMixin extends FUniver {
|
||||
() => commandService.beforeCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== InsertSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IInsertDrawingCommandParams;
|
||||
const params = commandInfo.params as IInsertSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
@@ -77,7 +77,7 @@ export class FUniverSheetsDrawingMixin extends FUniver {
|
||||
() => commandService.beforeCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== RemoveSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IDeleteDrawingCommandParams;
|
||||
const params = commandInfo.params as IRemoveSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
@@ -186,7 +186,7 @@ export class FUniverSheetsDrawingMixin extends FUniver {
|
||||
() => commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== InsertSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IInsertDrawingCommandParams;
|
||||
const params = commandInfo.params as IInsertSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
@@ -208,7 +208,7 @@ export class FUniverSheetsDrawingMixin extends FUniver {
|
||||
() => commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id !== RemoveSheetDrawingCommand.id) return;
|
||||
|
||||
const params = commandInfo.params as IDeleteDrawingCommandParams;
|
||||
const params = commandInfo.params as IRemoveSheetDrawingCommandParams;
|
||||
const workbook = this.getActiveWorkbook();
|
||||
if (workbook == null || params == null) {
|
||||
return;
|
||||
|
||||
@@ -20,12 +20,9 @@ export {
|
||||
transformToDrawingPosition,
|
||||
} from './basics/transform-position';
|
||||
export { InsertSheetDrawingCommand } from './commands/commands/insert-sheet-drawing.command';
|
||||
export type { IInsertDrawingCommandParams } from './commands/commands/insert-sheet-drawing.command';
|
||||
export type { IInsertSheetDrawingCommandParams } from './commands/commands/insert-sheet-drawing.command';
|
||||
export { RemoveSheetDrawingCommand } from './commands/commands/remove-sheet-drawing.command';
|
||||
export type {
|
||||
IDeleteDrawingCommandParam,
|
||||
IDeleteDrawingCommandParams,
|
||||
} from './commands/commands/remove-sheet-drawing.command';
|
||||
export type { IRemoveSheetDrawingCommandParam, IRemoveSheetDrawingCommandParams } from './commands/commands/remove-sheet-drawing.command';
|
||||
export { SetDrawingArrangeCommand } from './commands/commands/set-drawing-arrange.command';
|
||||
export type { ISetDrawingArrangeCommandParams } from './commands/commands/set-drawing-arrange.command';
|
||||
export { SetSheetDrawingCommand } from './commands/commands/set-sheet-drawing.command';
|
||||
|
||||
Generated
+6
@@ -952,6 +952,9 @@ importers:
|
||||
'@univerjs/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
'@univerjs/docs':
|
||||
specifier: workspace:*
|
||||
version: link:../docs
|
||||
'@univerjs/drawing':
|
||||
specifier: workspace:*
|
||||
version: link:../drawing
|
||||
@@ -959,6 +962,9 @@ importers:
|
||||
'@univerjs-infra/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../common/shared
|
||||
'@univerjs/engine-render':
|
||||
specifier: workspace:*
|
||||
version: link:../engine-render
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
|
||||
@@ -21,6 +21,10 @@ import { UniverDocsDrawingUIPlugin } from '@univerjs/docs-drawing-ui';
|
||||
import { UniverDrawingPlugin } from '@univerjs/drawing';
|
||||
import { UniverDrawingUIPlugin } from '@univerjs/drawing-ui';
|
||||
|
||||
import '@univerjs/docs-drawing/facade';
|
||||
|
||||
export type * from '@univerjs/docs-drawing/facade';
|
||||
|
||||
export interface IUniverDocsDrawingPresetConfig {
|
||||
collaboration?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user