change(ui) - notes guide

This commit is contained in:
Shekar Siri 2022-11-15 12:27:31 +01:00
parent c73cb9e60a
commit 55bbd85972
6 changed files with 167 additions and 50 deletions

View file

@ -3,6 +3,7 @@ import { Button } from 'UI';
import { connectPlayer, pause } from 'Player';
import { connect } from 'react-redux';
import { setCreateNoteTooltip } from 'Duck/sessions';
import GuidePopup, { FEATURE_KEYS } from 'Shared/GuidePopup';
function NotePopup({
setCreateNoteTooltip,
@ -24,9 +25,18 @@ function NotePopup({
}, []);
return (
<Button icon="quotes" variant="text" disabled={tooltipActive} onClick={toggleNotePopup}>
Add Note
</Button>
<GuidePopup
title={
<div className="color-gray-dark">
Introducing <span className={''}>Notes</span>
</div>
}
description={'Annotate session replays and share your feedback with the rest of your team.'}
>
<Button icon="quotes" variant="text" disabled={tooltipActive} onClick={toggleNotePopup}>
Add Note
</Button>
</GuidePopup>
);
}
@ -36,7 +46,7 @@ const NotePopupPl = connectPlayer(
)(React.memo(NotePopup));
const NotePopupComp = connect(
(state) => ({ tooltipActive: state.getIn(['sessions', 'createNoteTooltip', 'isVisible']) }),
(state: any) => ({ tooltipActive: state.getIn(['sessions', 'createNoteTooltip', 'isVisible']) }),
{ setCreateNoteTooltip }
)(NotePopupPl);

View file

@ -1,37 +1,71 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Controls as Player } from 'Player';
import { Tooltip } from 'UI';
import { INDEXES } from 'App/constants/zindex';
export const FEATURE_KEYS = {
XRAY: 'featureViewed'
}
XRAY: 'featureViewed',
NOTES: 'notesFeatureViewed',
};
interface IProps {
children: React.ReactNode
title: React.ReactNode
description: React.ReactNode
key?: keyof typeof FEATURE_KEYS
children?: React.ReactNode;
title: React.ReactNode;
description: React.ReactNode;
key?: keyof typeof FEATURE_KEYS;
}
export default function GuidePopup({ children, title, description }: IProps) {
return (
// @ts-ignore
<Tooltip
title={
<div>
<div className="font-bold">
{title}
const [showGuide, setShowGuide] = useState(!localStorage.getItem(FEATURE_KEYS.NOTES));
useEffect(() => {
if (!showGuide) {
return;
}
Player.pause();
}, []);
const onClick = () => {
setShowGuide(false);
localStorage.setItem(FEATURE_KEYS.NOTES, 'true');
};
return showGuide ? (
<div>
<div
onClick={onClick}
className="bg-gray-darkest fixed inset-0 z-10 w-full h-screen cursor-pointer"
style={{ zIndex: INDEXES.POPUP_GUIDE_BG, opacity: '0.7' }}
></div>
<Tooltip
offset={30}
className="!bg-white rounded text-center shadow !p-6"
title={
<div className="relative">
<div className="font-bold">{title}</div>
<div className="color-gray-medium w-80">{description}</div>
<div className="w-10 h-10 bg-white rotate-45 absolute right-0 left-0 m-auto" style={{ top: '-38px'}} />
</div>
<div className="color-gray-medium">
{description}
}
open={true}
>
<div className="relative pointer-events-none">
<div className="" style={{ zIndex: INDEXES.POPUP_GUIDE_BTN, position: 'inherit' }}>
{children}
</div>
<div
className="absolute bg-white top-0 left-0"
style={{
zIndex: INDEXES.POPUP_GUIDE_BG,
width: '120px',
height: '40px',
borderRadius: '30px',
margin: '-2px -10px',
}}
></div>
</div>
}
// distance={30}
// theme={'light'}
open={true}
// arrow={true}
>
{children}
</Tooltip>
</Tooltip>
</div>
) : (
children
);
}

View file

@ -39,10 +39,10 @@ function XRayButton(props: Props) {
)}
<div className="relative">
{showGuide ? (
<GuidePopup
title={<>Introducing <span className={stl.text}>X-Ray</span></>}
description={"Get a quick overview on the issues in this session."}
>
// <GuidePopup
// title={<div className="color-gray-dark">Introducing <span className={stl.text}>X-Ray</span></div>}
// description={"Get a quick overview on the issues in this session."}
// >
<button
className={cn(stl.wrapper, { [stl.default]: !isActive, [stl.active]: isActive })}
onClick={onClick}
@ -51,17 +51,17 @@ function XRayButton(props: Props) {
<span className="z-1">X-RAY</span>
</button>
<div
className="absolute bg-white top-0 left-0 z-0"
style={{
zIndex: INDEXES.POPUP_GUIDE_BG,
width: '100px',
height: '50px',
borderRadius: '30px',
margin: '-10px -16px',
}}
></div>
</GuidePopup>
// <div
// className="absolute bg-white top-0 left-0 z-0"
// style={{
// zIndex: INDEXES.POPUP_GUIDE_BG,
// width: '100px',
// height: '50px',
// borderRadius: '30px',
// margin: '-10px -16px',
// }}
// ></div>
// </GuidePopup>
) : (
<Tooltip title="Get a quick overview on the issues in this session." disabled={isActive}>
<button

View file

@ -3,7 +3,7 @@ import { mergeRefs } from 'react-merge-refs';
import {
useFloating,
autoUpdate,
offset,
offset as _offset,
flip,
shift,
useHover,
@ -12,6 +12,8 @@ import {
useRole,
useInteractions,
FloatingPortal,
arrow,
computePosition,
} from '@floating-ui/react-dom-interactions';
import type { Placement } from '@floating-ui/react-dom-interactions';
import { INDEXES } from 'App/constants/zindex';
@ -20,14 +22,19 @@ export function useTooltipState({
disabled = false,
initialOpen = false,
placement = 'top',
offset = 5,
delay,
arrowRef = null,
}: {
disabled?: boolean;
initialOpen?: boolean;
placement?: Placement;
offset?: number;
delay?: number;
arrowRef?: any;
} = {}) {
const [open, setOpen] = React.useState(initialOpen);
const staticSide = mapPlacementSideToCSSProperty(placement);
React.useEffect(() => {
if (disabled) {
@ -40,7 +47,7 @@ export function useTooltipState({
open,
onOpenChange: setOpen,
whileElementsMounted: autoUpdate,
middleware: [offset(5), flip(), shift()],
middleware: [_offset(offset), flip(), shift(), arrow({ element: arrowRef })],
});
const context = data.context;
@ -49,15 +56,16 @@ export function useTooltipState({
const focus = useFocus(context);
const dismiss = useDismiss(context);
const role = useRole(context, { role: 'tooltip' });
const interactions = useInteractions([hover, focus, dismiss, role]);
return React.useMemo(
() => ({
open,
setOpen,
arrowRef,
...interactions,
...data,
staticSide,
}),
[open, setOpen, interactions, data]
);
@ -73,6 +81,7 @@ export const TooltipAnchor = React.forwardRef<
}
>(function TooltipAnchor({ children, state, asChild = false, ...props }, propRef) {
const childrenRef = (children as any).ref;
const ref = React.useMemo(
() => mergeRefs([state.reference, propRef, childrenRef]),
[state.reference, propRef, childrenRef]
@ -119,3 +128,46 @@ export const FloatingTooltip = React.forwardRef<
</FloatingPortal>
);
});
function mapPlacementSideToCSSProperty(placement: Placement) {
const staticSide = {
top: 'bottom',
right: 'left',
bottom: 'top',
left: 'right',
}[placement.split('-')[0]];
return staticSide;
}
export const FloatingArrow = React.forwardRef<
HTMLDivElement,
React.HTMLProps<HTMLDivElement> & { state: TooltipState }
>(function Tooltip({ state, ...props }, propRef) {
const ref = React.useMemo(() => state.arrowRef, [state.arrowRef, propRef]);
const { x: arrowX, y: arrowY } = state.middlewareData?.arrow || { x: 0, y: 0 };
const staticSide = state.staticSide;
return (
<FloatingPortal>
{state.open && (
<div
ref={ref}
style={{
width: '20px',
height: '20px',
backgroundColor: 'white',
position: state.strategy,
left: arrowX != null ? `${arrowX + state.x}px` : '',
top: arrowY != null ? `${arrowY + state.y}px` : '',
[staticSide]: '-10px',
zIndex: INDEXES.TOOLTIP - 1,
transform: 'rotate(45deg)',
...props.style,
}}
{...state.getFloatingProps(props)}
/>
)}
</FloatingPortal>
);
});

View file

@ -1,5 +1,5 @@
import React from 'react';
import { useTooltipState, TooltipAnchor, FloatingTooltip } from './FloatingTooltip';
import { useTooltipState, TooltipAnchor, FloatingTooltip, FloatingArrow } from './FloatingTooltip';
import type { Placement } from '@floating-ui/react-dom-interactions';
import cn from 'classnames';
@ -12,6 +12,7 @@ interface Props {
className?: string;
delay?: number;
style?: any;
offset?: number;
}
function Tooltip(props: Props) {
const {
@ -22,18 +23,30 @@ function Tooltip(props: Props) {
className = '',
delay = 500,
style = {},
offset = 5,
} = props;
const state = useTooltipState({ disabled: disabled, placement, delay });
const arrowRef = React.useRef(null);
const state = useTooltipState({
disabled: disabled,
placement,
delay,
initialOpen: open,
offset,
arrowRef,
});
return (
<>
<div className="relative">
<TooltipAnchor state={state}>{props.children}</TooltipAnchor>
<FloatingTooltip
state={state}
className={cn('bg-gray-darkest color-white rounded py-1 px-2 animate-fade', className)}
>
{title}
{/* <FloatingArrow state={state} className="" /> */}
</FloatingTooltip>
</>
</div>
);
}

View file

@ -347,4 +347,12 @@ p {
0% {
opacity: 0;
}
}
#arrow {
position: absolute;
background: #333;
width: 80px;
height: 80px;
transform: rotate(45deg);
}