UX: improve quote/edit bar's rendering (#24097)

This change allows for a faster and smoother experience. It's also less noisy because the buttons will not be shown while selecting text.
This commit is contained in:
Joffrey JAFFEUX 2023-10-25 18:51:47 +02:00 committed by GitHub
parent 115a05f37a
commit 334be4eac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,4 +1,5 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { cached, tracked } from "@glimmer/tracking";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { cancel } from "@ember/runloop"; import { cancel } from "@ember/runloop";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
@ -44,7 +45,9 @@ export default class PostTextSelection extends Component {
@service siteSettings; @service siteSettings;
@service menu; @service menu;
prevSelection; @tracked isSelecting = false;
prevSelectedText;
runLoopHandlers = modifier(() => { runLoopHandlers = modifier(() => {
return () => { return () => {
@ -86,17 +89,24 @@ export default class PostTextSelection extends Component {
@bind @bind
async selectionChanged() { async selectionChanged() {
if (this.isSelecting) {
return;
}
const _selectedText = selectedText(); const _selectedText = selectedText();
// avoid hard loops in quote selection unconditionally // avoid hard loops in quote selection unconditionally
// this can happen if you triple click text in firefox // this can happen if you triple click text in firefox
// it's also generally unecessary work to go // it's also generally unecessary work to go
// through this if the selection hasn't changed // through this if the selection hasn't changed
if (this.menuInstance?.expanded && this.prevSelection === _selectedText) { if (
this.menuInstance?.expanded &&
this.prevSelectedText === _selectedText
) {
return; return;
} }
this.prevSelection = _selectedText; this.prevSelectedText = _selectedText;
const selection = window.getSelection(); const selection = window.getSelection();
if (selection.isCollapsed) { if (selection.isCollapsed) {
@ -182,18 +192,15 @@ export default class PostTextSelection extends Component {
} }
} }
// on Desktop, shows the button at the beginning of the selection
// on Mobile, shows the button at the end of the selection
const { isIOS, isAndroid, isOpera } = this.capabilities;
const showAtEnd = this.site.isMobileDevice || isIOS || isAndroid || isOpera;
const options = { const options = {
identifier: "post-text-selection-toolbar",
component: PostTextSelectionToolbar, component: PostTextSelectionToolbar,
inline: true, inline: true,
placement: showAtEnd ? "bottom-start" : "top-start", placement: this.shouldRenderUnder ? "bottom-start" : "top-start",
fallbackPlacements: showAtEnd fallbackPlacements: this.shouldRenderUnder
? ["bottom-end", "top-start"] ? ["bottom-end", "top-start"]
: ["bottom-start"], : ["bottom-start"],
offset: showAtEnd ? 25 : 3, offset: this.shouldRenderUnder ? 25 : 3,
trapTab: false, trapTab: false,
data: { data: {
canEditPost: this.canEditPost, canEditPost: this.canEditPost,
@ -206,7 +213,7 @@ export default class PostTextSelection extends Component {
}, },
}; };
this.menuInstance?.destroy(); await this.menuInstance?.destroy();
this.menuInstance = await this.menu.show( this.menuInstance = await this.menu.show(
virtualElementFromTextRange(), virtualElementFromTextRange(),
@ -217,7 +224,7 @@ export default class PostTextSelection extends Component {
@bind @bind
onSelectionChanged() { onSelectionChanged() {
const { isIOS, isWinphone, isAndroid } = this.capabilities; const { isIOS, isWinphone, isAndroid } = this.capabilities;
const wait = isIOS || isWinphone || isAndroid ? INPUT_DELAY : 100; const wait = isIOS || isWinphone || isAndroid ? INPUT_DELAY : 25;
this.selectionChangeHandler = discourseDebounce( this.selectionChangeHandler = discourseDebounce(
this, this,
this.selectionChanged, this.selectionChanged,
@ -233,10 +240,14 @@ export default class PostTextSelection extends Component {
this.validMouseDown = false; this.validMouseDown = false;
return; return;
} }
this.isSelecting = true;
} }
@bind @bind
mouseup() { mouseup() {
this.isSelecting = false;
if (!this.validMouseDown) { if (!this.validMouseDown) {
return; return;
} }
@ -254,6 +265,14 @@ export default class PostTextSelection extends Component {
return this.siteSettings.enable_fast_edit && this.post?.can_edit; return this.siteSettings.enable_fast_edit && this.post?.can_edit;
} }
// on Desktop, shows the bar at the beginning of the selection
// on Mobile, shows the bar at the end of the selection
@cached
get shouldRenderUnder() {
const { isIOS, isAndroid, isOpera } = this.capabilities;
return this.site.isMobileDevice || isIOS || isAndroid || isOpera;
}
@action @action
async insertQuote() { async insertQuote() {
await this.args.selectText(); await this.args.selectText();