DEV: Update ExpandingTextArea component (#25890)

This commit is contained in:
Jarek Radosz 2024-03-01 17:20:15 +01:00 committed by GitHub
parent 3200e276b7
commit 5dc95eaacc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 47 additions and 251 deletions

View File

@ -24,8 +24,9 @@
{{/if}}
<ExpandingTextArea
@value={{this.buffered.value}}
@rows="1"
{{on "input" (with-event-value (fn (mut this.buffered.value)))}}
value={{this.buffered.value}}
rows="1"
class="site-text-value"
/>

View File

@ -0,0 +1,27 @@
import { on } from "@ember/modifier";
import autosize from "autosize";
import { modifier } from "ember-modifier";
import autoFocus from "discourse/modifiers/auto-focus";
const resize = modifier((element) => {
autosize(element);
return () => autosize.destroy(element);
});
const ExpandingTextArea = <template>
<textarea
{{autoFocus}}
{{resize}}
{{! deprecated args: }}
autocorrect={{@autocorrect}}
class={{@class}}
maxlength={{@maxlength}}
name={{@name}}
rows={{@rows}}
value={{@value}}
{{(if @input (modifier on "input" @input))}}
...attributes
></textarea>
</template>;
export default ExpandingTextArea;

View File

@ -1,30 +0,0 @@
import { TextArea } from "@ember/legacy-built-in-components";
import { schedule } from "@ember/runloop";
import $ from "jquery";
import autosize from "discourse/lib/autosize";
import { observes, on } from "discourse-common/utils/decorators";
export default TextArea.extend({
@on("didInsertElement")
_startWatching() {
schedule("afterRender", () => {
$(this.element).focus();
autosize(this.element);
});
},
@observes("value")
_updateAutosize() {
this.element.value = this.value;
const event = new Event("autosize:update", {
bubbles: true,
cancelable: false,
});
this.element.dispatchEvent(event);
},
@on("willDestroyElement")
_disableAutosize() {
autosize.destroy($(this.element));
},
});

View File

@ -42,8 +42,12 @@
</label>
<ExpandingTextArea
@name="membership-request-template"
@value={{this.model.membership_request_template}}
{{on
"input"
(with-event-value (fn (mut this.model.membership_request_template)))
}}
value={{this.model.membership_request_template}}
name="membership-request-template"
class="group-form-membership-request-template input-xxlarge"
/>
</div>

View File

@ -10,7 +10,11 @@
{{i18n "groups.membership_request.reason"}}
</label>
<ExpandingTextArea @value={{this.reason}} @maxlength="5000" />
<ExpandingTextArea
{{on "input" (with-event-value (fn (mut this.reason)))}}
value={{this.reason}}
maxlength="5000"
/>
</div>
</:body>

View File

@ -1,216 +0,0 @@
const set =
typeof Set === "function"
? new Set()
: (function () {
const list = [];
return {
has(key) {
return Boolean(list.includes(key));
},
add(key) {
list.push(key);
},
delete(key) {
list.splice(list.indexOf(key), 1);
},
};
})();
function assign(ta, { setOverflowX = true, setOverflowY = true } = {}) {
if (!ta || !ta.nodeName || ta.nodeName !== "TEXTAREA" || set.has(ta)) {
return;
}
let heightOffset = null;
let overflowY = null;
let clientWidth = ta.clientWidth;
function init() {
const style = window.getComputedStyle(ta, null);
overflowY = style.overflowY;
if (style.resize === "vertical") {
ta.style.resize = "none";
} else if (style.resize === "both") {
ta.style.resize = "horizontal";
}
if (style.boxSizing === "content-box") {
heightOffset = -(
parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)
);
} else {
heightOffset =
parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
}
// Fix when a textarea is not on document body and heightOffset is Not a Number
if (isNaN(heightOffset)) {
heightOffset = 0;
}
update();
}
function changeOverflow(value) {
{
// Chrome/Safari-specific fix:
// When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
// made available by removing the scrollbar. The following forces the necessary text reflow.
const width = ta.style.width;
ta.style.width = "0px";
// Force reflow:
/* jshint ignore:start */
ta.offsetWidth;
/* jshint ignore:end */
ta.style.width = width;
}
overflowY = value;
if (setOverflowY) {
ta.style.overflowY = value;
}
resize();
}
function resize() {
const htmlTop = window.pageYOffset;
const bodyTop = document.body.scrollTop;
const originalHeight = ta.style.height;
ta.style.height = "auto";
let endHeight = ta.scrollHeight + heightOffset;
if (ta.scrollHeight === 0) {
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
ta.style.height = originalHeight;
return;
}
ta.style.height = endHeight + "px";
// used to check if an update is actually necessary on window.resize
clientWidth = ta.clientWidth;
// prevents scroll-position jumping
document.documentElement.scrollTop = htmlTop;
document.body.scrollTop = bodyTop;
}
function update() {
const startHeight = ta.style.height;
resize();
const style = window.getComputedStyle(ta, null);
if (style.height !== ta.style.height) {
if (overflowY !== "visible") {
changeOverflow("visible");
}
} else {
if (overflowY !== "hidden") {
changeOverflow("hidden");
}
}
if (startHeight !== ta.style.height) {
const event = new Event("autosize:resized", {
bubbles: true,
cancelable: false,
});
ta.dispatchEvent(event);
}
}
const pageResize = () => {
if (ta.clientWidth !== clientWidth) {
update();
}
};
const destroy = (style) => {
window.removeEventListener("resize", pageResize, false);
ta.removeEventListener("input", update, false);
ta.removeEventListener("keyup", update, false);
ta.removeEventListener("autosize:destroy", destroy, false);
ta.removeEventListener("autosize:update", update, false);
set.delete(ta);
Object.keys(style).forEach((key) => {
ta.style[key] = style[key];
});
};
ta.addEventListener("autosize:destroy", destroy, false);
// IE9 does not fire onpropertychange or oninput for deletions,
// so binding to onkeyup to catch most of those events.
// There is no way that I know of to detect something like 'cut' in IE9.
if ("onpropertychange" in ta && "oninput" in ta) {
ta.addEventListener("keyup", update, false);
}
window.addEventListener("resize", pageResize, false);
ta.addEventListener("input", update, false);
ta.addEventListener("autosize:update", update, false);
set.add(ta);
if (setOverflowX) {
ta.style.overflowX = "hidden";
ta.style.wordWrap = "break-word";
}
init();
}
function exportDestroy(ta) {
if (!(ta && ta.nodeName && ta.nodeName === "TEXTAREA")) {
return;
}
const event = new Event("autosize:destroy", {
bubbles: true,
cancelable: false,
});
ta.dispatchEvent(event);
}
function exportUpdate(ta) {
if (!(ta && ta.nodeName && ta.nodeName === "TEXTAREA")) {
return;
}
const event = new Event("autosize:update", {
bubbles: true,
cancelable: false,
});
ta.dispatchEvent(event);
}
let autosize = (el, options) => {
if (el) {
Array.prototype.forEach.call(el.length ? el : [el], (x) =>
assign(x, options)
);
}
return el;
};
autosize.destroy = (el) => {
if (el) {
Array.prototype.forEach.call(el.length ? el : [el], exportDestroy);
}
return el;
};
autosize.update = (el) => {
if (el) {
Array.prototype.forEach.call(el.length ? el : [el], exportUpdate);
}
return el;
};
export default autosize;

View File

@ -59,6 +59,7 @@
"@uppy/xhr-upload": "3.1.1",
"a11y-dialog": "8.0.4",
"admin": "1.0.0",
"autosize": "^6.0.1",
"babel-import-util": "^2.0.1",
"babel-plugin-ember-template-compilation": "^2.2.1",
"bootstrap": "3.4.1",

View File

@ -3084,6 +3084,11 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autosize@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/autosize/-/autosize-6.0.1.tgz#64ee78dd7029be959eddd3afbbd33235b957e10f"
integrity sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==
available-typed-arrays@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"