PresenceChild.mjs 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. "use client";
  2. import { jsx } from 'react/jsx-runtime';
  3. import * as React from 'react';
  4. import { useId, useCallback, useMemo } from 'react';
  5. import { PresenceContext } from '../../context/PresenceContext.mjs';
  6. import { useConstant } from '../../utils/use-constant.mjs';
  7. import { PopChild } from './PopChild.mjs';
  8. const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, }) => {
  9. const presenceChildren = useConstant(newChildrenMap);
  10. const id = useId();
  11. const memoizedOnExitComplete = useCallback((childId) => {
  12. presenceChildren.set(childId, true);
  13. for (const isComplete of presenceChildren.values()) {
  14. if (!isComplete)
  15. return; // can stop searching when any is incomplete
  16. }
  17. onExitComplete && onExitComplete();
  18. }, [presenceChildren, onExitComplete]);
  19. const context = useMemo(() => ({
  20. id,
  21. initial,
  22. isPresent,
  23. custom,
  24. onExitComplete: memoizedOnExitComplete,
  25. register: (childId) => {
  26. presenceChildren.set(childId, false);
  27. return () => presenceChildren.delete(childId);
  28. },
  29. }),
  30. /**
  31. * If the presence of a child affects the layout of the components around it,
  32. * we want to make a new context value to ensure they get re-rendered
  33. * so they can detect that layout change.
  34. */
  35. presenceAffectsLayout
  36. ? [Math.random(), memoizedOnExitComplete]
  37. : [isPresent, memoizedOnExitComplete]);
  38. useMemo(() => {
  39. presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
  40. }, [isPresent]);
  41. /**
  42. * If there's no `motion` components to fire exit animations, we want to remove this
  43. * component immediately.
  44. */
  45. React.useEffect(() => {
  46. !isPresent &&
  47. !presenceChildren.size &&
  48. onExitComplete &&
  49. onExitComplete();
  50. }, [isPresent]);
  51. if (mode === "popLayout") {
  52. children = (jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, children: children }));
  53. }
  54. return (jsx(PresenceContext.Provider, { value: context, children: children }));
  55. };
  56. function newChildrenMap() {
  57. return new Map();
  58. }
  59. export { PresenceChild };