-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port Image Edit Operations #2670
Changes from 7 commits
52f30e4
5bb6b15
36f592c
ec80d60
4aa4828
ec97315
531985b
b91e403
7a06999
091f2db
c11771f
13dcca1
816b769
cc260bc
b7a0ca8
a1586a5
89d51b1
63125da
21599f9
cca526e
7b5cd18
75bea9e
554e3da
207657d
3806813
3221722
8a50224
abe2b18
5c15d3e
f91b48b
f45bf71
69b4204
ee28ea4
d5e6775
4766498
3642b10
7ea11d5
da849be
98d3520
cb87afd
814b54f
dd732bb
d95360c
71f830b
6251d8d
ac8790f
6d86454
2ec3974
5e1be98
85198f2
8b5961c
357c7c7
53b044b
4c5963a
f86541c
3c465c6
27792a0
9a9abae
baff339
4c8d7eb
26d6e90
9dd412a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,10 +13,10 @@ import { updateImageEditInfo } from './utils/updateImageEditInfo'; | |
import { updateRotateHandle } from './Rotator/updateRotateHandle'; | ||
import { updateWrapper } from './utils/updateWrapper'; | ||
import { | ||
ensureImageHasSpanParent, | ||
getSelectedSegmentsAndParagraphs, | ||
isElementOfType, | ||
isNodeOfType, | ||
mutateSegment, | ||
unwrap, | ||
} from 'roosterjs-content-model-dom'; | ||
import type { DragAndDropHelper } from '../pluginUtils/DragAndDrop/DragAndDropHelper'; | ||
|
@@ -39,7 +39,7 @@ const DefaultOptions: Partial<ImageEditOptions> = { | |
preserveRatio: true, | ||
disableRotate: false, | ||
disableSideResize: false, | ||
onSelectState: 'resizeAndRotate', | ||
onSelectState: 'resize', | ||
}; | ||
|
||
const IMAGE_EDIT_CHANGE_SOURCE = 'ImageEdit'; | ||
|
@@ -127,7 +127,6 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { | |
apiOperation?: ImageEditOperation | ||
) { | ||
const contentModelImage = getContentModelImage(editor); | ||
ensureImageHasSpanParent(image); | ||
const imageSpan = image.parentElement; | ||
if ( | ||
!contentModelImage || | ||
|
@@ -293,13 +292,15 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { | |
} | ||
|
||
public canRegenerateImage(image: HTMLImageElement): boolean { | ||
return canRegenerateImage(image) || canRegenerateImage(this.selectedImage); | ||
return canRegenerateImage(image); | ||
} | ||
|
||
public cropImage(image: HTMLImageElement) { | ||
if (!this.editor) { | ||
public cropImage() { | ||
const selection = this.editor?.getDOMSelection(); | ||
if (!this.editor || !selection || selection.type !== 'image') { | ||
return; | ||
} | ||
let image = selection.image; | ||
if (this.wrapper && this.selectedImage && this.shadowSpan) { | ||
image = this.removeImageWrapper() ?? image; | ||
} | ||
|
@@ -411,35 +412,39 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { | |
this.shadowSpan | ||
) { | ||
editor.formatContentModel( | ||
(model, _) => { | ||
(model, context) => { | ||
const selectedSegmentsAndParagraphs = getSelectedSegmentsAndParagraphs( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So here we edit image based on current selection, then why we need to pass in image object in other editing functions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We pass the image element to add and remove the wrapper element. Here we edit the selected content model image |
||
model, | ||
false | ||
); | ||
if (!selectedSegmentsAndParagraphs[0]) { | ||
return false; | ||
} | ||
const segment = selectedSegmentsAndParagraphs[0][0]; | ||
|
||
if ( | ||
this.lastSrc && | ||
this.selectedImage && | ||
this.imageEditInfo && | ||
this.clonedImage && | ||
segment.segmentType == 'Image' | ||
) { | ||
applyChange( | ||
editor, | ||
this.selectedImage, | ||
segment, | ||
this.imageEditInfo, | ||
this.lastSrc, | ||
this.wasImageResized || this.isCropMode, | ||
this.clonedImage | ||
); | ||
segment.isSelected = shouldSelectImage; | ||
segment.isSelectedAsImageSelection = shouldSelectAsImageSelection; | ||
|
||
const segment = selectedSegmentsAndParagraphs[0][0]; | ||
const paragraph = selectedSegmentsAndParagraphs[0][1]; | ||
|
||
if (paragraph && segment.segmentType == 'Image') { | ||
mutateSegment(paragraph, segment, image => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is nice. And I think we only need to return true for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually you can move this down into the "if" block below. |
||
if ( | ||
this.lastSrc && | ||
this.selectedImage && | ||
this.imageEditInfo && | ||
this.clonedImage | ||
) { | ||
applyChange( | ||
editor, | ||
this.selectedImage, | ||
image, | ||
this.imageEditInfo, | ||
this.lastSrc, | ||
this.wasImageResized || this.isCropMode, | ||
this.clonedImage | ||
); | ||
image.isSelected = shouldSelectImage; | ||
image.isSelectedAsImageSelection = shouldSelectAsImageSelection; | ||
} | ||
}); | ||
return true; | ||
} | ||
|
||
|
@@ -473,7 +478,12 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { | |
return image; | ||
} | ||
|
||
public flipImage(image: HTMLImageElement, direction: 'horizontal' | 'vertical') { | ||
public flipImage(direction: 'horizontal' | 'vertical') { | ||
const selection = this.editor?.getDOMSelection(); | ||
if (!this.editor || !selection || selection.type !== 'image') { | ||
return; | ||
} | ||
const image = selection.image; | ||
if (this.editor) { | ||
this.editImage(this.editor, image, 'flip', imageEditInfo => { | ||
const angleRad = imageEditInfo.angleRad || 0; | ||
|
@@ -497,7 +507,12 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { | |
} | ||
} | ||
|
||
public rotateImage(image: HTMLImageElement, angleRad: number) { | ||
public rotateImage(angleRad: number) { | ||
const selection = this.editor?.getDOMSelection(); | ||
if (!this.editor || !selection || selection.type !== 'image') { | ||
return; | ||
} | ||
const image = selection.image; | ||
if (this.editor) { | ||
this.editImage(this.editor, image, 'rotate', imageEditInfo => { | ||
imageEditInfo.angleRad = (imageEditInfo.angleRad || 0) + angleRad; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,10 +5,10 @@ import type { ContentModelImage, IEditor } from 'roosterjs-content-model-types'; | |
* @internal | ||
*/ | ||
export function getContentModelImage(editor: IEditor): ContentModelImage | null { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is this used? This will give you a cloned object but not the original image object. Is that ok? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used to get the image dataset |
||
const model = editor.getContentModelCopy('disconnected' /*mode*/); | ||
const model = editor.getContentModelCopy('disconnected'); | ||
const selectedSegments = getSelectedSegments(model, false /*includeFormatHolder*/); | ||
if (selectedSegments.length == 1 && selectedSegments[0].segmentType == 'Image') { | ||
return selectedSegments[0]; | ||
return selectedSegments[0] as ContentModelImage; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine here, but if we do this kind of type cast in other place, it will be dangerous. Actually, if you only want dataset, just get it directly from the connect model, and return the dataset instead. Disconnected model actually need a clone, which is not as good perf as connected one. function getSelectedImageDataset() {
let result;
editor.formatContentModel(model => {
const image = getSelectedImage(model);
result = getImageMetadata(image);
return false;
});
return result; Something like this. |
||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool!