Skip to content

Commit

Permalink
fix: Handle partial page updates (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
fsbraun authored Jan 18, 2025
1 parent e90fb53 commit 37f8957
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 122 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@
Changelog
=========

0.5.3 (18-01-2025)
==================
* fix: Markup error disabled the block toolbar in inline editing
* fix: Handle partial page updates

0.5.2 (16-01-2025)
==================

* feat: Add text color from list of options by @fsbraun in https://github.com/django-cms/djangocms-text/pull/48
* feat: allows an ``admin_css: tuple`` in ``RTEConfig`` for a list of CSS files only to be loaded into the admin for the editor by @fsbraun in https://github.com/django-cms/djangocms-text/pull/49
* feat: Add configurable block and inline styles for Tiptap by @fsbraun in https://github.com/django-cms/djangocms-text/pull/51
* fix: Update CKEditor4 vendor files to work with CMS plugins
* fix: Update icon paths for CKEditor4

Expand Down
2 changes: 1 addition & 1 deletion djangocms_text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
10. Github actions will publish the new package to pypi
"""

__version__ = "0.5.2"
__version__ = "0.5.3"
2 changes: 2 additions & 0 deletions private/css/cms.toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

[role="menubar"] {
bottom: calc(100% - 1px);
min-width: 375px;
z-index: 1;
padding: 2px 0.4rem;
/* border-radius: 3px; */
margin: 0 !important;
Expand Down
220 changes: 122 additions & 98 deletions private/js/cms.editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ class CMSEditor {
// CMS Editor: constructor
// Initialize the editor object
constructor() {
this._editors = [];
this._generic_editors = [];
this._global_settings = {};
this._editor_settings = {};
this._generic_editors = {};
this._admin_selector = 'textarea.CMS_Editor';
this._admin_add_row_selector = 'body.change-form .add-row a';
this._inline_admin_selector = 'body.change-form .form-row';
Expand Down Expand Up @@ -94,83 +93,25 @@ class CMSEditor {
// CMS Editor: init
// Initialize a single editor
init (el) {
let content;

// Get content: json > textarea > innerHTML
if (el.dataset.json) {
content = JSON.parse(el.dataset.json);
} else {
content = el.innerHTML;
}
if (el.tagName === 'TEXTAREA') {
el.visible = false;
content = el.value;
// el = el.insertAdjacentElement('afterend', document.createElement('div'));
}
if (!el.id) {
el.id = "cms-edit-" + Math.random().toString(36).slice(2, 9);
}
const settings = this.getSettings(el);
// Element options overwrite
settings.options = Object.assign({},
settings.options || {},
JSON.parse(el.dataset.options || '{}')
);

// Add event listener to delete data on modal cancel
if (settings.revert_on_cancel) {
const CMS = this.CMS;
const csrf = CMS.config?.csrf || document.querySelector('input[name="csrfmiddlewaretoken"]').value;
CMS.API.Helpers.addEventListener(
'modal-close.text-plugin.text-plugin-' + settings.plugin_id,
function(e, opts) {
if (!settings.revert_on_cancel || !settings.cancel_plugin_url) {
return;
}
CMS.$.ajax({
method: 'POST',
url: settings.cancel_plugin_url,
data: {
token: settings.action_token,
csrfmiddlewaretoken: csrf
},
}).done(function () {
CMS.API.Helpers.removeEventListener(
'modal-close.text-plugin.text-plugin-' + settings.plugin_id
);
opts.instance.close();
}).fail(function (res) {
CMS.API.Messages.open({
message: res.responseText + ' | ' + res.status + ' ' + res.statusText,
delay: 0,
error: true
});
});

}
);
if (el.id in this._editor_settings) {
// Already initialized - happens when an inline editor is scrolled back into the viewport
return;
}
const inModal = !!document.querySelector(
'.app-djangocms_text.model-text.change-form #' + el.id
);

// Create editor
if (!el.dataset.cmsType ||el.dataset.cmsType === 'TextPlugin' || el.dataset.cmsType === 'HTMLField') {
window.cms_editor_plugin.create(
el,
inModal,
content, settings,
el.tagName !== 'TEXTAREA' ? () => this.saveData(el) : () => {
}
);
if (!el.dataset.cmsType || el.dataset.cmsType === 'TextPlugin' || el.dataset.cmsType === 'HTMLField') {
this._createRTE(el);
} else if (el.dataset.cmsType === 'CharField') {
this._generic_editors.push(new CmsTextEditor(el, {
// Creat simple generic text editor
this._generic_editors[el.id] = new CmsTextEditor(el, {
spellcheck: el.dataset.spellcheck || 'false',
},
(el) => this.saveData(el)
));
);
}
this._editors.push(el);
}

// CMS Editor: initInlineEditors
Expand Down Expand Up @@ -208,8 +149,8 @@ class CMSEditor {

if (plugin[1].type === 'plugin' && plugin[1].plugin_type === 'TextPlugin') {
// Text plugin
const elements = document.querySelectorAll('.cms-plugin.cms-plugin-' + id);
wrapper = this._initInlineRichText(elements, url, id);
const elements = document.querySelectorAll('.cms-plugin.' + plugin[0]);
wrapper = this._initInlineRichText(elements, url, plugin[0]);
if (wrapper) {
wrapper.dataset.cmsPluginId = id;
wrapper.dataset.cmsType = 'TextPlugin';
Expand All @@ -224,7 +165,7 @@ class CMSEditor {
const search_key = `${generic_class[2]}-${generic_class[3]}-${edit_fields}`;
if (generic_inline_fields[search_key]) {
// Inline editable?
wrapper = this._initInlineRichText(document.getElementsByClassName(plugin[0]), url, id);
wrapper = this._initInlineRichText(document.getElementsByClassName(plugin[0]), url, plugin[0]);
if (wrapper) {
wrapper.dataset.cmsCsrfToken = this.CMS.config.csrf;
wrapper.dataset.cmsField = edit_fields;
Expand All @@ -241,22 +182,25 @@ class CMSEditor {
if (wrapper) {
// Catch CMS single click event to highlight the plugin
// Catch CMS double click event if present, since double click is needed by Editor
this.observer.observe(wrapper);
if (this.CMS) {
// Remove django CMS core's double click event handler which opens an edit dialog
this.CMS.$(wrapper).off('dblclick.cms.plugin')
.on('dblclick.cms-editor', function (event) {
event.stopPropagation();
});
wrapper.addEventListener('focusin.cms-editor', () => {
this._highlightTextplugin(id);
}, true);
// Prevent tooltip on hover
this.CMS.$(wrapper).off('pointerover.cms.plugin pointerout.cms.plugin')
.on('pointerover.cms-editor', function (event) {
window.CMS.API.Tooltip.displayToggle(false, event.target, '', id);
event.stopPropagation();
});
if (!Array.from(this.observer.root?.children || []).includes(wrapper)) {
// Only add to the observer if not already observed (e.g., if the page only was update partially)
this.observer.observe(wrapper);
if (this.CMS) {
// Remove django CMS core's double click event handler which opens an edit dialog
this.CMS.$(wrapper).off('dblclick.cms.plugin')
.on('dblclick.cms-editor', function (event) {
event.stopPropagation();
});
wrapper.addEventListener('focusin', () => {
this._highlightTextplugin(id);
}, true);
// Prevent tooltip on hover
this.CMS.$(wrapper).off('pointerover.cms.plugin pointerout.cms.plugin')
.on('pointerover.cms-editor', function (event) {
window.CMS.API.Tooltip.displayToggle(false, event.target, '', id);
event.stopPropagation();
});
}
}
}
}
Expand All @@ -271,17 +215,22 @@ class CMSEditor {
});
}

_initInlineRichText(elements, url, id) {
_initInlineRichText(elements, url, cls) {
let wrapper;

if (elements.length > 0) {
if (elements.length === 1 && elements[0].tagName === 'DIV' || elements[0].tagName === 'CMS-PLUGIN') {
if (elements.length === 1 && (
elements[0].tagName === 'DIV' || // Single wrapping div
elements[0].tagName === 'CMS-PLUGIN' || // Single wrapping cms-plugin tag
elements[0].classList.contains('cms-editor-inline-wrapper') // already wrapped
)) {
// already wrapped?
wrapper = elements[0];
wrapper.classList.add('cms-editor-inline-wrapper');
} else { // no, wrap now!
wrapper = document.createElement('div');
wrapper.classList.add('cms-editor-inline-wrapper', 'wrapped');
wrapper.classList.add('cms-plugin', cls);
wrapper = this._wrapAll(elements, wrapper);
}
wrapper.dataset.cmsEditUrl = url;
Expand All @@ -291,6 +240,73 @@ class CMSEditor {
return undefined;
}

_createRTE(el) {
const settings = this.getSettings(el);
// Element options overwrite
settings.options = Object.assign({},
settings.options || {},
JSON.parse(el.dataset.options || '{}')
);

// Add event listener to delete data on modal cancel
if (settings.revert_on_cancel) {
const CMS = this.CMS;
const csrf = CMS.config?.csrf || document.querySelector('input[name="csrfmiddlewaretoken"]').value;
CMS.API.Helpers.addEventListener(
'modal-close.text-plugin.text-plugin-' + settings.plugin_id,
function(e, opts) {
if (!settings.revert_on_cancel || !settings.cancel_plugin_url) {
return;
}
CMS.$.ajax({
method: 'POST',
url: settings.cancel_plugin_url,
data: {
token: settings.action_token,
csrfmiddlewaretoken: csrf
},
}).done(function () {
CMS.API.Helpers.removeEventListener(
'modal-close.text-plugin.text-plugin-' + settings.plugin_id
);
opts.instance.close();
}).fail(function (res) {
CMS.API.Messages.open({
message: res.responseText + ' | ' + res.status + ' ' + res.statusText,
delay: 0,
error: true
});
});

}
);
}
const inModal = !!document.querySelector(
'.app-djangocms_text.model-text.change-form #' + el.id
);
// Get content: json > textarea > innerHTML
let content;

if (el.dataset.json) {
content = JSON.parse(el.dataset.json);
} else {
content = el.innerHTML;
}
if (el.tagName === 'TEXTAREA') {
el.visible = false;
content = el.value;
// el = el.insertAdjacentElement('afterend', document.createElement('div'));
}

window.cms_editor_plugin.create(
el,
inModal,
content, settings,
el.tagName !== 'TEXTAREA' ? () => this.saveData(el) : () => {
}
);
}

/**
* Retrieves the settings for the given editor.
* If the element is a string, it will be treated as an element's ID.
Expand All @@ -315,11 +331,15 @@ class CMSEditor {
this._editor_settings[el.id] = Object.assign(
{},
this._global_settings,
JSON.parse(settings_el.textContent) || {}
JSON.parse(settings_el.textContent || '{}')
);
} else {
this._editor_settings[el.id] = Object.assign(
{},
this._global_settings,
);
return this._editor_settings[el.id];
}
return {};
return this._editor_settings[el.id];
}

/**
Expand All @@ -336,11 +356,16 @@ class CMSEditor {

// CMS Editor: destroy
destroyAll() {
while (this._editors.length) {
const el = this._editors.pop();
this.destroyGenericEditor(el);
this.destroyRTE();
this.destroyGenericEditor();
}

destroyRTE() {
for (const el of Object.keys(this._editor_settings)) {
const element = document.getElementById(el);
window.cms_editor_plugin.destroyEditor(el);
}
this._editor_settings = {};
}

// CMS Editor: destroyGenericEditor
Expand Down Expand Up @@ -611,7 +636,6 @@ class CMSEditor {

// CMS Editor: resetInlineEditors
_resetInlineEditors () {
this.destroyAll();
this.initAll();
}

Expand All @@ -627,7 +651,7 @@ class CMSEditor {
}

_highlightTextplugin (pluginId) {
const HIGHLIGHT_TIMEOUT = 800;
const HIGHLIGHT_TIMEOUT = 100;

if (this.CMS) {
const $ = this.CMS.$;
Expand Down Expand Up @@ -683,5 +707,5 @@ class CMSEditor {


// Create global editor object
window.CMS_Editor = new CMSEditor();
window.CMS_Editor = window.CMS_Editor || new CMSEditor();

2 changes: 1 addition & 1 deletion private/js/cms.tiptap.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,4 @@ class CMSTipTapPlugin {
}


window.cms_editor_plugin = new CMSTipTapPlugin({});
window.cms_editor_plugin = window.cms_editor_plugin || new CMSTipTapPlugin({});
4 changes: 1 addition & 3 deletions private/js/tiptap_plugins/cms.toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,7 @@ function _createToolbarButton(editor, itemName, filter) {
</form>`;
}
const content = repr.icon || `<span>${repr.title}</span>`;
return `<button tabindex="-1" data-action="${repr.dataaction}" ${cmsplugin}${title}${position}class="${classes}">
${content}${form}
</button>`;
return `<button data-action="${repr.dataaction}" ${cmsplugin}${title}${position}class="${classes}">${content}${form}</button>`;
}
return '';
}
Expand Down
Loading

0 comments on commit 37f8957

Please sign in to comment.