fix size observer for content-box

This commit is contained in:
Rene
2020-12-27 13:39:51 +01:00
parent aaa87e208a
commit fca2dff869
8 changed files with 38 additions and 19 deletions
@@ -57,7 +57,7 @@ export const createLifecycleBase = <O, C>(
const cacheOptions = createCache<O>(options, true); const cacheOptions = createCache<O>(options, true);
const update = (hints: LifecycleUpdateHints<O, C>) => { const update = (hints: LifecycleUpdateHints<O, C>) => {
const hasForce = isBoolean(hints._force); const hasForce = isBoolean(hints._force); // indication that it was called from outside
const force = hints._force === true; const force = hints._force === true;
const changedCache = cacheChange(force ? null : hints._changedCache || (hasForce ? null : []), force); const changedCache = cacheChange(force ? null : hints._changedCache || (hasForce ? null : []), force);
@@ -29,7 +29,7 @@ export const createStructureLifecycle = (
target: OSTargetObject, target: OSTargetObject,
initialOptions?: StructureLifecycleOptions initialOptions?: StructureLifecycleOptions
): Lifecycle<StructureLifecycleOptions> => { ): Lifecycle<StructureLifecycleOptions> => {
const { host, viewport, content } = target; const { host, padding: paddingElm, viewport, content } = target;
const destructFns: (() => any)[] = []; const destructFns: (() => any)[] = [];
const env: Environment = getEnvironment(); const env: Environment = getEnvironment();
const scrollbarsOverlaid = env._nativeScrollbarIsOverlaid; const scrollbarsOverlaid = env._nativeScrollbarIsOverlaid;
@@ -74,7 +74,7 @@ export const createStructureLifecycle = (
paddingStyle.b -= env._nativeScrollbarSize.x; paddingStyle.b -= env._nativeScrollbarSize.x;
} }
style(viewport, { top: paddingStyle.t, left: paddingStyle.l, 'margin-right': paddingStyle.r, 'margin-bottom': paddingStyle.b }); style(paddingElm, { top: paddingStyle.t, left: paddingStyle.l, 'margin-right': paddingStyle.r, 'margin-bottom': paddingStyle.b });
} }
console.log(options); // eslint-disable-line console.log(options); // eslint-disable-line
@@ -25,6 +25,7 @@ const ResizeObserverConstructor = jsAPI('ResizeObserver');
const classNameSizeObserver = 'os-size-observer'; const classNameSizeObserver = 'os-size-observer';
const classNameSizeObserverAppear = `${classNameSizeObserver}-appear`; const classNameSizeObserverAppear = `${classNameSizeObserver}-appear`;
const classNameSizeObserverListener = `${classNameSizeObserver}-listener`; const classNameSizeObserverListener = `${classNameSizeObserver}-listener`;
const classNameSizeObserverListenerScroll = `${classNameSizeObserverListener}-scroll`;
const classNameSizeObserverListenerItem = `${classNameSizeObserverListener}-item`; const classNameSizeObserverListenerItem = `${classNameSizeObserverListener}-item`;
const classNameSizeObserverListenerItemFinal = `${classNameSizeObserverListenerItem}-final`; const classNameSizeObserverListenerItemFinal = `${classNameSizeObserverListenerItem}-final`;
const cAF = cancelAnimationFrame; const cAF = cancelAnimationFrame;
@@ -65,6 +66,7 @@ export const createSizeObserver = (
`<div class="${classNameSizeObserverListenerItem}" dir="ltr"><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}"></div></div><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}" style="width: 200%; height: 200%"></div></div></div>` `<div class="${classNameSizeObserverListenerItem}" dir="ltr"><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}"></div></div><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}" style="width: 200%; height: 200%"></div></div></div>`
); );
appendChildren(listenerElement, observerElementChildren); appendChildren(listenerElement, observerElementChildren);
addClass(listenerElement, classNameSizeObserverListenerScroll);
const observerElementChildrenRoot = observerElementChildren[0] as HTMLElement; const observerElementChildrenRoot = observerElementChildren[0] as HTMLElement;
const shrinkElement = observerElementChildrenRoot.lastChild as HTMLElement; const shrinkElement = observerElementChildrenRoot.lastChild as HTMLElement;
const expandElement = observerElementChildrenRoot.firstChild as HTMLElement; const expandElement = observerElementChildrenRoot.firstChild as HTMLElement;
@@ -6,6 +6,7 @@ import { createTrinsicObserver } from 'observers/trinsicObserver';
import { Lifecycle } from 'lifecycles/lifecycleBase'; import { Lifecycle } from 'lifecycles/lifecycleBase';
const classNameHost = 'os-host'; const classNameHost = 'os-host';
const classNamePadding = 'os-padding';
const classNameViewport = 'os-viewport'; const classNameViewport = 'os-viewport';
const classNameContent = 'os-content'; const classNameContent = 'os-content';
@@ -13,25 +14,29 @@ const normalizeTarget = (target: OSTarget): OSTargetObject => {
if (isHTMLElement(target)) { if (isHTMLElement(target)) {
const isTextarea = is(target, 'textarea'); const isTextarea = is(target, 'textarea');
const host = (isTextarea ? createDiv() : target) as HTMLElement; const host = (isTextarea ? createDiv() : target) as HTMLElement;
const padding = createDiv(classNamePadding);
const viewport = createDiv(classNameViewport); const viewport = createDiv(classNameViewport);
const content = createDiv(classNameContent); const content = createDiv(classNameContent);
appendChildren(padding, viewport);
appendChildren(viewport, content); appendChildren(viewport, content);
appendChildren(content, contents(target)); appendChildren(content, contents(target));
appendChildren(target, viewport); appendChildren(target, padding);
addClass(host, classNameHost); addClass(host, classNameHost);
return { return {
target, target,
host, host,
padding,
viewport, viewport,
content, content,
}; };
} }
const { host, viewport, content } = target; const { host, padding, viewport, content } = target;
addClass(host, classNameHost); addClass(host, classNameHost);
addClass(padding, classNamePadding);
addClass(viewport, classNameViewport); addClass(viewport, classNameViewport);
addClass(content, classNameContent); addClass(content, classNameContent);
@@ -34,9 +34,8 @@ $scrollbar-cushion: 100px;
.os-size-observer-listener { .os-size-observer-listener {
display: block; display: block;
height: 200%; height: 500%;
width: 200%; width: 500%;
box-sizing: content-box;
// lets assume no scrollbar is 100px wide // lets assume no scrollbar is 100px wide
& > .os-size-observer-listener-item { & > .os-size-observer-listener-item {
@@ -47,6 +46,10 @@ $scrollbar-cushion: 100px;
} }
} }
.os-size-observer-listener-scroll {
box-sizing: content-box;
}
.os-size-observer-listener-item { .os-size-observer-listener-item {
right: 0; right: 0;
bottom: 0; bottom: 0;
@@ -1,10 +1,12 @@
.os-host { .os-host,
.os-padding {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
} }
.os-padding,
.os-viewport { .os-viewport {
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
@@ -15,6 +17,8 @@
margin: 0; margin: 0;
border: none; border: none;
overflow: visible; overflow: visible;
max-width: 100%;
z-index: 0;
} }
.os-content { .os-content {
@@ -5,6 +5,7 @@ export type OSTargetElement = HTMLElement | HTMLTextAreaElement;
export interface OSTargetObject { export interface OSTargetObject {
target: OSTargetElement; target: OSTargetElement;
host: HTMLElement; host: HTMLElement;
padding: HTMLElement;
viewport: HTMLElement; viewport: HTMLElement;
content: HTMLElement; content: HTMLElement;
} }
@@ -10,16 +10,16 @@ import { createSizeObserver } from 'observers/sizeObserver';
let sizeIterations = 0; let sizeIterations = 0;
let directionIterations = 0; let directionIterations = 0;
const contentBox = (elm: HTMLElement | null) => { const contentBox = (elm: HTMLElement | null): WH<number> => {
if (elm) { if (elm) {
const computedStyle = window.getComputedStyle(elm); const computedStyle = window.getComputedStyle(elm);
return { return {
width: elm.clientWidth - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)), w: elm.clientWidth - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
height: elm.clientHeight - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)), h: elm.clientHeight - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
}; };
} }
return { width: 0, height: 0 }; return { w: 0, h: 0 };
}; };
const targetElm = document.querySelector('#target'); const targetElm = document.querySelector('#target');
@@ -39,6 +39,7 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
currSizeIterations: number; currSizeIterations: number;
currDirectionIterations: number; currDirectionIterations: number;
currOffsetSize: WH<number>; currOffsetSize: WH<number>;
currContentSize: WH<number>;
currDir: string; currDir: string;
} }
@@ -47,36 +48,39 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
const currSizeIterations = sizeIterations; const currSizeIterations = sizeIterations;
const currDirectionIterations = directionIterations; const currDirectionIterations = directionIterations;
const currOffsetSize = offsetSize(targetElm as HTMLElement); const currOffsetSize = offsetSize(targetElm as HTMLElement);
const currContentSize = contentBox(targetElm as HTMLElement);
const currDir = style(targetElm as HTMLElement, 'direction'); const currDir = style(targetElm as HTMLElement, 'direction');
return { return {
currSizeIterations, currSizeIterations,
currDirectionIterations, currDirectionIterations,
currOffsetSize, currOffsetSize,
currContentSize,
currDir, currDir,
}; };
}, },
async check({ currSizeIterations, currDirectionIterations, currOffsetSize, currDir }) { async check({ currSizeIterations, currDirectionIterations, currOffsetSize, currContentSize, currDir }) {
const newOffsetSize = offsetSize(targetElm as HTMLElement); const newOffsetSize = offsetSize(targetElm as HTMLElement);
const newContentSize = contentBox(targetElm as HTMLElement);
const newDir = style(targetElm as HTMLElement, 'direction'); const newDir = style(targetElm as HTMLElement, 'direction');
const offsetSizeChanged = currOffsetSize.w !== newOffsetSize.w || currOffsetSize.h !== newOffsetSize.h; const offsetSizeChanged = currOffsetSize.w !== newOffsetSize.w || currOffsetSize.h !== newOffsetSize.h;
const contentSizeChanged = currContentSize.w !== newContentSize.w || currContentSize.h !== newContentSize.h;
const dirChanged = currDir !== newDir; const dirChanged = currDir !== newDir;
const dimensions = hasDimensions(targetElm as HTMLElement); const dimensions = hasDimensions(targetElm as HTMLElement);
const contentSize = contentBox(targetElm as HTMLElement);
const observerElm = targetElm?.firstElementChild as HTMLElement; const observerElm = targetElm?.firstElementChild as HTMLElement;
// no overflow if not needed // no overflow if not needed
if (targetElm && contentSize.width > 0) { if (targetElm && newContentSize.w > 0) {
should.ok(observerElm.getBoundingClientRect().right <= targetElm.getBoundingClientRect().right); should.ok(observerElm.getBoundingClientRect().right <= targetElm.getBoundingClientRect().right);
} }
if (targetElm && contentSize.height > 0) { if (targetElm && newContentSize.h > 0) {
should.ok(observerElm.getBoundingClientRect().bottom <= targetElm.getBoundingClientRect().bottom); should.ok(observerElm.getBoundingClientRect().bottom <= targetElm.getBoundingClientRect().bottom);
} }
if (dimensions && (offsetSizeChanged || dirChanged)) { if (dimensions && (offsetSizeChanged || contentSizeChanged || dirChanged)) {
await waitFor( await waitFor(
() => { () => {
if (offsetSizeChanged) { if (offsetSizeChanged || contentSizeChanged) {
should.equal(sizeIterations, currSizeIterations + 1); should.equal(sizeIterations, currSizeIterations + 1);
} }
if (dirChanged) { if (dirChanged) {