Skip to content
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

Modify Image wrapper to insert image wrapper outside the editor #1377

Merged
merged 11 commits into from
Nov 7, 2022

Conversation

juliaroldi
Copy link
Contributor

@juliaroldi juliaroldi commented Oct 31, 2022

After this change, when in editing the image will be cloned and it's clone will be added to a wrapper outside the editor.
Every change made to cloned image will be reflected to image when the quite editing, the editing process will happen outside the editor and DOM structure of the editor will not change while editing.

imageWrapper

@juliaroldi juliaroldi changed the title WIP Modify Image wrapper to insert image wrapper outside the editor Nov 1, 2022
@juliaroldi juliaroldi marked this pull request as ready for review November 1, 2022 17:49
@juliaroldi juliaroldi requested review from JiuqingSong, BryanValverdeU, Andres-CT98 and ianeli1 and removed request for JiuqingSong November 1, 2022 17:49
const clone = this.image.cloneNode();
const wrappedImage = wrap(clone, KnownCreateElementDataIndex.ImageEditWrapper);

this.clonedImage = wrappedImage.firstElementChild as HTMLImageElement;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use "clone" instead of "wrappedImage.firstElementChild as HTMLImageElement" here?


this.clonedImage = wrappedImage.firstElementChild as HTMLImageElement;

