fix scrollContainer again and implement sticky_bottom

This commit is contained in:
Aran-Fey
2024-06-30 02:23:30 +02:00
parent b6dbf9cadd
commit 5d2fb85cee
4 changed files with 57 additions and 80 deletions
+5 -10
View File
@@ -1,11 +1,10 @@
import { ComponentBase, ComponentState } from './componentBase';
import { componentsById } from '../componentManagement';
import { devToolsConnector, pixelsPerRem } from '../app';
import { getDisplayableChildren } from '../devToolsTreeWalk';
import { Highlighter } from '../highlighter';
import { DevToolsConnectorComponent } from './devToolsConnector';
import { Debouncer } from '../debouncer';
import { markEventAsHandled } from '../eventHandling';
import { pixelsPerRem } from '../app';
export type LayoutDisplayState = ComponentState & {
_type_: 'LayoutDisplay-builtin';
@@ -252,15 +251,11 @@ export class LayoutDisplayComponent extends ComponentBase {
// Position the margin
let margins = childComponent.state._margin_;
let marginLeft = childLeft - margins[0];
let marginTop = childTop - margins[1];
let marginLeft = childLeft - margins[0] * pixelsPerRem;
let marginTop = childTop - margins[1] * pixelsPerRem;
marginElement.style.left = `${
marginLeft * pixelsPerRem * scalePerX
}%`;
marginElement.style.top = `${
marginTop * pixelsPerRem * scalePerY
}%`;
marginElement.style.left = `${marginLeft * scalePerX}%`;
marginElement.style.top = `${marginTop * scalePerY}%`;
// Size the margin
marginElement.style.width = `${
+18 -48
View File
@@ -1,5 +1,4 @@
import { ComponentId } from '../dataModels';
import { NaturalHeightObserver } from '../naturalSizeObservers';
import { ComponentBase, ComponentState } from './componentBase';
export type ScrollContainerState = ComponentState & {
@@ -15,23 +14,9 @@ export type ScrollContainerState = ComponentState & {
export class ScrollContainerComponent extends ComponentBase {
state: Required<ScrollContainerState>;
// This is the element where the `overflow` setting is applied
private scrollerElement: HTMLElement;
// 'auto'-scrolling in the y direction has a unique problem: Because the
// width of an element is decided before its height, the browser doesn't
// know whether a vertical scroll bar will be needed until it's too late. If
// it turns out that the parent didn't allocate enough width for the child
// *and* the vertical scroll bar, it will suddenly start scrolling in *both*
// directions. That's not what we want - we want to increase the parent's
// width instead.
//
// The workaround: Whenever the child's or parent's size changes, check if a
// vertical scroll bar is needed and set `overflow-y` to `scroll` or
// `visible` accordingly.
private childNaturalHeight: number = 0;
private resizeObserver: ResizeObserver;
private naturalHeightObserver: NaturalHeightObserver;
private childContainer: HTMLElement;
private scrollAnchor: HTMLElement;
createElement(): HTMLElement {
let element = document.createElement('div');
@@ -40,17 +25,20 @@ export class ScrollContainerComponent extends ComponentBase {
this.scrollerElement = document.createElement('div');
element.appendChild(this.scrollerElement);
this.naturalHeightObserver = new NaturalHeightObserver(
this._onChildNaturalHeightChanged.bind(this)
);
this.scrollerElement.appendChild(
this.naturalHeightObserver.outerElement
);
// `sticky_bottom` is implemented via scroll anchoring, so we need a
// column that contains the child component and the scroll anchor
let column = document.createElement('div');
this.scrollerElement.appendChild(column);
this.resizeObserver = new ResizeObserver(
this._updateScrollY.bind(this)
this.childContainer = document.createElement('div');
this.childContainer.classList.add(
'rio-scroll-container-child-container'
);
this.resizeObserver.observe(this.scrollerElement);
column.appendChild(this.childContainer);
this.scrollAnchor = document.createElement('div');
this.scrollAnchor.classList.add('rio-scroll-container-anchor');
column.appendChild(this.scrollAnchor);
// Once the layouting is done, scroll to the initial position
requestAnimationFrame(() => {
@@ -68,11 +56,6 @@ export class ScrollContainerComponent extends ComponentBase {
return element;
}
onDestruction(): void {
this.resizeObserver.disconnect();
this.naturalHeightObserver.destroy();
}
updateElement(
deltaState: ScrollContainerState,
latentComponents: Set<ComponentBase>
@@ -82,7 +65,7 @@ export class ScrollContainerComponent extends ComponentBase {
this.replaceOnlyChild(
latentComponents,
deltaState.content,
this.naturalHeightObserver.innerElement
this.childContainer
);
if (deltaState.scroll_x !== undefined) {
@@ -94,22 +77,9 @@ export class ScrollContainerComponent extends ComponentBase {
}
if (deltaState.sticky_bottom !== undefined) {
// Note: CSS has a `overflow-anchor` thing which is supposed to help
// with this, but I couldn't get it to work. I think it only works
// if new elements are added (as direct children of the scrolling
// element).
this.scrollAnchor.style.overflowAnchor = deltaState.sticky_bottom
? 'auto'
: 'none';
}
}
private _onChildNaturalHeightChanged(naturalHeight: number): void {
this.childNaturalHeight = naturalHeight;
this._updateScrollY();
}
private _updateScrollY(): void {
this.element.dataset.scrollY =
this.childNaturalHeight > this.scrollerElement.clientHeight + 1
? 'always'
: 'never';
}
}
+26 -14
View File
@@ -758,7 +758,7 @@ $rio-input-box-text-distance-from-bottom: 0.4rem; // To be aligned with the <inp
height: 1.54rem;
margin: auto;
border-radius: 4.3rem;
background-color: var(--rio-global-disabled-bg-variant);
background-color: var(--rio-local-bg-variant);
transition: 0.3s ease all;
z-index: 1;
}
@@ -1633,8 +1633,8 @@ $rio-input-box-text-distance-from-bottom: 0.4rem; // To be aligned with the <inp
//
// 2. This is where the CSS `overflow` setting is applied. We also override the
// child's size request by explicitly setting the `width`/`height` to the
// minimum size where the scroll bars are still usable. (And force the
// element to fill its parent by setting `min-width`/`min-height` to `100%`.)
// minimum size where the scroll bars are still usable. Finally, we force the
// element to fill its parent by setting `min-width`/`min-height` to `100%`.
//
// 3. For some reason, when an element scrolls horizontally, it overrides the
// width of its child, which can lead to the child being smaller than its
@@ -1680,28 +1680,40 @@ $rio-input-box-text-distance-from-bottom: 0.4rem; // To be aligned with the <inp
// this.
&[data-scroll-x='auto'] > * > *,
&[data-scroll-x='always'] > * > * {
min-width: max-content;
width: max-content;
}
// Stretch to fill the parent (Note: This is the
// .rio-scroll-container-column)
& > * > * {
min-width: 100%;
min-height: 100%;
}
// 'auto'-scrolling in the y direction has a unique problem: Because the
// width of an element is decided before its height, the browser doesn't
// know whether a vertical scroll bar will be needed until it's too late. If
// it turns out that the parent didn't allocate enough width for the child
// *and* the vertical scroll bar, it will suddenly start scrolling in *both*
// directions. That's not what we want - we want to increase the parent's
// width instead.
&[data-scroll-x='never'][data-scroll-y='auto'] > * {
scrollbar-gutter: stable;
}
}
.rio-scroll-container-column {
display: flex;
flex-direction: column;
}
// Make the user content grow (but not the anchor)
& > .rio-single-container {
flex-grow: 1;
}
.rio-scroll-container-child-container {
flex-grow: 1;
overflow-anchor: none;
}
.rio-scroll-container-anchor {
height: 1px;
overflow-anchor: none;
&.enabled {
overflow-anchor: auto;
}
}
// Markdown
+8 -8
View File
@@ -335,7 +335,7 @@ class LayoutExplainer:
else:
result.write("\n\n")
result.write(
f"Due to `align_{x_or_y}` having been given, the {target_class_name} only takes up the minimum amount of space necessary"
f"Due to `align_{x_or_y}` being set, the {target_class_name} only takes up the minimum amount of space necessary"
)
if alignment <= 0.03:
@@ -370,13 +370,6 @@ class LayoutExplainer:
f"Assign a `{axis}` to the component that is greater than its allocated {axis} of {allocated_space:.1f}"
)
# If the component has more space available than it needs, suggest using
# an alignment to shrink
if allocated_space > natural_size + 0.1:
suggest_shrink(
f"Align the component using `align_{x_or_y}`, so it only takes up its natural {axis}"
)
# If the component isn't aligned, suggest to grow it by growing its
# parent
if alignment is None:
@@ -384,5 +377,12 @@ class LayoutExplainer:
f"Increase the {axis} of the parent component, so it hands down more space to the {target_class_name}"
)
# If the component has more space available than it needs, suggest
# using an alignment to shrink
if alignment is None and allocated_space > natural_size + 0.1:
suggest_shrink(
f"Align the component using `align_{x_or_y}`, so it only takes up its natural {axis}"
)
# Done!
return result.getvalue()