mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
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:
parent
115a05f37a
commit
334be4eac7
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user