Skip to content

Commit

Permalink
feat(esl-popup): add popup flipping and hiding by detection intersect…
Browse files Browse the repository at this point in the history
…ion side
  • Loading branch information
dshovchko committed Jul 28, 2021
1 parent 907ce47 commit a213262
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 10 deletions.
8 changes: 5 additions & 3 deletions pages/views/drafts/esl-popup.njk
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ tags: drafts
<section class="row">
<div id="a" style="margin: 599px 0; height: 800px; overflow-y: auto; width: 100%; background-color: azure;">
<p>Lorem</p><p>ipsum</p><p>dot</p><p>color</p>
<esl-trigger target="#popup1" mode="toggle" class="btn btn-primary"></esl-trigger>
<esl-trigger target="#popup1" mode="toggle" class="btn btn-primary">Trigger B</esl-trigger>
<div id="b" style="margin: 599px 0; height: 650px; overflow-y: auto; width: 100%; background-color: beige;">
<p>Lorem</p><p>ipsum</p><p>dot</p><p>color</p>
<esl-trigger target="#popup1" mode="toggle" class="btn btn-primary">Trigger B</esl-trigger>
<esl-trigger target="#popup1" mode="toggle" class="btn btn-secondary">Trigger B</esl-trigger>
<div id="c" style="margin: 599px 0; height: 500px; overflow-y: auto; width: 100%; background-color: bisque;">
<p>Lorem</p><p>ipsum</p><p>dot</p><p>color</p>
<esl-trigger target="#popup1" mode="toggle" class="btn btn-primary">Trigger C</esl-trigger>
<div id="d" style="margin: 599px 0; height: 350px; overflow-y: auto; width: 100%; background-color: tan;">
<p>Lorem</p><p>ipsum</p><p>dot</p><p>color</p>
<esl-trigger target="#popup1" mode="toggle" class="btn btn-primary">Trigger D (top)</esl-trigger>
<esl-trigger target="#popup4" mode="toggle" class="btn btn-primary">Trigger D (right)</esl-trigger>
<esl-trigger target="#popup2" mode="toggle" class="btn btn-primary">Trigger D (bottom)</esl-trigger>
<esl-trigger target="#popup3" mode="toggle" class="btn btn-secondary">Trigger D (left)</esl-trigger>
<esl-trigger target="#popup4" mode="toggle" class="btn btn-secondary">Trigger D (right)</esl-trigger>
<esl-popup id="popup9" position="top">Exadel Smart Library Popup content<span class="esl-popup-arrow"></span></esl-popup>
</div>
</div>
Expand Down
16 changes: 12 additions & 4 deletions src/modules/esl-popup/core/calcPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@ export interface ElementRect extends ObjectRect {
cy: number;
}

export interface IntersetionRatioRect {
top?: number;
left?: number;
right?: number;
bottom?: number;
}

