Add 'scrollIntoView' to baseUIElement, autoscroll questions into view when appropriate

This commit is contained in:
Pieter Vander Vennet 2022-12-06 03:43:54 +01:00
parent aec5ffba79
commit 9e97eba519
4 changed files with 78 additions and 13 deletions

View file

@ -3,6 +3,8 @@
*
* Assumes a read-only configuration, so it has no 'ListenTo'
*/
import {Utils} from "../Utils";
export default abstract class BaseUIElement {
protected _constructedHtmlElement: HTMLElement
protected isDestroyed = false
@ -41,6 +43,34 @@ export default abstract class BaseUIElement {
this._constructedHtmlElement?.scrollTo(0, 0)
}
public ScrollIntoView(options?: {
onlyIfPartiallyHidden?: boolean
}) {
if(this._constructedHtmlElement === undefined){
return
}
let alignToTop = true;
if(options?.onlyIfPartiallyHidden){
// Is the element completely in the view?
const parentRect = Utils.findParentWithScrolling(this._constructedHtmlElement.parentElement).getBoundingClientRect();
const elementRect = this._constructedHtmlElement.getBoundingClientRect();
// Check if the element is within the vertical bounds of the parent element
const topIsVisible = elementRect.top >= parentRect.top
const bottomIsVisible = elementRect.bottom <= parentRect.bottom
const inView = topIsVisible && bottomIsVisible ;
if(inView){
return
}
if(topIsVisible){
alignToTop = false
}
}
this._constructedHtmlElement?.scrollIntoView({
behavior: "smooth",
block: "start"
})
}
/**
* Adds all the relevant classes, space separated
*/

View file

@ -1,16 +1,15 @@
import { UIEventSource } from "../../Logic/UIEventSource"
import {UIEventSource} from "../../Logic/UIEventSource"
import TagRenderingQuestion from "./TagRenderingQuestion"
import Translations from "../i18n/Translations"
import Combine from "../Base/Combine"
import TagRenderingAnswer from "./TagRenderingAnswer"
import Svg from "../../Svg"
import Toggle from "../Input/Toggle"
import BaseUIElement from "../BaseUIElement"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import { Unit } from "../../Models/Unit"
import {Unit} from "../../Models/Unit"
import Lazy from "../Base/Lazy"
import { FixedUiElement } from "../Base/FixedUiElement"
import { EditButton } from "./SaveButton"
import {FixedUiElement} from "../Base/FixedUiElement"
import {EditButton} from "./SaveButton"
export default class EditableTagRendering extends Toggle {
constructor(
@ -31,10 +30,9 @@ export default class EditableTagRendering extends Toggle {
configuration.IsKnown(tags) &&
(configuration?.condition?.matchesProperties(tags) ?? true)
)
const editMode = options.editMode ?? new UIEventSource<boolean>(false)
super(
new Lazy(() => {
const editMode = options.editMode ?? new UIEventSource<boolean>(false)
let rendering = EditableTagRendering.CreateRendering(
state,
tags,
@ -54,6 +52,13 @@ export default class EditableTagRendering extends Toggle {
undefined,
renderingIsShown
)
const self = this;
editMode.addCallback(editing => {
if(editing){
console.log("Scrolling etr into view")
self.ScrollIntoView()
}
})
}
private static CreateRendering(
@ -69,12 +74,6 @@ export default class EditableTagRendering extends Toggle {
if (configuration.question !== undefined && state?.featureSwitchUserbadge?.data) {
// We have a question and editing is enabled
const answerWithEditButton = new Combine([
answer,
new EditButton(state.osmConnection, () => {
editMode.setData(true)
}),
]).SetClass("flex justify-between w-full")
const question = new Lazy(
() =>
@ -92,7 +91,18 @@ export default class EditableTagRendering extends Toggle {
})
)
const answerWithEditButton = new Combine([
answer,
new EditButton(state.osmConnection, () => {
editMode.setData(true)
question.ScrollIntoView({
onlyIfPartiallyHidden:true
})
}),
]).SetClass("flex justify-between w-full")
rendering = new Toggle(question, answerWithEditButton, editMode)
}
return rendering
}

View file

@ -33,6 +33,8 @@ export default class QuestionBox extends VariableUiElement {
.filter((tr) => tr.question !== undefined)
.filter((tr) => tr.question !== null)
let focus: () => void = () => {};
const tagRenderingQuestions = tagRenderings.map(
(tagRendering, i) =>
new Lazy(
@ -42,6 +44,7 @@ export default class QuestionBox extends VariableUiElement {
afterSave: () => {
// We save and indicate progress by pinging and recalculating
skippedQuestions.ping()
focus()
},
cancelButton: Translations.t.general.skip
.Clone()
@ -49,6 +52,8 @@ export default class QuestionBox extends VariableUiElement {
.onClick(() => {
skippedQuestions.data.push(i)
skippedQuestions.ping()
focus()
}),
})
)
@ -136,5 +141,8 @@ export default class QuestionBox extends VariableUiElement {
this.skippedQuestions = skippedQuestions
this.restingQuestions = questionsToAsk
focus = () => this.ScrollIntoView({
onlyIfPartiallyHidden: true
})
}
}

View file

@ -1,4 +1,5 @@
import * as colors from "./assets/colors.json"
import HTML = Mocha.reporters.HTML;
export class Utils {
/**
@ -1221,4 +1222,20 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
d.setUTCMilliseconds(0)
d.setUTCMinutes(0)
}
public static findParentWithScrolling(element: HTMLElement): HTMLElement {
// Check if the element itself has scrolling
if (element.scrollHeight > element.clientHeight) {
return element;
}
// If the element does not have scrolling, check if it has a parent element
if (!element.parentElement) {
return null;
}
// If the element has a parent, repeat the process for the parent element
return Utils.findParentWithScrolling(element.parentElement);
}
}