From 13dce878f5aed3479bf8a783141283b9925c19af Mon Sep 17 00:00:00 2001 From: Konnor Rogers Date: Thu, 12 Oct 2023 08:16:37 -0400 Subject: [PATCH] allow light dom editor to be slotted (#143) * allow light dom editor to be slotted. * uncomment some thing * add the ability to slot in a light dom editor * add the ability to slot in a light dom editor * add the ability to slot in a light dom editor * working on tests * working on tests * fix test suite * fix test suite * prettier --- .changeset/late-zebras-march.md | 5 ++ .changeset/serious-avocados-act.md | 5 ++ docs/frontend/styles/_normalize.css | 2 +- docs/frontend/styles/components/_top_nav.css | 2 + ...d-additional-attributes-onto-the-editor.md | 37 ++++++++++ src/exports/elements/tip-tap-editor-base.ts | 73 ++++++++++++++----- src/exports/elements/tip-tap-editor.ts | 4 +- src/exports/styles/editor.js | 5 +- tests/rails/app/views/posts/_form.html.erb | 2 + tests/unit/light-dom-render.test.js | 27 +++++++ tests/unit/serializer.test.js | 4 +- 11 files changed, 139 insertions(+), 27 deletions(-) create mode 100644 .changeset/late-zebras-march.md create mode 100644 .changeset/serious-avocados-act.md create mode 100644 docs/src/_documentation/how_tos/13-add-additional-attributes-onto-the-editor.md create mode 100644 tests/unit/light-dom-render.test.js diff --git a/.changeset/late-zebras-march.md b/.changeset/late-zebras-march.md new file mode 100644 index 00000000..09f722f0 --- /dev/null +++ b/.changeset/late-zebras-march.md @@ -0,0 +1,5 @@ +--- +"rhino-editor": patch +--- + +fix: link-dialog buttons now have proper hover / focus state. diff --git a/.changeset/serious-avocados-act.md b/.changeset/serious-avocados-act.md new file mode 100644 index 00000000..357681c4 --- /dev/null +++ b/.changeset/serious-avocados-act.md @@ -0,0 +1,5 @@ +--- +"rhino-editor": minor +--- + +BREAKING_CHANGE: Allow the light-dom editor to be slotted. Do note, this change may result in a small breaking change for the users relying on the original light-dom structure being `div > div.trix-content`. Most users should not see a difference. diff --git a/docs/frontend/styles/_normalize.css b/docs/frontend/styles/_normalize.css index 8dceecf1..49387381 100644 --- a/docs/frontend/styles/_normalize.css +++ b/docs/frontend/styles/_normalize.css @@ -1,7 +1,7 @@ html { box-sizing: border-box; height: 100%; - font-size: 18px; + font-size: 16px; /* letter-spacing: 0.025em; */ } diff --git a/docs/frontend/styles/components/_top_nav.css b/docs/frontend/styles/components/_top_nav.css index 10cf8494..240a41f8 100644 --- a/docs/frontend/styles/components/_top_nav.css +++ b/docs/frontend/styles/components/_top_nav.css @@ -20,6 +20,8 @@ .top-nav__hamburger__button { font-size: 1.75em; + display: flex; + align-items: center; } .top-nav__hamburger__button::part(base), diff --git a/docs/src/_documentation/how_tos/13-add-additional-attributes-onto-the-editor.md b/docs/src/_documentation/how_tos/13-add-additional-attributes-onto-the-editor.md new file mode 100644 index 00000000..3dce87dd --- /dev/null +++ b/docs/src/_documentation/how_tos/13-add-additional-attributes-onto-the-editor.md @@ -0,0 +1,37 @@ +--- +title: Add Additional Attributes onto the Editor +permalink: /add-additional-attributes-onto-the-editor/ +--- + +Sometimes you may want to add additional attributes directly onto the `contenteditable` of RhinoEditor. + +The easiest way to do this is by slotting in an editor with the attributes you would like. Here's an example of how we +could add `aria-*` attributes onto the editor in cases where perhaps the form failed validation. + +```erb + + +
+
+
+ +
+ <%% if object.errors.any? %> + <%%= object.errors.to_s %> + <%% end %> +
+``` + +This will produce something like the following: + +```html + + +
+
+
+ +
+ Wow dude. You really messed up. What did you even submit? +
+``` diff --git a/src/exports/elements/tip-tap-editor-base.ts b/src/exports/elements/tip-tap-editor-base.ts index 6d57ac16..e7eef19c 100644 --- a/src/exports/elements/tip-tap-editor-base.ts +++ b/src/exports/elements/tip-tap-editor-base.ts @@ -121,33 +121,68 @@ export class TipTapEditorBase extends BaseElement { */ extensions: EditorOptions["extensions"] = []; + /** + * @internal + */ + __initialAttributes: Record = {}; + + /** + * @internal + */ + __hasRendered: boolean = false; + + __getInitialAttributes() { + if (this.__hasRendered) return; + + const slottedEditor = this.slottedEditor; + if (slottedEditor) { + this.__initialAttributes = {}; + [...slottedEditor.attributes].forEach((attr) => { + const { nodeName, nodeValue } = attr; + if (nodeName && nodeValue != null) { + this.__initialAttributes[nodeName] = nodeValue; + } + }); + } + + this.__hasRendered = true; + } + /** * Reset mechanism. This is called on first connect, and called anytime extensions, * or editor options get modified to make sure we have a fresh instance. */ rebuildEditor() { + const editors = this.querySelectorAll("[slot='editor']"); + + this.__getInitialAttributes(); + // Make sure we dont render the editor more than once. if (this.editor) this.editor.destroy(); - const editors = this.querySelectorAll("[slot='editor']"); + editors.forEach((el) => { // @ts-expect-error - el.querySelector(".tiptap")?.editor?.destroy(); + el.editor?.destroy(); el.remove(); }); - // light-dom version. - const div = document.createElement("div"); - div.setAttribute("slot", "editor"); + this.editor = this.__setupEditor(this); - // This may seem strange, but for some reason its the only wayto get the DropCursor working correctly. - div.style.position = "relative"; - this.insertAdjacentElement("beforeend", div); + this.__bindEditorListeners(); - this.editor = this.__setupEditor(div); + this.editorElement = this.querySelector(".ProseMirror"); - this.__bindEditorListeners(); - this.editorElement = div.querySelector(".ProseMirror"); - // + Object.entries(this.__initialAttributes)?.forEach( + ([attrName, attrValue]) => { + if (attrName === "class") { + this.editorElement?.classList.add(...attrValue.split(" ")); + return; + } + this.editorElement?.setAttribute(attrName, attrValue); + }, + ); + + this.editorElement?.setAttribute("slot", "editor"); this.editorElement?.classList.add("trix-content"); this.editorElement?.setAttribute("tabindex", "0"); this.editorElement?.setAttribute("role", "textbox"); @@ -157,9 +192,7 @@ export class TipTapEditorBase extends BaseElement { } protected willUpdate( - changedProperties: - | PropertyValueMap - | Map, + changedProperties: PropertyValueMap | Map, ): void { if (changedProperties.has("class")) { this.classList.add("rhino-editor"); @@ -175,6 +208,10 @@ export class TipTapEditorBase extends BaseElement { protected updated( changedProperties: PropertyValueMap | Map, ): void { + if (changedProperties.has("readonly")) { + this.editor?.setEditable(!this.readonly); + } + if ( changedProperties.has("extensions") || changedProperties.has("starterKitOptions") || @@ -183,10 +220,6 @@ export class TipTapEditorBase extends BaseElement { this.rebuildEditor(); } - if (changedProperties.has("readonly")) { - this.editor?.setEditable(!this.readonly); - } - super.updated(changedProperties); } @@ -614,7 +647,7 @@ export class TipTapEditorBase extends BaseElement { this.editor.off("blur", this.__handleBlur); } - private __setupEditor(element: Element): Editor { + private __setupEditor(element: Element = this): Editor { if (!this.serializer || this.serializer === "html") { // This is a super hacky way to get __to_trix_html to support figcaptions without patching it. this.normalizeDOM(this.inputElement); diff --git a/src/exports/elements/tip-tap-editor.ts b/src/exports/elements/tip-tap-editor.ts index ea81322b..ff9452e9 100644 --- a/src/exports/elements/tip-tap-editor.ts +++ b/src/exports/elements/tip-tap-editor.ts @@ -1109,14 +1109,14 @@ export class TipTapEditor extends TipTapEditorBase { /> `) + await aTimeout(0) + const rhinoEditor = div.querySelector("rhino-editor") const input = div.querySelector("input") assert.equal(rhinoEditor.serializer, "html") - assert.equal(input.value, "

") + assert.equal(input.value, "") rhinoEditor.serializer = "json"