Skip to content

Commit

Permalink
feat(popover): option to force show/hide popover arrow/angle
Browse files Browse the repository at this point in the history
  • Loading branch information
nolimits4web committed Aug 5, 2022
1 parent 456aa4f commit d09f2ac
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 111 deletions.
8 changes: 6 additions & 2 deletions src/react/components/Popover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const Popover = forwardRef((props, ref) => {
const {
component = 'div',
className,
angle,
angleClassName = '',
colors: colorsProp,
size = 'w-64',
Expand Down Expand Up @@ -83,14 +84,16 @@ const Popover = forwardRef((props, ref) => {
className
);

const needsAngle = typeof angle === 'undefined' ? theme === 'ios' : angle;

const setPopover = () => {
if (!target || !elRef.current || !opened) return;
setPositions(
calcPopoverPosition({
popoverEl: elRef.current,
targetEl: target,
angleEl: angleElRef.current,
theme,
needsAngle,
targetX,
targetY,
targetHeight,
Expand Down Expand Up @@ -141,6 +144,7 @@ const Popover = forwardRef((props, ref) => {
'bottom-right': 'origin-top-left',
'bottom-left': 'origin-top-right',
};

const classes = cls(
c.base[state],
theme === 'material' && originClasses[positions.popoverPosition]
Expand All @@ -158,7 +162,7 @@ const Popover = forwardRef((props, ref) => {
style={popoverStyle}
{...attrs}
>
{theme === 'ios' && (
{needsAngle && (
<div
ref={angleElRef}
style={angleStyle}
Expand Down
168 changes: 63 additions & 105 deletions src/shared/calc-popover-position.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export const calcPopoverPosition = ({
popoverEl,
targetEl,
angleEl,
theme,
needsAngle,
targetX,
targetY,
targetWidth = 0,
Expand Down Expand Up @@ -41,11 +41,8 @@ export const calcPopoverPosition = ({
let angleLeft;
let angleTop;
let anglePosition = '';

if (theme === 'ios') {
if (needsAngle) {
angleSize = angleEl.offsetWidth / 2;
} else {
// do nothing
}

let targetOffsetLeft;
Expand Down Expand Up @@ -74,115 +71,76 @@ export const calcPopoverPosition = ({
}

let [left, top, diff] = [0, 0, 0];
let popoverVerticalPosition = '';
let popoverHorizontalPosition = '';

// Top Position
let position = theme === 'material' ? 'bottom' : 'top';
if (theme === 'material') {
if (popoverHeight < appHeight - targetOffsetTop - targetHeight) {
// On bottom
position = 'bottom';
top = targetOffsetTop + targetHeight;
} else if (popoverHeight < targetOffsetTop - safeAreaTop) {
// On top
top = targetOffsetTop - popoverHeight;
position = 'top';
} else {
// On middle
position = 'middle';
top = targetHeight / 2 + targetOffsetTop - popoverHeight / 2;
}
top = Math.max(8, Math.min(top, appHeight - popoverHeight - 8));
let position = 'top';

// Horizontal Position
let hPosition;
if (targetOffsetLeft < appWidth / 2) {
hPosition = 'right';
left =
position === 'middle'
? targetOffsetLeft + targetWidth
: targetOffsetLeft;
} else {
hPosition = 'left';
left =
position === 'middle'
? targetOffsetLeft - popoverWidth
: targetOffsetLeft + targetWidth - popoverWidth;
if (popoverHeight + angleSize < targetOffsetTop - safeAreaTop) {
// On top
top = targetOffsetTop - popoverHeight - angleSize;
} else if (
popoverHeight + angleSize <
appHeight - targetOffsetTop - targetHeight
) {
// On bottom
position = 'bottom';
top = targetOffsetTop + targetHeight + angleSize;
} else {
// On middle
position = 'middle';
top = targetHeight / 2 + targetOffsetTop - popoverHeight / 2;
diff = top;
top = Math.max(5, Math.min(top, appHeight - popoverHeight - 5));
diff -= top;
}

// Horizontal Position
if (position === 'top' || position === 'bottom') {
left = targetWidth / 2 + targetOffsetLeft - popoverWidth / 2;
diff = left;
left = Math.max(5, Math.min(left, appWidth - popoverWidth - 5));
if (safeAreaLeft) {
left = Math.max(left, safeAreaLeft);
}
if (safeAreaRight && left + popoverWidth > appWidth - 5 - safeAreaRight) {
left = appWidth - 5 - safeAreaRight - popoverWidth;
}
left = Math.max(
8,
Math.min(left, appWidth - popoverWidth - 8 - safeAreaRight),
safeAreaLeft
if (position === 'top') {
anglePosition = 'bottom';
}
if (position === 'bottom') {
anglePosition = 'top';
}
diff -= left;
angleLeft = popoverWidth / 2 - angleSize + diff;
angleLeft = Math.max(
Math.min(angleLeft, popoverWidth - angleSize * 2 - 13),
13
);
popoverVerticalPosition = position;
popoverHorizontalPosition = hPosition;
} else {
// ios
if (popoverHeight + angleSize < targetOffsetTop - safeAreaTop) {
// On top
top = targetOffsetTop - popoverHeight - angleSize;
} else if (
popoverHeight + angleSize <
appHeight - targetOffsetTop - targetHeight
} else if (position === 'middle') {
left = targetOffsetLeft - popoverWidth - angleSize;
anglePosition = 'right';
if (
left < 5 ||
left + popoverWidth + safeAreaRight > appWidth ||
left < safeAreaLeft
) {
// On bottom
position = 'bottom';
top = targetOffsetTop + targetHeight + angleSize;
} else {
// On middle
position = 'middle';
top = targetHeight / 2 + targetOffsetTop - popoverHeight / 2;
diff = top;
top = Math.max(5, Math.min(top, appHeight - popoverHeight - 5));
diff -= top;
}

// Horizontal Position
if (position === 'top' || position === 'bottom') {
left = targetWidth / 2 + targetOffsetLeft - popoverWidth / 2;
diff = left;
left = Math.max(5, Math.min(left, appWidth - popoverWidth - 5));
if (safeAreaLeft) {
left = Math.max(left, safeAreaLeft);
}
if (safeAreaRight && left + popoverWidth > appWidth - 5 - safeAreaRight) {
left = appWidth - 5 - safeAreaRight - popoverWidth;
}
if (position === 'top') {
anglePosition = 'bottom';
}
if (position === 'bottom') {
anglePosition = 'top';
}
diff -= left;
angleLeft = popoverWidth / 2 - angleSize + diff;
angleLeft = Math.max(
Math.min(angleLeft, popoverWidth - angleSize * 2 - 13),
13
);
} else if (position === 'middle') {
left = targetOffsetLeft - popoverWidth - angleSize;
anglePosition = 'right';
if (
left < 5 ||
left + popoverWidth + safeAreaRight > appWidth ||
left < safeAreaLeft
) {
if (left < 5) left = targetOffsetLeft + targetWidth + angleSize;
if (left + popoverWidth + safeAreaRight > appWidth)
left = appWidth - popoverWidth - 5 - safeAreaRight;
if (left < safeAreaLeft) left = safeAreaLeft;
anglePosition = 'left';
}
angleTop = popoverHeight / 2 - angleSize + diff;
angleTop = Math.max(
Math.min(angleTop, popoverHeight - angleSize * 2 - 13),
13
);
if (left < 5) left = targetOffsetLeft + targetWidth + angleSize;
if (left + popoverWidth + safeAreaRight > appWidth)
left = appWidth - popoverWidth - 5 - safeAreaRight;
if (left < safeAreaLeft) left = safeAreaLeft;
anglePosition = 'left';
}
angleTop = popoverHeight / 2 - angleSize + diff;
angleTop = Math.max(
Math.min(angleTop, popoverHeight - angleSize * 2 - 13),
13
);
}

const popoverVerticalPosition = position;
const popoverHorizontalPosition = left < targetOffsetLeft ? 'left' : 'right';

return {
set: true,
angleTop: typeof angleTop === 'undefined' ? undefined : `${angleTop}px`,
Expand Down
1 change: 1 addition & 0 deletions src/shared/classes/PopoverClasses.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const PopoverClasses = (props, colors, classes) => {
'absolute w-6.5 h-6.5 z-50 overflow-hidden',
translucent && 'opacity-80'
),
material: cls('absolute w-6.5 h-6.5 z-50 overflow-hidden'),
bottom: 'top-full',
top: 'bottom-full',
left: 'right-full',
Expand Down
7 changes: 5 additions & 2 deletions src/svelte/components/Popover.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
export let material = undefined;
export let style = '';
export let angle = undefined;
export let angleClass = '';
export let size = 'w-64';
export let opened = false;
Expand Down Expand Up @@ -60,13 +61,15 @@
(v) => (c = v)
);
$: needsAngle = typeof angle === 'undefined' ? theme === 'ios' : angle;
const setPopover = () => {
if (!target || !el || !opened) return;
positions = calcPopoverPosition({
popoverEl: el,
targetEl: target,
angleEl: angleEl,
theme,
needsAngle,
targetX,
targetY,
targetWidth,
Expand Down Expand Up @@ -130,7 +133,7 @@
<div class={c.backdrop[state]} on:click={onBackdropClick} />
{/if}
<div bind:this={el} class={classes} style={popoverStyle} {...$$restProps}>
{#if theme === 'ios'}
{#if needsAngle}
<div
bind:this={angleEl}
style={angleStyle}
Expand Down
5 changes: 5 additions & 0 deletions src/types/Popover.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ interface Props {
* @default true
*/
translucent?: boolean;
/**
* Renders popover "angle"/"corner", if not specified then it will automatically show "angle"/"corner" if iOS theme is enabled
*
*/
angle?: boolean;
/**
* Additional css class to add on "angle"/"corner" element
*
Expand Down
12 changes: 10 additions & 2 deletions src/vue/components/Popover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div v-if="backdrop" :class="c.backdrop[state]" @click="onBackdropClick" />
<component :is="component" ref="elRef" :style="popoverStyle" :class="classes">
<div
v-if="theme === 'ios'"
v-if="needsAngle"
ref="angleElRef"
:style="angleStyle"
:class="c.angleWrap[positions.anglePosition]"
Expand Down Expand Up @@ -43,6 +43,7 @@
size: { type: String, default: 'w-64' },
opened: Boolean,
backdrop: { type: Boolean, default: true },
angle: { type: Boolean, default: true },
angleClass: { type: String, default: '' },
target: { type: [Object, String], default: undefined },
targetX: { type: Number, default: undefined },
Expand Down Expand Up @@ -77,13 +78,19 @@
PopoverClasses(props, colors.value, ctx.attrs.class)
);
const needsAngle = computed(() => {
return typeof props.angle === 'undefined'
? theme.value === 'ios'
: props.angle;
});
const setPopover = () => {
if (!props.target || !elRef.value || !props.opened) return;
positions.value = calcPopoverPosition({
popoverEl: elRef.value,
targetEl: props.target,
angleEl: angleElRef.value,
theme: theme.value,
needsAngle: needsAngle.value,
targetX: props.targetX,
targetY: props.targetY,
targetWidth: props.targetWidth,
Expand Down Expand Up @@ -161,6 +168,7 @@
angleElRef,
classes,
popoverStyle,
needsAngle,
angleStyle,
positions,
theme,
Expand Down

0 comments on commit d09f2ac

Please sign in to comment.