Skip to content

Commit

Permalink
feat: Improved dynamic link ui for tiptap teditor
Browse files Browse the repository at this point in the history
  • Loading branch information
fsbraun committed Dec 21, 2024
1 parent e9d3b48 commit f01f15c
Show file tree
Hide file tree
Showing 11 changed files with 1,121 additions and 3,477 deletions.
4,372 changes: 995 additions & 3,377 deletions package-lock.json

Large diffs are not rendered by default.

83 changes: 26 additions & 57 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
},
"homepage": "https://github.com/django-cms/djangocms-text-rte",
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/jest-dom": "^6.6.3",
"babel-jest": "^29.7.0",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
Expand All @@ -32,68 +32,37 @@
"jest-environment-jsdom": "^29.7.0",
"mini-css-extract-plugin": "^2.9.2",
"npm-upgrade": "^3.1.0",
"postcss": "^8.4.47",
"postcss": "^8.4.49",
"postcss-import": "^16.1.0",
"postcss-loader": "^8.1.1",
"postcss-nested": "^6.2.0",
"sass": "^1.80.6",
"sass-loader": "^16.0.3",
"slim-select": "^2.9.2",
"sass": "^1.83.0",
"sass-loader": "^16.0.4",
"slim-select": "^2.10.0",
"source-map-loader": "^5.0.0",
"style-loader": "^4.0.0",
"webpack": "^5.96.1",
"webpack": "^5.97.1",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@ckeditor/ckeditor5-adapter-ckfinder": "^43.1.1",
"@ckeditor/ckeditor5-alignment": "^43.1.1",
"@ckeditor/ckeditor5-autoformat": "^43.1.1",
"@ckeditor/ckeditor5-autosave": "^43.1.1",
"@ckeditor/ckeditor5-basic-styles": "^43.1.1",
"@ckeditor/ckeditor5-block-quote": "^43.1.1",
"@ckeditor/ckeditor5-ckfinder": "^43.1.1",
"@ckeditor/ckeditor5-cloud-services": "^43.1.1",
"@ckeditor/ckeditor5-code-block": "^43.1.1",
"@ckeditor/ckeditor5-dev-utils": "^45.0.8",
"@ckeditor/ckeditor5-easy-image": "^43.1.1",
"@ckeditor/ckeditor5-editor-balloon": "^43.1.1",
"@ckeditor/ckeditor5-editor-classic": "^43.1.1",
"@ckeditor/ckeditor5-editor-inline": "^43.1.1",
"@ckeditor/ckeditor5-essentials": "^43.1.1",
"@ckeditor/ckeditor5-font": "^43.1.1",
"@ckeditor/ckeditor5-heading": "^43.1.1",
"@ckeditor/ckeditor5-highlight": "^43.1.1",
"@ckeditor/ckeditor5-horizontal-line": "^43.1.1",
"@ckeditor/ckeditor5-image": "^43.1.1",
"@ckeditor/ckeditor5-indent": "^43.1.1",
"@ckeditor/ckeditor5-link": "^43.1.1",
"@ckeditor/ckeditor5-list": "^43.1.1",
"@ckeditor/ckeditor5-media-embed": "^43.1.1",
"@ckeditor/ckeditor5-paragraph": "^43.1.1",
"@ckeditor/ckeditor5-paste-from-office": "^43.1.1",
"@ckeditor/ckeditor5-source-editing": "^43.1.1",
"@ckeditor/ckeditor5-table": "^43.1.1",
"@ckeditor/ckeditor5-theme-lark": "^43.1.1",
"@ckeditor/ckeditor5-typing": "^43.1.1",
"@ckeditor/ckeditor5-upload": "^43.1.1",
"@tiptap/core": "^2.9.1",
"@tiptap/extension-blockquote": "^2.9.1",
"@tiptap/extension-bubble-menu": "^2.9.1",
"@tiptap/extension-character-count": "^2.9.1",
"@tiptap/extension-floating-menu": "^2.9.1",
"@tiptap/extension-image": "^2.9.1",
"@tiptap/extension-link": "^2.9.1",
"@tiptap/extension-placeholder": "^2.9.1",
"@tiptap/extension-subscript": "^2.9.1",
"@tiptap/extension-superscript": "^2.9.1",
"@tiptap/extension-table": "^2.9.1",
"@tiptap/extension-table-cell": "^2.9.1",
"@tiptap/extension-table-header": "^2.9.1",
"@tiptap/extension-table-row": "^2.9.1",
"@tiptap/extension-text-align": "^2.9.1",
"@tiptap/extension-underline": "^2.9.1",
"@tiptap/pm": "^2.9.1",
"@tiptap/starter-kit": "^2.9.1",
"@tiptap/core": "^2.10.4",
"@tiptap/extension-blockquote": "^2.10.4",
"@tiptap/extension-bubble-menu": "^2.10.4",
"@tiptap/extension-character-count": "^2.10.4",
"@tiptap/extension-floating-menu": "^2.10.4",
"@tiptap/extension-image": "^2.10.4",
"@tiptap/extension-link": "^2.10.4",
"@tiptap/extension-placeholder": "^2.10.4",
"@tiptap/extension-subscript": "^2.10.4",
"@tiptap/extension-superscript": "^2.10.4",
"@tiptap/extension-table": "^2.10.4",
"@tiptap/extension-table-cell": "^2.10.4",
"@tiptap/extension-table-header": "^2.10.4",
"@tiptap/extension-table-row": "^2.10.4",
"@tiptap/extension-text-align": "^2.10.4",
"@tiptap/extension-underline": "^2.10.4",
"@tiptap/pm": "^2.10.4",
"@tiptap/starter-kit": "^2.10.4",
"html-loader": "^5.1.0",
"quill": "^2.0.3"
}
Expand Down
4 changes: 3 additions & 1 deletion private/css/cms.balloon-toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
border-radius: 3px;
box-shadow: none;
/* box-shadow: 0 1.5px 1.5px rgba(var(--dca-shadow),.4); */
inset-inline-end: calc(100% + 1rem);
inset-inline-end: calc(100% + 0.75rem);
width: calc(1.6*var(--size));
height: calc(1.6*var(--size));
line-height: calc(1.3*var(--size));
Expand Down Expand Up @@ -43,4 +43,6 @@

