Last active
February 8, 2023 15:48
-
-
Save vctls/ee8c77a1ec712984e85b247cbf9f6459 to your computer and use it in GitHub Desktop.
OpenProject : replace inline editing on click by a standard edit button.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name OpenProject Standard Edit | |
// @namespace http://tampermonkey.net/ | |
// @version 0.9 | |
// @downloadURL https://gist.github.com/vctls/ee8c77a1ec712984e85b247cbf9f6459/raw | |
// @updateURL https://gist.github.com/vctls/ee8c77a1ec712984e85b247cbf9f6459/raw | |
// @description Replace inline editing on click in OpenProject by a standard edit button. | |
// @author vctls | |
// @match https://*.openproject.com/* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=openproject.com | |
// @grant none | |
// ==/UserScript== | |
'use strict'; | |
const clickHandler = event => { | |
const target = event.target; | |
const parent = target.parentElement; | |
if ( | |
// If the click event is our custom one, | |
// or it is triggered on one of the editor buttons | |
// let it pass. | |
// Otherwise, prevent it from propagating. | |
// TODO This must have a cleaner solution. | |
event.custom !== undefined | |
|| target.tagName === 'A' | |
|| target.tagName === 'OP-ICON' | |
|| parent.tagName === 'OP-ICON' | |
|| (parent.parent && parent.parent.tagName === 'BUTTON') | |
|| target.classList.contains('editbutton') | |
// Allow form controls inside an active editor. | |
|| target.closest('div.inline-edit--container').classList.contains('-active') && ( | |
(target.attributes.type?.nodeValue === "checkbox" ?? false) | |
|| target.closest('div.document-editor__toolbar') | |
) | |
) { | |
return; | |
} | |
event.preventDefault(); | |
event.stopPropagation(); | |
}; | |
const buttonClickHandler = event => { | |
if (event.custom !== undefined) return; | |
const target = event.target; | |
const clickEvent = new MouseEvent('click', { | |
bubbles: true | |
}); | |
clickEvent.custom = true; | |
target.parentElement.querySelector('.-editable').dispatchEvent(clickEvent); | |
} | |
function setupEditorButton(editor) { | |
const button = document.createElement('button'); | |
button.classList.add('editbutton'); | |
button.classList.add('icon-edit'); // Use existing edit icon. | |
button.classList.add('inplace-edit--controls') // Style it as existing edit controls. | |
const s = button.style; | |
// Absolute position does not work well on the "New Task" view, | |
// but the script isn't really useful there. | |
s.position = 'absolute'; | |
s.right = '0'; | |
s.top = '0'; | |
s.height = '40px'; | |
s.width = '40px'; | |
editor.append(button); | |
button.addEventListener('click', buttonClickHandler); | |
editor.addEventListener( | |
'click', | |
clickHandler, | |
// This third argument is necessary | |
// in order to capture all events before | |
// they trigger other event listeners. | |
true | |
); | |
} | |
function check(changes, observer) { | |
// Skip irrelevant pages. | |
// At the moment, only work pacakage editing views should be handled. | |
const relevantPagesPattern = new RegExp('(?:/projects/\\w+/)?work_packages/\\d+/.*'); | |
if (!window.location.pathname.match(relevantPagesPattern)){ | |
return; | |
} | |
// TODO Get the editors from the changelist instead of querying them. | |
const editors = document.querySelectorAll('op-editable-attribute-field'); | |
editors.forEach( | |
editor => { | |
if ( | |
editor !== null | |
&& editor.querySelector('.subject, .description') | |
&& !editor.hasButton) { | |
setupEditorButton(editor); | |
editor.hasButton = true; | |
} | |
} | |
); | |
// Automatically dismiss HTTP response failure errors. | |
const errorToast = document.querySelector('op-toast') | |
if (errorToast && errorToast.textContent.includes('Http failure response')) { | |
errorToast.querySelector('button').dispatchEvent(new MouseEvent('click')); | |
} | |
} | |
(function () { | |
'use strict'; | |
(new MutationObserver(check)).observe(document, {childList: true, subtree: true}); | |
})(); |
v0.9: Fixed interference with anchors.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
v0.8: Fixed interference with CKEditor controls.