export interface PopupPositionConfig {
position: PositionType;
behavior: string;
intersectionRatio: IntersetionRatioRect,
element: DOMRect;
inner: ElementRect;
outer: ObjectRect;
Expand Down Expand Up @@ -95,22 +103,22 @@ function fitByMajorAxis(cfg: PopupPositionConfig, rect: BasicRect, arrow: ArrowP

switch (cfg.position) {
case 'bottom':
if (cfg.outer.bottom < rect.bottom) {
if (cfg.intersectionRatio.bottom || cfg.outer.bottom < rect.bottom) {
oppositeOnMajor('bottom', cfg.inner.top, -cfg.element.height, rect, arrow);
}
break;
case 'left':
if (rect.left < cfg.outer.left) {
if (cfg.intersectionRatio.left || rect.left < cfg.outer.left) {
oppositeOnMajor('left', cfg.inner.right, cfg.element.width, rect, arrow);
}
break;
case 'right':
if (cfg.outer.right < rect.right) {
if (cfg.intersectionRatio.right || cfg.outer.right < rect.right) {
oppositeOnMajor('right', cfg.inner.left, -cfg.element.width, rect, arrow);
}
break;
default:
if (rect.top < cfg.outer.top) {
if (cfg.intersectionRatio.top || rect.top < cfg.outer.top) {
oppositeOnMajor('top', cfg.inner.bottom, cfg.element.height, rect, arrow);
}
}
Expand Down
44 changes: 41 additions & 3 deletions src/modules/esl-popup/core/esl-popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {listScrollParents} from './listScrollParents';
import {calcPopupPosition, resizeRect} from './calcPosition';

import type {ToggleableActionParams} from '../../esl-toggleable/core';
import type {PositionType} from './calcPosition';
import type {PositionType, IntersetionRatioRect} from './calcPosition';

const INTERSECTION_LIMIT_FOR_ADJACENT_AXIS = 0.7;

export interface PopupActionParams extends ToggleableActionParams {
/** popup position relative to trigger */
Expand Down Expand Up @@ -37,6 +39,7 @@ export class ESLPopup extends ESLToggleable {
protected _offsetWindow: number;
protected _deferredUpdatePosition = rafDecorator(() => this._updatePosition());
protected _activatorObserver: ActivatorObserver;
protected _intersectionRatio: IntersetionRatioRect = {};

@attr({defaultValue: 'top'}) public position: PositionType;
@attr({defaultValue: 'fit'}) public behavior: string;
Expand Down Expand Up @@ -71,6 +74,14 @@ export class ESLPopup extends ESLToggleable {
window.removeEventListener('scroll', this._deferredUpdatePosition);
}

protected get _isPositioningAlongHorizontal() {
return ['left', 'right'].includes(this.position);
}

protected get _isPositioningAlongVertical() {
return ['top', 'bottom'].includes(this.position);
}

// TODO: move to utilities
protected get _windowWidth() {
// return document.documentElement.clientWidth || document.body.clientWidth;
Expand Down Expand Up @@ -122,10 +133,36 @@ export class ESLPopup extends ESLToggleable {
this.activator && this._removeActivatorObserver(this.activator);
}

protected _checkIntersectionForAdjacentAxis(isAdjacentAxis: boolean, intersectionRatio: number) {
if (isAdjacentAxis && intersectionRatio < INTERSECTION_LIMIT_FOR_ADJACENT_AXIS) {
this.hide();
}
}

@bind
protected onActivatorIntersection(entries: IntersectionObserverEntry[], observer: IntersectionObserver) {
if (!entries[0].isIntersecting) {
const entry = entries[0];
this._intersectionRatio = {};
if (!entry.isIntersecting) {
this.hide();
return;
}

if (entry.intersectionRect.top !== entry.boundingClientRect.top) {
this._intersectionRatio.top = entry.intersectionRect.height / entry.boundingClientRect.height;
this._checkIntersectionForAdjacentAxis(this._isPositioningAlongHorizontal, this._intersectionRatio.top);
}
if (entry.intersectionRect.bottom !== entry.boundingClientRect.bottom) {
this._intersectionRatio.bottom = entry.intersectionRect.height / entry.boundingClientRect.height;
this._checkIntersectionForAdjacentAxis(this._isPositioningAlongHorizontal, this._intersectionRatio.bottom);
}
if (entry.intersectionRect.left !== entry.boundingClientRect.left) {
this._intersectionRatio.left = entry.intersectionRect.width / entry.boundingClientRect.width;
this._checkIntersectionForAdjacentAxis(this._isPositioningAlongVertical, this._intersectionRatio.left);
}
if (entry.intersectionRect.right !== entry.boundingClientRect.right) {
this._intersectionRatio.right = entry.intersectionRect.width / entry.boundingClientRect.width;
this._checkIntersectionForAdjacentAxis(this._isPositioningAlongVertical, this._intersectionRatio.right);
}
}

Expand All @@ -147,7 +184,7 @@ export class ESLPopup extends ESLToggleable {

const options = {
rootMargin: '0px',
threshold: [0, 1]
threshold: [...Array(9).keys()].map((x) => x/8)
} as IntersectionObserverInit;

const observer = new IntersectionObserver(this.onActivatorIntersection, options);
Expand Down Expand Up @@ -197,6 +234,7 @@ export class ESLPopup extends ESLToggleable {
const config = {
position: this.position,
behavior: this.behavior,
intersectionRatio: this._intersectionRatio,
element: popupRect,
trigger,
inner: resizeRect(trigger, innerMargin),
Expand Down

0 comments on commit a213262

Please sign in to comment.