dialog.cms-form-dialog.cms-balloon-menu {
padding: 2px 0.4rem;
border-radius: 0;
box-shadow: 0 1.5px 1.5px rgba(var(--dca-shadow),.4);
}
10 changes: 5 additions & 5 deletions private/css/cms.linkfield.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
.cms-linkfield-selected {
font-weight: bold;
}
.cms-linkfield-dropdown:not(:empty), .cms-linkfield-dropdown:active {
/* Hide dropdown when empty */
visibility: visible;
}
.cms-linkfield-dropdown {
z-index: 1;
visibility: hidden;
visibility: visible;
&:empty {
/* Hide dropdown when empty */
visibility: hidden;
}
position: absolute;
max-block-size: 400px;
overflow: auto;
Expand Down
43 changes: 33 additions & 10 deletions private/css/cms.tiptap.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,41 @@
overflow-y: auto;
}
position: relative;
a {
pointer-events: none;
}
& cms-plugin {
pointer-events: auto;
a {
pointer-events: none; /* for text-enabled link plugins */
.tiptap {
&.resize-cursor {
cursor: col-resize;
}
a[href] {
cursor: pointer;
}
&.ProseMirror-selectednode > * {
outline: 2px solid #fad507;
outline-offset: 2px;
table {
th, td {
position: relative;
}
.selectedCell {
background: color-mix(in srgb, var(--dca-primary) 20%, transparent);
}
.column-resize-handle {
top: 0;
bottom: 0;
right: -1px;
width: 2px;
position: absolute;
background: var(--dca-primary);
box-shadow: 0 0 2px var(--dca-primary);
}
}
& cms-plugin {
pointer-events: auto;
a {
pointer-events: none; /* for text-enabled link plugins */
}
&.ProseMirror-selectednode > * {
outline: 2px solid #fad507;
outline-offset: 2px;
}
}

}
[role="menubar"] {
bottom: calc(100% - 1px);
Expand Down
23 changes: 12 additions & 11 deletions private/js/cms.dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CmsDialog {
this.el = el;
this.saveSuccess = saveSuccess;
this.cancel = cancel;
this.close = this._close.bind(this);
}

/**
Expand Down Expand Up @@ -176,8 +177,8 @@ class CmsDialog {
* @memberof ClassName
* @returns {void}
*/
close() {
this.dialog.removeEventListener("close", this.close.bind(this));
_close() {
this.dialog.removeEventListener("close", this.close);
this.dialog.remove();
}

Expand Down Expand Up @@ -251,6 +252,7 @@ class CmsForm {
this.el = el;
this.saveSuccess = saveSuccess;
this.cancel = cancel;
this.close = this._close.bind(this);
}

formDialog(form, options) {
Expand All @@ -277,7 +279,7 @@ class CmsForm {
const el_pos = this.el.getBoundingClientRect();
if (options.x > window.innerWidth / 2) {
this.dialog.classList.add("right");
this.dialog.style.right = ( el_pos.x + el_pos.width - options.x - 24) + 'px';
this.dialog.style.right = (el_pos.x + el_pos.width - options.x - 24 - 10) + 'px';
} else {
this.dialog.style.left = (options.x - el_pos.x - 24) + 'px';
}
Expand All @@ -291,11 +293,10 @@ class CmsForm {
event.stopPropagation();
this.close();
});
document.addEventListener("click", this.close.bind(this));
document.addEventListener("click", this.close);
if (this.dialog.querySelector('.cancel')) {
this.dialog
.querySelector(".cancel")
.addEventListener('click', (event) => this.close() );
this.dialog.querySelector(".cancel")
.addEventListener('click', () => this.close() );
}
this.dialog.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
Expand Down Expand Up @@ -329,14 +330,14 @@ class CmsForm {
}
}

close(event) {
_close(event) {
if (!event || !this.dialog.contains(event.target)) {

// Do only close if the click is outside the dialog
document.removeEventListener("click", this.close);
this.dialog.removeEventListener("close", this.close);
if (this.cancel) {
this.cancel(event);
}
document.removeEventListener("click", this.close.bind(this));
this.dialog.removeEventListener("close", this.close.bind(this));
this.dialog.remove();
}
}
Expand Down
4 changes: 2 additions & 2 deletions private/js/cms.linkfield.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ class LinkField {
}

openDropdown(event) {
if (this.dropdown.style.visibility === 'visible') {
if (this.dropdown.style.visibility !== 'hidden') {
return;
}
this.dropdown.style.visibility = 'visible';
this.dropdown.style.visibility = '';
document.addEventListener('click', this.closeDropdown.bind(this));
}

Expand Down
14 changes: 7 additions & 7 deletions private/js/cms.tiptap.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ class CMSTipTapPlugin {
TableRow,
TableHeader,
TableCell,
CmsDynLink.extend({
inclusive: false,
}),
CmsDynLink,
Small, Var, Kbd, Samp,
CmsPluginNode,
CmsBlockPluginNode,
Expand Down Expand Up @@ -124,7 +122,7 @@ class CMSTipTapPlugin {
save_callback: save_callback,
settings: settings,
});
editor.on('blur', ({editor, event}) => {
el.addEventListener('blur', ({editor, event}) => {
this._blurEditor(editor, event);
});
editor.on('update', ({editor}) => {
Expand All @@ -139,10 +137,11 @@ class CMSTipTapPlugin {
}
});
this._editors[el.id] = editor;
const el_rect = el.getBoundingClientRect();
editor.cmsPlugin = this;

if (el.tagName === 'TEXTAREA' || el_rect.x < 32) {
// Not inline
const el_rect = el.getBoundingClientRect();
if (el.tagName === 'TEXTAREA' || el_rect.x < 28) {
// Not inline or too close to the left edge to see the block toolbar
this._createTopToolbar(editorElement, editor, options);
if (el.rows && !el.closest('body.app-djangocms_text.change-form')) {
editorElement.querySelector('.tiptap').style.height = el.rows * 1.5 + 'em';
Expand Down Expand Up @@ -530,6 +529,7 @@ class CMSTipTapPlugin {
if (action) {
if (TiptapToolbar[action]) {
const toolbarItem = this._getRepresentation(action);
button.disabled = !toolbarItem.enabled(editor, button);
try {
button.disabled = !toolbarItem.enabled(editor, button);
try {
Expand Down
35 changes: 32 additions & 3 deletions private/js/tiptap_plugins/cms.dynlink.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-env es6 */
/* jshint esversion: 6 */
/* eslint-env es11 */
/* jshint esversion: 11 */
/* global document, window, console */

import Link from '@tiptap/extension-link';


const CmsDynLink = Link.extend({
addAttributes() {
'use strict';
Expand All @@ -18,7 +19,35 @@ const CmsDynLink = Link.extend({
default: null
},
};
}
},

onCreate({editor}) {
editor.parent?.(); // Call the parent implementation, if it exists
this.handleEvent = ((event) => {
'use strict';
const target = event.target.closest('a[href]');
if (target) {
event.preventDefault();
setTimeout(() => {
if (this.editor.isActive('link')) {
this.editor.commands.openCmsForm('Link');
}
}, 0);
}
}).bind(this); // hacky: move the eventHandler to the Mark object (this) to be able to remove it later
console.log("Event", editor.view.dom);
editor.view.dom.addEventListener('click', this);
},

onDestroy({editor}) {
editor.parent?.(); // Call the parent implementation, if it exists
editor.view.dom.removeEventListener('click', this);
},
}).configure({
openOnClick: false,
HTMLAttributes: {
rel: 'noopener noreferrer',
},
});


Expand Down
4 changes: 2 additions & 2 deletions private/js/tiptap_plugins/cms.formextension.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const FormExtension = Extension.create({
}
const dialog = new CmsForm(
editor.options.element,
data => TiptapToolbar[action].formAction(editor, data)
// () => editor.commands.focus()
data => TiptapToolbar[action].formAction(editor, data),
() => editor.commands.focus()
);
const formRepresentation = window.cms_editor_plugin._getRepresentation(action);
const formElement = dialog.formDialog(formToHtml(formRepresentation.form), options);
Expand Down
Loading

0 comments on commit f01f15c

Please sign in to comment.