-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Vhuseinova/watermark plugin update (#2507)
* Add update function for Watermark plugin * Code formatting update
- Loading branch information
1 parent
4c342fc
commit c073faf
Showing
1 changed file
with
160 additions
and
138 deletions.
There are no files selected for viewing
298 changes: 160 additions & 138 deletions
298
packages/roosterjs-editor-plugins/lib/plugins/Watermark/Watermark.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,138 +1,160 @@ | ||
import { applyFormat, getEntitySelector, getTagOfNode } from 'roosterjs-editor-dom'; | ||
import { ContentPosition, EntityOperation, PluginEventType } from 'roosterjs-editor-types'; | ||
import { insertEntity } from 'roosterjs-editor-api'; | ||
import type { | ||
DefaultFormat, | ||
EditorPlugin, | ||
Entity, | ||
IEditor, | ||
PluginEvent, | ||
} from 'roosterjs-editor-types'; | ||
|
||
const ENTITY_TYPE = 'WATERMARK_WRAPPER'; | ||
|
||
/** | ||
* A watermark plugin to manage watermark string for roosterjs | ||
*/ | ||
export default class Watermark implements EditorPlugin { | ||
private editor: IEditor | null = null; | ||
private disposer: (() => void) | null = null; | ||
private format: DefaultFormat; | ||
|
||
/** | ||
* Create an instance of Watermark plugin | ||
* @param watermark The watermark string | ||
*/ | ||
constructor(private watermark: string, format?: DefaultFormat, private customClass?: string) { | ||
this.format = format || { | ||
fontSize: '14px', | ||
textColors: { | ||
lightModeColor: '#AAAAAA', | ||
darkModeColor: '#6B6B6B', | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Get a friendly name of this plugin | ||
*/ | ||
getName() { | ||
return 'Watermark'; | ||
} | ||
|
||
/** | ||
* Initialize this plugin. This should only be called from Editor | ||
* @param editor Editor instance | ||
*/ | ||
initialize(editor: IEditor) { | ||
this.editor = editor; | ||
this.disposer = this.editor.addDomEventHandler({ | ||
focus: this.showHideWatermark, | ||
blur: this.showHideWatermark, | ||
}); | ||
} | ||
|
||
/** | ||
* Dispose this plugin | ||
*/ | ||
dispose() { | ||
this.disposer?.(); | ||
this.disposer = null; | ||
this.editor = null; | ||
} | ||
|
||
/** | ||
* Handle events triggered from editor | ||
* @param event PluginEvent object | ||
*/ | ||
onPluginEvent(event: PluginEvent) { | ||
if ( | ||
event.eventType == PluginEventType.EditorReady || | ||
(event.eventType == PluginEventType.ContentChanged && | ||
(<Entity>event.data)?.type != ENTITY_TYPE) | ||
) { | ||
this.showHideWatermark(); | ||
} else if ( | ||
event.eventType == PluginEventType.EntityOperation && | ||
event.entity.type == ENTITY_TYPE && | ||
this.editor | ||
) { | ||
const { | ||
operation, | ||
entity: { wrapper }, | ||
} = event; | ||
if (operation == EntityOperation.ReplaceTemporaryContent) { | ||
this.removeWatermark(wrapper); | ||
} else if (event.operation == EntityOperation.NewEntity) { | ||
applyFormat( | ||
wrapper, | ||
this.format, | ||
this.editor.isDarkMode(), | ||
this.editor.getDarkColorHandler() | ||
); | ||
wrapper.spellcheck = false; | ||
} | ||
} | ||
} | ||
|
||
private showHideWatermark = () => { | ||
if (!this.editor) { | ||
return; | ||
} | ||
const hasFocus = this.editor.hasFocus(); | ||
const watermarks = this.editor.queryElements(getEntitySelector(ENTITY_TYPE)); | ||
const isShowing = watermarks.length > 0; | ||
|
||
if (hasFocus && isShowing) { | ||
watermarks.forEach(this.removeWatermark); | ||
this.editor.focus(); | ||
} else if (!hasFocus && !isShowing && this.editor.isEmpty()) { | ||
const newEntity = insertEntity( | ||
this.editor, | ||
ENTITY_TYPE, | ||
this.editor.getDocument().createTextNode(this.watermark), | ||
false /*isBlock*/, | ||
false /*isReadonly*/, | ||
ContentPosition.Begin | ||
); | ||
if (this.customClass) { | ||
newEntity.wrapper.classList.add(this.customClass); | ||
} | ||
} | ||
}; | ||
|
||
private removeWatermark = (wrapper: HTMLElement) => { | ||
const parentNode = wrapper.parentNode; | ||
parentNode?.removeChild(wrapper); | ||
|
||
// After remove watermark node, if it leaves an empty DIV, append a BR node into it to make it a regular empty line | ||
if ( | ||
parentNode && | ||
this.editor?.contains(parentNode) && | ||
getTagOfNode(parentNode) == 'DIV' && | ||
!parentNode.firstChild | ||
) { | ||
parentNode.appendChild(this.editor.getDocument().createElement('BR')); | ||
} | ||
}; | ||
} | ||
import { applyFormat, getEntitySelector, getTagOfNode } from 'roosterjs-editor-dom'; | ||
import { ContentPosition, EntityOperation, PluginEventType } from 'roosterjs-editor-types'; | ||
import { insertEntity } from 'roosterjs-editor-api'; | ||
import type { | ||
DefaultFormat, | ||
EditorPlugin, | ||
Entity, | ||
IEditor, | ||
PluginEvent, | ||
} from 'roosterjs-editor-types'; | ||
|
||
const ENTITY_TYPE = 'WATERMARK_WRAPPER'; | ||
|
||
/** | ||
* A watermark plugin to manage watermark string for roosterjs | ||
*/ | ||
export default class Watermark implements EditorPlugin { | ||
private editor: IEditor | null = null; | ||
private disposer: (() => void) | null = null; | ||
private format: DefaultFormat; | ||
|
||
/** | ||
* Create an instance of Watermark plugin | ||
* @param watermark The watermark string | ||
*/ | ||
constructor(private watermark: string, format?: DefaultFormat, private customClass?: string) { | ||
this.format = format || { | ||
fontSize: '14px', | ||
textColors: { | ||
lightModeColor: '#AAAAAA', | ||
darkModeColor: '#6B6B6B', | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Updates the watermark text. | ||
* @param watermark - The new watermark text. | ||
*/ | ||
updateWatermark(watermark: string) { | ||
this.watermark = watermark; | ||
|
||
if (!this.editor) { | ||
return; | ||
} | ||
const watermarks = this.editor.queryElements(getEntitySelector(ENTITY_TYPE)); | ||
const isShowing = watermarks.length > 0; | ||
// re-render watermark only if it's already displayed | ||
if (isShowing) { | ||
// hide watermark | ||
const watermarks = this.editor.queryElements(getEntitySelector(ENTITY_TYPE)); | ||
watermarks.forEach(this.removeWatermark); | ||
// show watermark | ||
this.showHideWatermark(); | ||
} | ||
} | ||
|
||
/** | ||
* Get a friendly name of this plugin | ||
*/ | ||
getName() { | ||
return 'Watermark'; | ||
} | ||
|
||
/** | ||
* Initialize this plugin. This should only be called from Editor | ||
* @param editor Editor instance | ||
*/ | ||
initialize(editor: IEditor) { | ||
this.editor = editor; | ||
this.disposer = this.editor.addDomEventHandler({ | ||
focus: this.showHideWatermark, | ||
blur: this.showHideWatermark, | ||
}); | ||
} | ||
|
||
/** | ||
* Dispose this plugin | ||
*/ | ||
dispose() { | ||
this.disposer?.(); | ||
this.disposer = null; | ||
this.editor = null; | ||
} | ||
|
||
/** | ||
* Handle events triggered from editor | ||
* @param event PluginEvent object | ||
*/ | ||
onPluginEvent(event: PluginEvent) { | ||
if ( | ||
event.eventType == PluginEventType.EditorReady || | ||
(event.eventType == PluginEventType.ContentChanged && | ||
(<Entity>event.data)?.type != ENTITY_TYPE) | ||
) { | ||
this.showHideWatermark(); | ||
} else if ( | ||
event.eventType == PluginEventType.EntityOperation && | ||
event.entity.type == ENTITY_TYPE && | ||
this.editor | ||
) { | ||
const { | ||
operation, | ||
entity: { wrapper }, | ||
} = event; | ||
if (operation == EntityOperation.ReplaceTemporaryContent) { | ||
this.removeWatermark(wrapper); | ||
} else if (event.operation == EntityOperation.NewEntity) { | ||
applyFormat( | ||
wrapper, | ||
this.format, | ||
this.editor.isDarkMode(), | ||
this.editor.getDarkColorHandler() | ||
); | ||
wrapper.spellcheck = false; | ||
} | ||
} | ||
} | ||
|
||
private showHideWatermark = () => { | ||
if (!this.editor) { | ||
return; | ||
} | ||
const hasFocus = this.editor.hasFocus(); | ||
const watermarks = this.editor.queryElements(getEntitySelector(ENTITY_TYPE)); | ||
const isShowing = watermarks.length > 0; | ||
|
||
if (hasFocus && isShowing) { | ||
watermarks.forEach(this.removeWatermark); | ||
this.editor.focus(); | ||
} else if (!hasFocus && !isShowing && this.editor.isEmpty()) { | ||
const newEntity = insertEntity( | ||
this.editor, | ||
ENTITY_TYPE, | ||
this.editor.getDocument().createTextNode(this.watermark), | ||
false /*isBlock*/, | ||
false /*isReadonly*/, | ||
ContentPosition.Begin | ||
); | ||
if (this.customClass) { | ||
newEntity.wrapper.classList.add(this.customClass); | ||
} | ||
} | ||
}; | ||
|
||
private removeWatermark = (wrapper: HTMLElement) => { | ||
const parentNode = wrapper.parentNode; | ||
parentNode?.removeChild(wrapper); | ||
|
||
// After remove watermark node, if it leaves an empty DIV, append a BR node into it to make it a regular empty line | ||
if ( | ||
parentNode && | ||
this.editor?.contains(parentNode) && | ||
getTagOfNode(parentNode) == 'DIV' && | ||
!parentNode.firstChild | ||
) { | ||
parentNode.appendChild(this.editor.getDocument().createElement('BR')); | ||
} | ||
}; | ||
} |