@atlaskit/spotlight
Version:
A spotlight introduces users to points of interest, from focused messages to multi-step tours.
133 lines (131 loc) • 4.53 kB
JavaScript
/* top-layer.tsx generated by @compiled/babel-plugin v3.0.2 */
import * as React from 'react';
import { ax, ix } from "@compiled/react/runtime";
import { useContext, useEffect, useLayoutEffect, useRef } from 'react';
import Motion from '@atlaskit/motion/entering/motion';
import { createPopoverCloseEvent } from '@atlaskit/top-layer/popover/create-close-event';
import { Popover } from '@atlaskit/top-layer/popover/popover';
import { useAnchoredPopover } from '@atlaskit/top-layer/use-anchored-popover';
import { useSimpleLightDismiss } from '@atlaskit/top-layer/use-simple-light-dismiss';
import { SpotlightContext } from '../../controllers/context';
import { usePositionArea } from '../../utils/use-position-area';
import { getPlacementWithOffset } from './get-placement-with-offset';
const noopUpdate = () => Promise.resolve();
const DefaultMotion = ({
children
}) => /*#__PURE__*/React.createElement(Motion, {
enteringAnimation: "var(--ds-spotlight-enter, 250ms cubic-bezier(0.4, 0, 0, 1) ScaleIn95to100, 250ms cubic-bezier(0.4, 0, 0, 1) FadeIn0to100)",
exitingAnimation: "var(--ds-spotlight-exit, 200ms cubic-bezier(0.6, 0, 0.8, 0.6) ScaleOut100to95, 200ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)"
}, children);
/**
* __PopoverContent__
*
* A `PopoverContent` is the element that is shown as a popover.
*/
export const PopoverContent = props => {
/**
* This composes the low-level `@atlaskit/top-layer` `Popover` primitive directly
* (rather than `@atlaskit/popup` or a higher-level wrapper) for two reasons:
*
* 1. Spotlight has bespoke positioning, dismiss, and motion requirements that
* are coupled to the surrounding `SpotlightContext` (heading id, card
* placement, primary/secondary actions, etc.). Building on the primitive
* keeps that wiring local to spotlight without leaking into a shared popup.
* 2. Spotlight's public API exposes a `dismiss(event)` callback that takes a
* `KeyboardEvent` / `MouseEvent`, while the top-layer primitive surfaces a
* structured `{ reason }`. Bridging that contract here (via
* `createPopoverCloseEvent`) preserves backwards compatibility for existing
* consumers as we migrate behind the `platform-dst-top-layer-spotlight` gate.
*/
const {
children,
placement,
motion = DefaultMotion,
isVisible = true,
shouldDismissOnClickOutside = true,
dismiss,
back,
testId,
offset
} = props;
const done = 'done' in props ? props.done : undefined;
const next = 'next' in props ? props.next : undefined;
const popoverRef = useRef(null);
const {
heading,
popoverContent,
card,
primaryAction,
secondaryAction,
target
} = useContext(SpotlightContext);
useAnchoredPopover({
anchorRef: target.ref,
popoverRef,
placement: getPlacementWithOffset({
placement,
offset
}),
isOpen: isVisible
});
useSimpleLightDismiss({
popoverRef,
isOpen: isVisible,
onClose: ({
reason
}) => {
if (reason === 'light-dismiss' && !shouldDismissOnClickOutside) {
return;
}
// `createPopoverCloseEvent` is typed as `KeyboardEvent | MouseEvent | Event`
// for forward-compatibility, but only `escape` / `light-dismiss` are
// possible here, so the result is always a Keyboard/MouseEvent.
dismiss(createPopoverCloseEvent({
reason
}));
}
});
useEffect(() => {
popoverContent.setRef(popoverRef);
popoverContent.setUpdate(() => noopUpdate);
}, [popoverContent]);
const positionArea = usePositionArea(popoverRef);
useEffect(() => {
popoverContent.setPositionArea(positionArea);
}, [popoverContent, positionArea]);
useEffect(() => {
if (!done && !next) {
return;
}
if (done) {
primaryAction.setAction(done);
return;
} else if (next) {
primaryAction.setAction(next);
return;
}
}, [done, next, primaryAction]);
useEffect(() => {
popoverContent.setDismiss(dismiss);
}, [dismiss, popoverContent]);
useEffect(() => {
if (!back) {
return;
}
secondaryAction.setAction(back);
}, [back, secondaryAction]);
useLayoutEffect(() => {
card.setPlacement(placement);
if (motion) {
card.setMotion(() => motion);
}
}, [placement, card, motion]);
return /*#__PURE__*/React.createElement(Popover, {
ref: popoverRef,
isOpen: isVisible,
mode: "manual",
role: "dialog",
labelledBy: heading.id,
testId: testId
}, children);
};