// Wrap the cloned image with an entity so that we can easily retrieve it later
const { wrapper } = insertEntity(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the editing wrapper is not part of editor now, I don't think we still need to "insertEntity"

@@ -352,9 +373,11 @@ export default class ImageEdit implements EditorPlugin {
this.lastSrc = this.image.getAttribute('src');

// Set image src to original src to help show editing UI, also it will be used when regenerate image dataURL after editing
this.image.style.visibility = 'hidden';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this will still modify the editor content.

Can we add this visibility rule to image selection's global CSS so that editor content is not impacted?

@@ -352,9 +373,11 @@ export default class ImageEdit implements EditorPlugin {
this.lastSrc = this.image.getAttribute('src');

// Set image src to original src to help show editing UI, also it will be used when regenerate image dataURL after editing
this.image.style.visibility = 'hidden';
this.image.src = this.editInfo.src;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need to change original image's src?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because when we applyFormat we compare the image edited with the previous src, so to make sure apply format works as expected we also need to change this src.


return entity?.type == IMAGE_EDIT_WRAPPER_ENTITY_TYPE ? entity.wrapper : null;
private getImageWrapper(): HTMLElement {
return document.getElementById(this.imageId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the cloned image will have the same id with the original one, how can we guarantee this will return the cloned one?

And if there are 2 editors in the same document, it is possible two editors both have image with the same id. Better change the image id to combine editor id and image id to make it unique

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And instead of querying by image id, since there will be only one image editing per editor, you can just hold the reference to the wrapper element in this plugin. So it will be easier to get it

@JiuqingSong
Copy link
Collaborator

Generally, the behavior is good. Only one issue, if editor is zoomed, the editing image is not zoomed together, so when select and edit the image, its size changes.

@@ -195,6 +190,7 @@ export default class ImageEdit implements EditorPlugin {
e.selectionRangeEx.image,
ImageEditOperation.ResizeAndRotate
);
this.addVisibilityCssTag();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this call need to be inside setEditingImage() function. Because setEditingImage() is a public function and other code can also call it. When it is called we should show/hide the original image

Comment on lines 325 to 328
this.wrapper = this.editor.getDocument().createElement('span');
this.wrapper.appendChild(wrappedImage);
this.wrapper.style.maxWidth = '100%';
this.wrapper.style.position = 'fixed';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be added into KnownCreateElementDataIndex.ImageEditWrapper so that one single wrap() call should make all of them working?


// Cache current src so that we can compare it after edit see if src is changed
this.lastSrc = this.image.getAttribute('src');

// Set image src to original src to help show editing UI, also it will be used when regenerate image dataURL after editing
this.image.style.visibility = 'hidden';
this.image.className = STYLE_IMAGE;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the image should already have id. If not, you add some API in editor to ensure it. So no need to change its className


private addVisibilityCssTag() {
const doc = this.editor.getDocument();
let styleTag = doc.getElementById(STYLE_IMAGE) as HTMLStyleElement;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why reuse the same string for style tag and image class?

Also, if there are multiple editor in the same page, there will be duplicated id for style tags then hard to distinguish.

We already have code in editor core to generate STYLE tag and add style rule. Consider reuse that code by adding some editor API or parameter to existing API. The reuse part can be in separate PR, but at least make sure the style element id is unique. You can use editor DIV id.

const doc = this.editor.getDocument();
let styleTag = doc.getElementById(STYLE_IMAGE) as HTMLStyleElement;
if (!styleTag) {
const cssRule = `.${STYLE_IMAGE} {visibility: hidden}`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use editor id and image id, just like how we do selection border for image

if (!styleTag) {
const cssRule = `.${STYLE_IMAGE} {visibility: hidden}`;
styleTag = doc.createElement('style');
styleTag.id = STYLE_IMAGE;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think you can hold a reference to this style element so no id is needed at all.

@@ -174,6 +168,7 @@ export default class ImageEdit implements EditorPlugin {
* Dispose this plugin
*/
dispose() {
this.removeVisibilityCssTag();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to remove the editing image DOM element here, and the call to removeVisibilityCssTag() can be part of it.

};

/**
* Update image edit elements to reflect current editing result
* @param context
*/
private updateWrapper = (context?: DragAndDropContext) => {
const wrapper = this.getImageWrapper();
if (wrapper) {
if (this.wrapper) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider do

const wrapper = this.wrapper;

if (wrapper) { 
...

So no need to do use "this.wrapper" everywhere to save some bytes

@@ -49,7 +53,7 @@ export const selectImage: SelectImage = (core: EditorCore, image: HTMLImageEleme
const select = (core: EditorCore, image: HTMLImageElement) => {
removeImportantStyleRule(image, ['border', 'margin']);
const borderCSS = buildBorderCSS(core, image.id);
addSelectionStyle(core, borderCSS, STYLE_ID);
setGlobalCssStyles(core.contentDiv.ownerDocument, borderCSS, STYLE_ID + core.contentDiv.id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use removeGlobalCssStyle() in line 67.
Same for selectTable()

Comment on lines 320 to 321
const wrappedImage = wrap(this.clonedImage, KnownCreateElementDataIndex.ImageEditWrapper);
this.wrapper = wrap(wrappedImage, KnownCreateElementDataIndex.ImageEditContainer);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I mean merge these two into one. Because wrappedImage is only used here. So we can do
this.wrapper = wrap(this.clonedImage, KnownCreateElementDataIndex.ImageEditWrapper);

Then you can modify the definition of ImageEditWrapper to make it has two layers of wrapper.

}

wrapper.style.display = Browser.isSafari ? 'inline-block' : 'inline-flex';
this.wrapper.style.display = Browser.isSafari ? 'inline-block' : 'inline-flex';

// Cache current src so that we can compare it after edit see if src is changed
this.lastSrc = this.image.getAttribute('src');

// Set image src to original src to help show editing UI, also it will be used when regenerate image dataURL after editing
this.image.src = this.editInfo.src;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have concern here. If you need to compare, can you compare with the cloned image?

My primary goal here is not to do any change to editor content when edit.

@juliaroldi juliaroldi force-pushed the u/juliaroldi/image-wrapper branch from 7a7562b to 98f41cc Compare November 7, 2022 21:03
export default function removeGlobalCssStyle(doc: Document, styleId: string) {
const styleTag = doc.getElementById(styleId) as HTMLStyleElement;
if (styleTag) {
doc.head.removeChild(styleTag);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use styleTag.parentNode?.removeChild(styleTag) instead

Although accoding to our code, style tag should always be under HEAD. But if we changed that behavior, there will be no build time error or test case to remind us change here. So better make it more robust

@juliaroldi juliaroldi merged commit 3630fb1 into master Nov 7, 2022
@JiuqingSong JiuqingSong deleted the u/juliaroldi/image-wrapper branch September 8, 2023 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants