Skip to content

Commit

Permalink
Migrate Tinymce module into a web component
Browse files Browse the repository at this point in the history
Instead of multiple initializations and removals let the browser handle the problem. Web components have life cycle hooks to create and destroy the Tinymce editor.
  • Loading branch information
sascha-karnatz committed Aug 25, 2023
1 parent 43030aa commit c27b32d
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 228 deletions.
4 changes: 0 additions & 4 deletions app/assets/javascripts/alchemy/alchemy.dialog.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class window.Alchemy.Dialog
@overlay.removeClass('open') if @overlay?
@$document.on 'webkitTransitionEnd transitionend oTransitionEnd', =>
@$document.off 'webkitTransitionEnd transitionend oTransitionEnd'
Alchemy.Tinymce.removeFrom $('.tinymce', @dialog_body)
@dialog_container.remove()
@overlay.remove() if @overlay?
@$body.removeClass('prevent-scrolling')
Expand Down Expand Up @@ -105,9 +104,6 @@ class window.Alchemy.Dialog
# Initializes the Dialog body
init: ->
Alchemy.GUI.init(@dialog_body)
Alchemy.Tinymce.initWith
selector: ".alchemy-dialog-body textarea.tinymce",
width: '65%'
$('#overlay_tabs', @dialog_body).tabs()
@watch_remote_forms()

Expand Down
12 changes: 0 additions & 12 deletions app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ $.extend Alchemy,
Alchemy.initializedSortableElements = false
$sortable_area = $(selector)

getTinymceIDs = (ui) ->
ids = []
$textareas = ui.item.find('textarea.has_tinymce')
$($textareas).each ->
id = this.id.replace(/tinymce_/, '')
ids.push parseInt(id, 10)
return ids

sortable_options =
items: "> .element-editor"
handle: "> .element-header .element-handle"
Expand Down Expand Up @@ -55,23 +47,19 @@ $.extend Alchemy,
$this = $(this)
name = ui.item.data('element-name')
$dropzone = $("[data-droppable-elements~='#{name}']")
ids = getTinymceIDs(ui)
$this.sortable('option', 'connectWith', $dropzone)
$this.sortable('refresh')
$dropzone.css('minHeight', 36)
ui.item.addClass('dragged')
if ui.item.hasClass('compact')
ui.placeholder.addClass('compact').css
height: ui.item.outerHeight()
Alchemy.Tinymce.remove(ids)
return
stop: (event, ui) ->
ids = getTinymceIDs(ui)
name = ui.item.data('element-name')
$dropzone = $("[data-droppable-elements~='#{name}']")
$dropzone.css('minHeight', '')
ui.item.removeClass('dragged')
Alchemy.Tinymce.init(ids)
return

$sortable_area.sortable(sortable_options)
Expand Down
8 changes: 1 addition & 7 deletions app/javascript/alchemy_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import ImageLoader from "alchemy_admin/image_loader"
import ImageCropper from "alchemy_admin/image_cropper"
import Initializer from "alchemy_admin/initializer"
import Sitemap from "alchemy_admin/sitemap"
import Tinymce from "alchemy_admin/tinymce"
import PagePublicationFields from "alchemy_admin/page_publication_fields"

// Web Components
import "alchemy_admin/components/char_counter"
import "alchemy_admin/components/tinymce"
import "alchemy_admin/components/datepicker"

// Global Alchemy object
Expand All @@ -38,13 +38,7 @@ Object.assign(Alchemy, {
Initializer,
IngredientAnchorLink,
Sitemap,
Tinymce,
PagePublicationFields
})

$(document).on("turbo:load", Initializer)

$(document).on("turbo:before-fetch-request", function () {
Alchemy.Tinymce.removeIntersectionObserver()
Alchemy.Tinymce.removeFrom($(".has_tinymce"))
})
72 changes: 72 additions & 0 deletions app/javascript/alchemy_admin/components/tinymce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
class Tinymce extends HTMLTextAreaElement {
constructor() {
super()
this.externalConfig = {}
}
/**
* the observer will initialize Tinymce if the textarea becomes visible
*/
connectedCallback() {
const observerCallback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
this.initTinymceEditor(entry.target)
// disable observer after the Tinymce was initialized
observer.unobserve(entry.target)
}
})
}

const options = {
root: document.getElementById("element_area"),
rootMargin: "0px",
threshold: [0.05]
}

this.tinymceIntersectionObserver = new IntersectionObserver(
observerCallback,
options
)
this.tinymceIntersectionObserver.observe(this)
}

