add shpitz

This commit is contained in:
Vadim Ogievetsky 2024-11-25 13:29:24 -08:00
parent 4b731c0b0c
commit 93cd91bedf
2 changed files with 28 additions and 7 deletions

View File

@ -18,6 +18,8 @@
@import '../../variables'; @import '../../variables';
$shpitz-size: 10px;
.portal-bubble { .portal-bubble {
position: absolute; position: absolute;
@include card-like; @include card-like;
@ -38,9 +40,21 @@
pointer-events: none; pointer-events: none;
} }
& > .shpitz {
content: '';
position: absolute;
transform: translate(-50%, 0);
bottom: -$shpitz-size;
border-top: $shpitz-size solid $dark-gray1;
border-right: $shpitz-size solid transparent;
border-left: $shpitz-size solid transparent;
border-bottom: none;
}
& > .bubble-title-bar { & > .bubble-title-bar {
position: relative; position: relative;
padding: 5px 5px 0 5px; padding: 5px 5px 0 5px;
white-space: nowrap;
font-weight: bold; font-weight: bold;
&.with-close { &.with-close {

View File

@ -20,7 +20,7 @@ import { Button } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useRef } from 'react'; import { useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { clamp } from '../../utils'; import { clamp } from '../../utils';
@ -33,24 +33,28 @@ interface PortalBubbleProps {
direction?: 'up' | 'down'; direction?: 'up' | 'down';
onClose?(): void; onClose?(): void;
mute?: boolean; mute?: boolean;
minimal?: boolean;
} }
export const PortalBubble = function PortalBubble(props: PortalBubbleProps) { export const PortalBubble = function PortalBubble(props: PortalBubbleProps) {
const { className, openOn, direction = 'up', onClose, mute } = props; const { className, openOn, direction = 'up', onClose, mute, minimal } = props;
const ref = useRef<HTMLDivElement | null>(null); const [myWidth, setMyWidth] = useState(200);
if (!openOn) return null; if (!openOn) return null;
const div: HTMLDivElement | null = ref.current; const halfMyWidth = myWidth / 2;
const myWidth = div ? div.offsetWidth : 200;
const x = clamp(openOn.x, myWidth / 2, window.innerWidth - myWidth / 2); const x = clamp(openOn.x, halfMyWidth, window.innerWidth - halfMyWidth);
const offset = clamp(x - openOn.x, -halfMyWidth, halfMyWidth);
return createPortal( return createPortal(
<div <div
className={classNames('portal-bubble', className, direction, { className={classNames('portal-bubble', className, direction, {
mute: mute && !onClose, mute: mute && !onClose,
})} })}
ref={ref} ref={element => {
if (!element) return;
setMyWidth(element.offsetWidth);
}}
style={{ left: x, top: openOn.y }} style={{ left: x, top: openOn.y }}
> >
{(openOn.title || onClose) && ( {(openOn.title || onClose) && (
@ -68,6 +72,9 @@ export const PortalBubble = function PortalBubble(props: PortalBubbleProps) {
</div> </div>
)} )}
<div className="bubble-content">{openOn.text}</div> <div className="bubble-content">{openOn.text}</div>
{!minimal && (
<div className="shpitz" style={{ left: offset ? `calc(50% - ${offset}px)` : '50%' }} />
)}
</div>, </div>,
document.body, document.body,
); );