forked from forgejo/forgejo
Improve markdown editor: width, height, preferred (#23895)
Follow #23876 1. Fine tune the heights of the editors (like before) * Auto expand the editor (increase/decrease the height) when editing 2. Remember user's last used editor (textarea/easymde) in LocalStorage, then next time the editor will be switched automatically * No need to introduce extra config option, it satisfies all users, including who prefer EasyMDE 3. Also fix the width problem of Review Panel Screenshot: <details>       </details> --------- Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
parent
97d5ec2aeb
commit
93eb914438
7 changed files with 203 additions and 55 deletions
|
@ -49,3 +49,124 @@ export function onDomReady(cb) {
|
|||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
// autosize a textarea to fit content. Based on
|
||||
// https://github.com/github/textarea-autosize
|
||||
// ---------------------------------------------------------------------
|
||||
// Copyright (c) 2018 GitHub, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
// ---------------------------------------------------------------------
|
||||
export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
|
||||
let isUserResized = false;
|
||||
// lastStyleHeight and initialStyleHeight are CSS values like '100px'
|
||||
let lastMouseX, lastMouseY, lastStyleHeight, initialStyleHeight;
|
||||
|
||||
function onUserResize(event) {
|
||||
if (isUserResized) return;
|
||||
if (lastMouseX !== event.clientX || lastMouseY !== event.clientY) {
|
||||
const newStyleHeight = textarea.style.height;
|
||||
if (lastStyleHeight && lastStyleHeight !== newStyleHeight) {
|
||||
isUserResized = true;
|
||||
}
|
||||
lastStyleHeight = newStyleHeight;
|
||||
}
|
||||
|
||||
lastMouseX = event.clientX;
|
||||
lastMouseY = event.clientY;
|
||||
}
|
||||
|
||||
function overflowOffset() {
|
||||
let offsetTop = 0;
|
||||
let el = textarea;
|
||||
|
||||
while (el !== document.body && el !== null) {
|
||||
offsetTop += el.offsetTop || 0;
|
||||
el = el.offsetParent;
|
||||
}
|
||||
|
||||
const top = offsetTop - document.defaultView.scrollY;
|
||||
const bottom = document.documentElement.clientHeight - (top + textarea.offsetHeight);
|
||||
return {top, bottom};
|
||||
}
|
||||
|
||||
function resizeToFit() {
|
||||
if (isUserResized) return;
|
||||
if (textarea.offsetWidth <= 0 && textarea.offsetHeight <= 0) return;
|
||||
|
||||
try {
|
||||
const {top, bottom} = overflowOffset();
|
||||
const isOutOfViewport = top < 0 || bottom < 0;
|
||||
|
||||
const computedStyle = getComputedStyle(textarea);
|
||||
const topBorderWidth = parseFloat(computedStyle.borderTopWidth);
|
||||
const bottomBorderWidth = parseFloat(computedStyle.borderBottomWidth);
|
||||
const isBorderBox = computedStyle.boxSizing === 'border-box';
|
||||
const borderAddOn = isBorderBox ? topBorderWidth + bottomBorderWidth : 0;
|
||||
|
||||
const adjustedViewportMarginBottom = bottom < viewportMarginBottom ? bottom : viewportMarginBottom;
|
||||
const curHeight = parseFloat(computedStyle.height);
|
||||
const maxHeight = curHeight + bottom - adjustedViewportMarginBottom;
|
||||
|
||||
textarea.style.height = 'auto';
|
||||
let newHeight = textarea.scrollHeight + borderAddOn;
|
||||
|
||||
if (isOutOfViewport) {
|
||||
// it is already out of the viewport:
|
||||
// * if the textarea is expanding: do not resize it
|
||||
if (newHeight > curHeight) {
|
||||
newHeight = curHeight;
|
||||
}
|
||||
// * if the textarea is shrinking, shrink line by line (just use the
|
||||
// scrollHeight). do not apply max-height limit, otherwise the page
|
||||
// flickers and the textarea jumps
|
||||
} else {
|
||||
// * if it is in the viewport, apply the max-height limit
|
||||
newHeight = Math.min(maxHeight, newHeight);
|
||||
}
|
||||
|
||||
textarea.style.height = `${newHeight}px`;
|
||||
lastStyleHeight = textarea.style.height;
|
||||
} finally {
|
||||
// ensure that the textarea is fully scrolled to the end, when the cursor
|
||||
// is at the end during an input event
|
||||
if (textarea.selectionStart === textarea.selectionEnd &&
|
||||
textarea.selectionStart === textarea.value.length) {
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onFormReset() {
|
||||
isUserResized = false;
|
||||
if (initialStyleHeight !== undefined) {
|
||||
textarea.style.height = initialStyleHeight;
|
||||
} else {
|
||||
textarea.style.removeProperty('height');
|
||||
}
|
||||
}
|
||||
|
||||
textarea.addEventListener('mousemove', onUserResize);
|
||||
textarea.addEventListener('input', resizeToFit);
|
||||
textarea.form?.addEventListener('reset', onFormReset);
|
||||
initialStyleHeight = textarea.style.height ?? undefined;
|
||||
if (textarea.value) resizeToFit();
|
||||
|
||||
return {
|
||||
resizeToFit,
|
||||
destroy() {
|
||||
textarea.removeEventListener('mousemove', onUserResize);
|
||||
textarea.removeEventListener('input', resizeToFit);
|
||||
textarea.form?.removeEventListener('reset', onFormReset);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue