Skip to content

Commit

Permalink
feat(esl-popup): add position-origin attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
dshovchko committed Nov 6, 2024
1 parent 7fdaa09 commit 6be5133
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 18 deletions.
66 changes: 50 additions & 16 deletions src/modules/esl-popup/core/esl-popup-position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Rect} from '../../esl-utils/dom/rect';
import type {Point} from '../../esl-utils/dom/point';

export type PositionType = 'top' | 'bottom' | 'left' | 'right';
export type PositionOriginType = 'inner' | 'outer';

export interface PopupPositionValue {
placedAt: PositionType;
Expand All @@ -19,6 +20,7 @@ export interface IntersectionRatioRect {

export interface PopupPositionConfig {
position: PositionType;
hasInnerOrigin: boolean;
behavior: string;
marginArrow: number;
offsetArrowRatio: number;
Expand Down Expand Up @@ -71,21 +73,21 @@ function calcPopupPositionByMinorAxis(cfg: PopupPositionConfig, centerPosition:
* @param cfg - popup position config
* */
function calcPopupBasicRect(cfg: PopupPositionConfig): Rect {
const {position, inner, element} = cfg;
const {position, inner, element, hasInnerOrigin} = cfg;
let x = isOnHorizontalAxis(position) ? 0 : calcPopupPositionByMinorAxis(cfg, inner.cx, 'width');
let y = isOnHorizontalAxis(position) ? calcPopupPositionByMinorAxis(cfg, inner.cy, 'height') : 0;
switch (cfg.position) {
switch (position) {
case 'left':
x = inner.x - element.width;
x = (hasInnerOrigin ? inner.right : inner.x) - element.width;
break;
case 'right':
x = inner.right;
x = hasInnerOrigin ? inner.x : inner.right;
break;
case 'bottom':
y = inner.bottom;
y = hasInnerOrigin ? inner.y : inner.bottom;
break;
default:
y = inner.y - element.height;
y = (hasInnerOrigin ? inner.bottom : inner.y) - element.height;
break;
}
return new Rect(x, y, element.width, element.height);
Expand Down Expand Up @@ -141,19 +143,51 @@ function fitOnMajorAxis(cfg: PopupPositionConfig, value: PopupPositionValue): Po
* @returns updated popup position value
* */
function adjustAlongMajorAxis(cfg: PopupPositionConfig, value: PopupPositionValue): PopupPositionValue {
let {popup, placedAt} = value;
const popup = isStartingSide(cfg.position)
? adjustForStartingSide(cfg, value.popup)
: adjustForEndingSide(cfg, value.popup);
const placedAt = getOppositePosition(cfg.position);
return {...value, popup, placedAt};
}

/**
* Updates popup rect to fit on major axis in case positioning on the starting side.
* @param cfg - popup position config
* @param popup - popup rect
* @returns updated popup rect
* */
function adjustForStartingSide(cfg: PopupPositionConfig, popup: Rect): Rect {
const {position, inner, hasInnerOrigin} = cfg;
let {x, y} = popup;
if (isStartingSide(cfg.position)) {
x = cfg.position === 'left' ? cfg.inner.right : x;
y = cfg.position === 'top' ? cfg.inner.bottom : y;
} else {
x = cfg.position === 'right' ? cfg.inner.x - popup.width : x;
y = cfg.position === 'bottom' ? cfg.inner.y - popup.height : y;
switch (position) {
case 'left':
x = hasInnerOrigin ? inner.x : inner.right;
break;
case 'top':
y = hasInnerOrigin ? inner.y : inner.bottom;
break;
}
popup = new Rect(x, y, popup.width, popup.height);
placedAt = getOppositePosition(cfg.position);
return new Rect(x, y, popup.width, popup.height);
}

return {...value, popup, placedAt};
/**
* Updates popup rect to fit on major axis in case positioning on the ending side.
* @param cfg - popup position config
* @param popup - popup rect
* @returns updated popup rect
* */
function adjustForEndingSide(cfg: PopupPositionConfig, popup: Rect): Rect {
const {position, inner, hasInnerOrigin} = cfg;
let {x, y} = popup;
switch (position) {
case 'right':
x = (hasInnerOrigin ? inner.right : inner.x) - popup.width;
break;
case 'bottom':
y = (hasInnerOrigin ? inner.bottom : inner.y) - popup.height;
break;
}
return new Rect(x, y, popup.width, popup.height);
}

/**
Expand Down
11 changes: 9 additions & 2 deletions src/modules/esl-popup/core/esl-popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ import {calcPopupPosition, isOnHorizontalAxis} from './esl-popup-position';
import {ESLPopupPlaceholder} from './esl-popup-placeholder';

import type {ESLToggleableActionParams} from '../../esl-toggleable/core';
import type {PositionType, IntersectionRatioRect} from './esl-popup-position';
import type {PositionType, PositionOriginType, IntersectionRatioRect} from './esl-popup-position';

const INTERSECTION_LIMIT_FOR_ADJACENT_AXIS = 0.7;
const DEFAULT_OFFSET_ARROW = 50;

export interface ESLPopupActionParams extends ESLToggleableActionParams {
/** popup position relative to trigger */
position?: PositionType;
/** clarification of the popup position, whether it should start on the outside of trigger or the inside of trigger */
positionOrigin?: PositionOriginType;
/** popup behavior if it does not fit in the window */
behavior?: string;
/** Disable hiding the popup depending on the visibility of the activator */
Expand Down Expand Up @@ -74,6 +76,9 @@ export class ESLPopup extends ESLToggleable {
*/
@attr({defaultValue: 'top'}) public position: PositionType;

/** From which side of the trigger starts the positioning of the popup: 'inner', 'outer' ('outer' by default) */
@attr({defaultValue: 'outer'}) public positionOrigin: PositionOriginType;

/** Popup behavior if it does not fit in the window ('fit' by default) */
@attr({defaultValue: 'fit'}) public behavior: string;

Expand Down Expand Up @@ -202,6 +207,7 @@ export class ESLPopup extends ESLToggleable {
// TODO: change flow to use merged params unless attribute state is used in CSS
Object.assign(this, copyDefinedKeys({
position: params.position,
positionOrigin: params.positionOrigin,
behavior: params.behavior,
container: params.container,
marginArrow: params.marginArrow,
Expand Down Expand Up @@ -401,10 +407,11 @@ export class ESLPopup extends ESLToggleable {
const triggerRect = Rect.from(this.activator).shift(window.scrollX, window.scrollY);
const {containerRect} = this;

const innerMargin = this._offsetTrigger + arrowRect.width / 2;
const innerMargin = this._offsetTrigger + (this.positionOrigin === 'inner' ? 0 : arrowRect.width / 2);

const config = {
position: this.position,
hasInnerOrigin: this.positionOrigin === 'inner',
behavior: this.behavior,
marginArrow: this.marginArrow,
offsetArrowRatio: this.offsetArrowRatio,
Expand Down

0 comments on commit 6be5133

Please sign in to comment.