/**
* disconnect intersection observer and remove Tinymce editor if the web components get destroyed
*/
disconnectedCallback() {
if (this.tinymceIntersectionObserver !== null) {
this.tinymceIntersectionObserver.disconnect()
}

// remove the display property to enable the intersection observer again
// Tinymce will leave the textarea with an display: none in the DOM and the intersection observer
// does not trigger on none visible elements
this.style.display = null

const editor = tinymce.get(this.id)
if (editor) {
editor.remove()
}
}

initTinymceEditor() {
const spinner = new Alchemy.Spinner("small")
this.closest(".tinymce_container").prepend(spinner.spin().el.get(0))
tinymce.init(this.configuration)
}

get configuration() {
return {
...Alchemy.TinymceDefaults,
...this.externalConfig,
locale: Alchemy.locale,
selector: `#${this.id}`
}
}

set configuration(config) {
this.externalConfig = config
}
}

customElements.define("alchemy-tinymce", Tinymce, { extends: "textarea" })
146 changes: 0 additions & 146 deletions app/javascript/alchemy_admin/tinymce.js

This file was deleted.

1 change: 0 additions & 1 deletion app/views/alchemy/admin/elements/create.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@

Alchemy.growl('<%= Alchemy.t(:successfully_added_element) %>');
Alchemy.closeCurrentDialog();
Alchemy.Tinymce.init(<%= @element.richtext_ingredients_ids.to_json %>);
Alchemy.PreviewWindow.refresh(function() {
Alchemy.ElementEditors.focusElementPreview(<%= @element.id %>);
});
Expand Down
1 change: 0 additions & 1 deletion app/views/alchemy/admin/elements/destroy.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ $('#element_<%= @element.id %>').hide(200, function() {
Alchemy.growl('<%= j @notice %>');
$('#element_area .sortable-elements').sortable('refresh');
Alchemy.PreviewWindow.refresh();
Alchemy.Tinymce.remove(<%= @richtext_ids.to_json %>);
<% if @element.fixed? %>
Alchemy.FixedElements.removeTab(<%= @element.id %>);
<% end %>
Expand Down
23 changes: 8 additions & 15 deletions app/views/alchemy/admin/elements/fold.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,14 @@
$el = $('#element_<%= @element.id %>');
$('#element_area .sortable-elements').sortable('refresh');

<% if @element.folded? -%>

Alchemy.Tinymce.remove(<%= @element.richtext_ingredients_ids.to_json %>);

<% else -%>

$el.trigger('FocusElementEditor.Alchemy');
Alchemy.Tinymce.init(<%= @element.richtext_ingredients_ids.to_json %>);
Alchemy.GUI.initElement($el);
Alchemy.SortableElements(
<%= @page.id %>,
'<%= form_authenticity_token %>',
$('> .nestable-elements .nested-elements', $el)
);

<% unless @element.folded? -%>
$el.trigger('FocusElementEditor.Alchemy');
Alchemy.GUI.initElement($el);
Alchemy.SortableElements(
<%= @page.id %>,
'<%= form_authenticity_token %>',
$('> .nestable-elements .nested-elements', $el)
);
<% end -%>

<% end -%>
Expand Down
1 change: 0 additions & 1 deletion app/views/alchemy/admin/pages/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@
Alchemy.SortableElements(<%= @page.id %>, '<%= form_authenticity_token %>');
Alchemy.ElementEditors.init();
Alchemy.SelectBox('.element-editor');
Alchemy.Tinymce.init(<%= @page.richtext_ingredients_ids.to_json %>);
$('#fixed-elements').tabs().tabs('paging', {
follow: true,
followOnSelect: true,
Expand Down
7 changes: 4 additions & 3 deletions app/views/alchemy/ingredients/_richtext_editor.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
<%= ingredient_label(richtext_editor, :value, for: richtext_dom_id) %>
<div class="tinymce_container">
<%= f.text_area :value,
id: richtext_dom_id,
class: "has_tinymce",
id: richtext_dom_id %>
is: "alchemy-tinymce" %>
</div>
<% end %>
<% if richtext_editor.has_custom_tinymce_config? %>
<script type="text/javascript" charset="utf-8">
Alchemy.Tinymce.setCustomConfig("<%= richtext_dom_id %>", {
document.getElementById("<%= richtext_dom_id %>").configuration = {
<% richtext_editor.custom_tinymce_config.each do |k, v| %>
<%= k %>: <%== v.to_json %>,
<% end %>
});
};
</script>
<% end %>
<% end %>
Loading

0 comments on commit c27b32d

Please sign in to comment.