mirror of
https://github.com/rio-labs/rio.git
synced 2026-05-05 02:19:20 -05:00
fix scrollContainer again and implement sticky_bottom
This commit is contained in:
@@ -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 = `${
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user