mirror of
https://github.com/koloml/philomena-tagging-assistant.git
synced 2026-05-09 07:12:19 +00:00
Extracted CLS compensation logic into separate method
This commit is contained in:
@@ -9,8 +9,8 @@ import { EVENT_PRESET_TAG_CHANGE_APPLIED, type PresetTagChange } from "$content/
|
||||
export class TagsForm extends BaseComponent {
|
||||
#togglePresetsButton: HTMLButtonElement = document.createElement('button');
|
||||
#presetsList = EditorPresetsBlock.create();
|
||||
#plainEditorTextarea: HTMLTextAreaElement|null = null;
|
||||
#fancyEditorInput: HTMLInputElement|null = null;
|
||||
#plainEditorTextarea: HTMLTextAreaElement | null = null;
|
||||
#fancyEditorInput: HTMLInputElement | null = null;
|
||||
#tagsSet: Set<string> = new Set();
|
||||
|
||||
protected build() {
|
||||
@@ -173,7 +173,7 @@ export class TagsForm extends BaseComponent {
|
||||
|
||||
#onTagChangeRequested(event: CustomEvent<PresetTagChange>) {
|
||||
const targetElement = event.target instanceof HTMLElement ? event.target : null;
|
||||
const { addedTags = null, removedTags = null } = event.detail;
|
||||
const {addedTags = null, removedTags = null} = event.detail;
|
||||
let tagChangeList: string[] = [];
|
||||
|
||||
if (addedTags) {
|
||||
@@ -188,31 +188,33 @@ export class TagsForm extends BaseComponent {
|
||||
);
|
||||
}
|
||||
|
||||
const containerOffset = this.#presetsList.container.offsetTop;
|
||||
const presetOffset = targetElement?.offsetTop || 0;
|
||||
|
||||
this.#applyTagChangesWithFancyTagEditor(
|
||||
tagChangeList.join(',')
|
||||
this.#executeAndCompensateForLayoutShift(
|
||||
() => this.#applyTagChangesWithFancyTagEditor(tagChangeList.join(',')),
|
||||
[this.#presetsList.container, targetElement],
|
||||
);
|
||||
}
|
||||
|
||||
const containerOffsetDifference = this.#presetsList.container.offsetTop - containerOffset;
|
||||
let presetOffsetDifference = (targetElement?.offsetTop || 0) - presetOffset;
|
||||
#executeAndCompensateForLayoutShift(executeOperation: () => void, elements: (HTMLElement | null)[]) {
|
||||
const offsetsListBefore = TagsForm.#gatherOffsetsFromElements(elements);
|
||||
executeOperation();
|
||||
const offsetsListAfter = TagsForm.#gatherOffsetsFromElements(elements);
|
||||
|
||||
// If target element is no longer visible, then there is no need to apply scrolling fix, otherwise it will shift
|
||||
// user up. Invisible elements are always report 0 offset.
|
||||
if (targetElement?.checkVisibility() === false) {
|
||||
presetOffsetDifference = 0;
|
||||
const resultDifference = offsetsListAfter
|
||||
.map((offsetAfter, index) =>
|
||||
offsetAfter !== null && offsetsListBefore[index] !== null
|
||||
? offsetAfter - offsetsListBefore[index]
|
||||
: null)
|
||||
.filter(difference => difference !== null)
|
||||
.reduce((summary, difference) => summary + difference, 0);
|
||||
|
||||
if (resultDifference === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compensating for the layout shift: when user clicks on a tag (or on "add/remove all tags"), tag editor might
|
||||
// overflow the current line and wrap tags around to the next line, causing presets section to shift. We need to
|
||||
// avoid that for better UX.
|
||||
if (containerOffsetDifference !== 0 || presetOffsetDifference !== 0) {
|
||||
window.scrollTo({
|
||||
top: window.scrollY + containerOffsetDifference + presetOffsetDifference,
|
||||
behavior: 'instant',
|
||||
});
|
||||
}
|
||||
window.scrollTo({
|
||||
top: scrollY + resultDifference,
|
||||
behavior: 'instant',
|
||||
})
|
||||
}
|
||||
|
||||
#applyTagChangesWithFancyTagEditor(tagsListWithChanges: string): void {
|
||||
@@ -241,7 +243,7 @@ export class TagsForm extends BaseComponent {
|
||||
this.refreshTagColors();
|
||||
}
|
||||
|
||||
#onPlainEditorReloadRequested(event: CustomEvent<ReloadCustomOptions|null>) {
|
||||
#onPlainEditorReloadRequested(event: CustomEvent<ReloadCustomOptions | null>) {
|
||||
if (!event.detail?.skipTagColorRefresh) {
|
||||
this.refreshTagColors();
|
||||
}
|
||||
@@ -251,6 +253,14 @@ export class TagsForm extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
static #gatherOffsetsFromElements(elements: (HTMLElement | null)[]): (number | null)[] {
|
||||
return elements.map(
|
||||
maybeElement => maybeElement?.checkVisibility()
|
||||
? maybeElement?.offsetTop
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
static watchForEditors() {
|
||||
document.body.addEventListener('click', event => {
|
||||
const targetElement = event.target;
|
||||
|
||||
Reference in New Issue
Block a user