Skip to content

Commit 0a63be1

Browse files
committed
refactor(Tinymce): Extend HTMLElement directly
Dropping the AlchemyHTMLElement base class avoids reading innerHTML at construction time and rewriting it during render, which violated the Web Components spec and contributed to the SortableJS drag-clone crash. The spinner is now appended via insertAdjacentHTML with an idempotent guard, so re-inserting the element does not stack multiple spinners. All setup (classname, min-height, editor hiding, observer, theme listener) is performed in connectedCallback.
1 parent efbc812 commit 0a63be1

3 files changed

Lines changed: 18 additions & 24 deletions

File tree

app/assets/builds/alchemy/alchemy_admin.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/assets/builds/alchemy/alchemy_admin.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/javascript/alchemy_admin/components/tinymce.js

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
import "tinymce"
2-
import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
32
import { currentLocale } from "alchemy_admin/i18n"
43

54
const DARK_THEME = "alchemy-dark"
65
const LIGHT_THEME = "alchemy"
76

8-
class Tinymce extends AlchemyHTMLElement {
7+
class Tinymce extends HTMLElement {
98
#min_height = null
109

1110
/**
1211
* the observer will initialize Tinymce if the textarea becomes visible
1312
*/
14-
connected() {
13+
connectedCallback() {
1514
this.className = "tinymce_container"
1615

16+
// Append the spinner if not already present (idempotent on reconnect/clone)
17+
if (!this.querySelector(":scope > alchemy-spinner")) {
18+
this.insertAdjacentHTML(
19+
"beforeend",
20+
`<alchemy-spinner size="small"></alchemy-spinner>`
21+
)
22+
}
23+
24+
// hide the textarea until TinyMCE is ready to show the editor
25+
this.style.minHeight = `${this.minHeight}px`
26+
this.editor.style.display = "none"
27+
1728
const observerCallback = (entries, observer) => {
1829
entries.forEach((entry) => {
1930
if (entry.intersectionRatio > 0) {
@@ -43,32 +54,15 @@ class Tinymce extends AlchemyHTMLElement {
4354
/**
4455
* disconnect intersection observer and remove Tinymce editor if the web components get destroyed
4556
*/
46-
disconnected() {
47-
if (this.tinymceIntersectionObserver !== null) {
48-
this.tinymceIntersectionObserver.disconnect()
49-
}
57+
disconnectedCallback() {
58+
this.tinymceIntersectionObserver?.disconnect()
5059

5160
// Remove theme change listener
5261
this._removeThemeChangeListener()
5362

5463
tinymce.get(this.editorId)?.remove(this.editorId)
5564
}
5665

57-
render() {
58-
return `
59-
${this.initialContent}
60-
<alchemy-spinner size="small"></alchemy-spinner>
61-
`
62-
}
63-
64-
/**
65-
* hide the textarea until TinyMCE is ready to show the editor
66-
*/
67-
afterRender() {
68-
this.style.minHeight = `${this.minHeight}px`
69-
this.editor.style.display = "none"
70-
}
71-
7266
/**
7367
* initialize Richtext area after the Intersection observer triggered
7468
* @private

0 commit comments

Comments
 (0)