Mission Control Dashboard - Initial implementation

This commit is contained in:
Daniel Arroyo
2026-03-27 18:36:05 +00:00
parent 257cea2c7d
commit a8fb4d4555
12516 changed files with 2307128 additions and 2 deletions

View File

@@ -0,0 +1,107 @@
"use client";
import { jsx } from 'react/jsx-runtime';
import { isHTMLElement } from 'motion-dom';
import * as React from 'react';
import { useId, useRef, useContext, useInsertionEffect } from 'react';
import { MotionConfigContext } from '../../context/MotionConfigContext.mjs';
import { useComposedRefs } from '../../utils/use-composed-ref.mjs';
/**
* Measurement functionality has to be within a separate component
* to leverage snapshot lifecycle.
*/
class PopChildMeasure extends React.Component {
getSnapshotBeforeUpdate(prevProps) {
const element = this.props.childRef.current;
if (isHTMLElement(element) && prevProps.isPresent && !this.props.isPresent && this.props.pop !== false) {
const parent = element.offsetParent;
const parentWidth = isHTMLElement(parent)
? parent.offsetWidth || 0
: 0;
const parentHeight = isHTMLElement(parent)
? parent.offsetHeight || 0
: 0;
const computedStyle = getComputedStyle(element);
const size = this.props.sizeRef.current;
size.height = parseFloat(computedStyle.height);
size.width = parseFloat(computedStyle.width);
size.top = element.offsetTop;
size.left = element.offsetLeft;
size.right = parentWidth - size.width - size.left;
size.bottom = parentHeight - size.height - size.top;
}
return null;
}
/**
* Required with getSnapshotBeforeUpdate to stop React complaining.
*/
componentDidUpdate() { }
render() {
return this.props.children;
}
}
function PopChild({ children, isPresent, anchorX, anchorY, root, pop }) {
const id = useId();
const ref = useRef(null);
const size = useRef({
width: 0,
height: 0,
top: 0,
left: 0,
right: 0,
bottom: 0,
});
const { nonce } = useContext(MotionConfigContext);
/**
* In React 19, refs are passed via props.ref instead of element.ref.
* We check props.ref first (React 19) and fall back to element.ref (React 18).
*/
const childRef = children.props?.ref ??
children?.ref;
const composedRef = useComposedRefs(ref, childRef);
/**
* We create and inject a style block so we can apply this explicit
* sizing in a non-destructive manner by just deleting the style block.
*
* We can't apply size via render as the measurement happens
* in getSnapshotBeforeUpdate (post-render), likewise if we apply the
* styles directly on the DOM node, we might be overwriting
* styles set via the style prop.
*/
useInsertionEffect(() => {
const { width, height, top, left, right, bottom } = size.current;
if (isPresent || pop === false || !ref.current || !width || !height)
return;
const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`;
const y = anchorY === "bottom" ? `bottom: ${bottom}` : `top: ${top}`;
ref.current.dataset.motionPopId = id;
const style = document.createElement("style");
if (nonce)
style.nonce = nonce;
const parent = root ?? document.head;
parent.appendChild(style);
if (style.sheet) {
style.sheet.insertRule(`
[data-motion-pop-id="${id}"] {
position: absolute !important;
width: ${width}px !important;
height: ${height}px !important;
${x}px !important;
${y}px !important;
}
`);
}
return () => {
ref.current?.removeAttribute("data-motion-pop-id");
if (parent.contains(style)) {
parent.removeChild(style);
}
};
}, [isPresent]);
return (jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, pop: pop, children: pop === false
? children
: React.cloneElement(children, { ref: composedRef }) }));
}
export { PopChild };
//# sourceMappingURL=PopChild.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,63 @@
"use client";
import { jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useId, useMemo } from 'react';
import { PresenceContext } from '../../context/PresenceContext.mjs';
import { useConstant } from '../../utils/use-constant.mjs';
import { PopChild } from './PopChild.mjs';
const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, anchorY, root }) => {
const presenceChildren = useConstant(newChildrenMap);
const id = useId();
let isReusedContext = true;
let context = useMemo(() => {
isReusedContext = false;
return {
id,
initial,
isPresent,
custom,
onExitComplete: (childId) => {
presenceChildren.set(childId, true);
for (const isComplete of presenceChildren.values()) {
if (!isComplete)
return; // can stop searching when any is incomplete
}
onExitComplete && onExitComplete();
},
register: (childId) => {
presenceChildren.set(childId, false);
return () => presenceChildren.delete(childId);
},
};
}, [isPresent, presenceChildren, onExitComplete]);
/**
* If the presence of a child affects the layout of the components around it,
* we want to make a new context value to ensure they get re-rendered
* so they can detect that layout change.
*/
if (presenceAffectsLayout && isReusedContext) {
context = { ...context };
}
useMemo(() => {
presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
}, [isPresent]);
/**
* If there's no `motion` components to fire exit animations, we want to remove this
* component immediately.
*/
React.useEffect(() => {
!isPresent &&
!presenceChildren.size &&
onExitComplete &&
onExitComplete();
}, [isPresent]);
children = (jsx(PopChild, { pop: mode === "popLayout", isPresent: isPresent, anchorX: anchorX, anchorY: anchorY, root: root, children: children }));
return (jsx(PresenceContext.Provider, { value: context, children: children }));
};
function newChildrenMap() {
return new Map();
}
export { PresenceChild };
//# sourceMappingURL=PresenceChild.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PresenceChild.mjs","sources":["../../../../src/components/AnimatePresence/PresenceChild.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useId, useMemo } from \"react\"\nimport {\n PresenceContext,\n type PresenceContextProps,\n} from \"../../context/PresenceContext\"\nimport { VariantLabels } from \"../../motion/types\"\nimport { useConstant } from \"../../utils/use-constant\"\nimport { PopChild } from \"./PopChild\"\n\ninterface PresenceChildProps {\n children: React.ReactElement\n isPresent: boolean\n onExitComplete?: () => void\n initial?: false | VariantLabels\n custom?: any\n presenceAffectsLayout: boolean\n mode: \"sync\" | \"popLayout\" | \"wait\"\n anchorX?: \"left\" | \"right\"\n anchorY?: \"top\" | \"bottom\"\n root?: HTMLElement | ShadowRoot\n}\n\nexport const PresenceChild = ({\n children,\n initial,\n isPresent,\n onExitComplete,\n custom,\n presenceAffectsLayout,\n mode,\n anchorX,\n anchorY,\n root\n}: PresenceChildProps) => {\n const presenceChildren = useConstant(newChildrenMap)\n const id = useId()\n\n let isReusedContext = true\n let context = useMemo((): PresenceContextProps => {\n isReusedContext = false\n return {\n id,\n initial,\n isPresent,\n custom,\n onExitComplete: (childId: string) => {\n presenceChildren.set(childId, true)\n\n for (const isComplete of presenceChildren.values()) {\n if (!isComplete) return // can stop searching when any is incomplete\n }\n\n onExitComplete && onExitComplete()\n },\n register: (childId: string) => {\n presenceChildren.set(childId, false)\n return () => presenceChildren.delete(childId)\n },\n }\n }, [isPresent, presenceChildren, onExitComplete])\n\n /**\n * If the presence of a child affects the layout of the components around it,\n * we want to make a new context value to ensure they get re-rendered\n * so they can detect that layout change.\n */\n if (presenceAffectsLayout && isReusedContext) {\n context = { ...context }\n }\n\n useMemo(() => {\n presenceChildren.forEach((_, key) => presenceChildren.set(key, false))\n }, [isPresent])\n\n /**\n * If there's no `motion` components to fire exit animations, we want to remove this\n * component immediately.\n */\n React.useEffect(() => {\n !isPresent &&\n !presenceChildren.size &&\n onExitComplete &&\n onExitComplete()\n }, [isPresent])\n\n children = (\n <PopChild pop={mode === \"popLayout\"} isPresent={isPresent} anchorX={anchorX} anchorY={anchorY} root={root}>\n {children}\n </PopChild>\n )\n\n return (\n <PresenceContext.Provider value={context}>\n {children}\n </PresenceContext.Provider>\n )\n}\n\nfunction newChildrenMap(): Map<string, boolean> {\n return new Map()\n}\n"],"names":[],"mappings":";;;;;;;;AAyBO;AAYH;AACA;;AAGA;;;;;;;AAOQ;AACI;;AAGI;AAAiB;;;;AAKzB;AACI;;;;;AAMZ;;;;AAIG;AACH;AACI;;;AAIA;AACJ;AAEA;;;AAGG;AACH;AACI;;;AAGI;AACR;AAEA;AAMA;AAKJ;AAEA;;AAEA;;"}

View File

@@ -0,0 +1,176 @@
"use client";
import { jsx, Fragment } from 'react/jsx-runtime';
import { useMemo, useRef, useState, useContext } from 'react';
import { LayoutGroupContext } from '../../context/LayoutGroupContext.mjs';
import { useConstant } from '../../utils/use-constant.mjs';
import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs';
import { PresenceChild } from './PresenceChild.mjs';
import { usePresence } from './use-presence.mjs';
import { onlyElements, getChildKey } from './utils.mjs';
/**
* `AnimatePresence` enables the animation of components that have been removed from the tree.
*
* When adding/removing more than a single child, every child **must** be given a unique `key` prop.
*
* Any `motion` components that have an `exit` property defined will animate out when removed from
* the tree.
*
* ```jsx
* import { motion, AnimatePresence } from 'framer-motion'
*
* export const Items = ({ items }) => (
* <AnimatePresence>
* {items.map(item => (
* <motion.div
* key={item.id}
* initial={{ opacity: 0 }}
* animate={{ opacity: 1 }}
* exit={{ opacity: 0 }}
* />
* ))}
* </AnimatePresence>
* )
* ```
*
* You can sequence exit animations throughout a tree using variants.
*
* If a child contains multiple `motion` components with `exit` props, it will only unmount the child
* once all `motion` components have finished animating out. Likewise, any components using
* `usePresence` all need to call `safeToRemove`.
*
* @public
*/
const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", anchorY = "top", root }) => {
const [isParentPresent, safeToRemove] = usePresence(propagate);
/**
* Filter any children that aren't ReactElements. We can only track components
* between renders with a props.key.
*/
const presentChildren = useMemo(() => onlyElements(children), [children]);
/**
* Track the keys of the currently rendered children. This is used to
* determine which children are exiting.
*/
const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
/**
* If `initial={false}` we only want to pass this to components in the first render.
*/
const isInitialRender = useRef(true);
/**
* A ref containing the currently present children. When all exit animations
* are complete, we use this to re-render the component with the latest children
* *committed* rather than the latest children *rendered*.
*/
const pendingPresentChildren = useRef(presentChildren);
/**
* Track which exiting children have finished animating out.
*/
const exitComplete = useConstant(() => new Map());
/**
* Track which components are currently processing exit to prevent duplicate processing.
*/
const exitingComponents = useRef(new Set());
/**
* Save children to render as React state. To ensure this component is concurrent-safe,
* we check for exiting children via an effect.
*/
const [diffedChildren, setDiffedChildren] = useState(presentChildren);
const [renderedChildren, setRenderedChildren] = useState(presentChildren);
useIsomorphicLayoutEffect(() => {
isInitialRender.current = false;
pendingPresentChildren.current = presentChildren;
/**
* Update complete status of exiting children.
*/
for (let i = 0; i < renderedChildren.length; i++) {
const key = getChildKey(renderedChildren[i]);
if (!presentKeys.includes(key)) {
if (exitComplete.get(key) !== true) {
exitComplete.set(key, false);
}
}
else {
exitComplete.delete(key);
exitingComponents.current.delete(key);
}
}
}, [renderedChildren, presentKeys.length, presentKeys.join("-")]);
const exitingChildren = [];
if (presentChildren !== diffedChildren) {
let nextChildren = [...presentChildren];
/**
* Loop through all the currently rendered components and decide which
* are exiting.
*/
for (let i = 0; i < renderedChildren.length; i++) {
const child = renderedChildren[i];
const key = getChildKey(child);
if (!presentKeys.includes(key)) {
nextChildren.splice(i, 0, child);
exitingChildren.push(child);
}
}
/**
* If we're in "wait" mode, and we have exiting children, we want to
* only render these until they've all exited.
*/
if (mode === "wait" && exitingChildren.length) {
nextChildren = exitingChildren;
}
setRenderedChildren(onlyElements(nextChildren));
setDiffedChildren(presentChildren);
/**
* Early return to ensure once we've set state with the latest diffed
* children, we can immediately re-render.
*/
return null;
}
if (process.env.NODE_ENV !== "production" &&
mode === "wait" &&
renderedChildren.length > 1) {
console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`);
}
/**
* If we've been provided a forceRender function by the LayoutGroupContext,
* we can use it to force a re-render amongst all surrounding components once
* all components have finished animating out.
*/
const { forceRender } = useContext(LayoutGroupContext);
return (jsx(Fragment, { children: renderedChildren.map((child) => {
const key = getChildKey(child);
const isPresent = propagate && !isParentPresent
? false
: presentChildren === renderedChildren ||
presentKeys.includes(key);
const onExit = () => {
if (exitingComponents.current.has(key)) {
return;
}
if (exitComplete.has(key)) {
exitingComponents.current.add(key);
exitComplete.set(key, true);
}
else {
return;
}
let isEveryExitComplete = true;
exitComplete.forEach((isExitComplete) => {
if (!isExitComplete)
isEveryExitComplete = false;
});
if (isEveryExitComplete) {
forceRender?.();
setRenderedChildren(pendingPresentChildren.current);
propagate && safeToRemove?.();
onExitComplete && onExitComplete();
}
};
return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial
? undefined
: false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, root: root, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, anchorY: anchorY, children: child }, key));
}) }));
};
export { AnimatePresence };
//# sourceMappingURL=index.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
"use client";
import { useContext } from 'react';
import { PresenceContext } from '../../context/PresenceContext.mjs';
function usePresenceData() {
const context = useContext(PresenceContext);
return context ? context.custom : undefined;
}
export { usePresenceData };
//# sourceMappingURL=use-presence-data.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"use-presence-data.mjs","sources":["../../../../src/components/AnimatePresence/use-presence-data.ts"],"sourcesContent":["\"use client\"\n\nimport { useContext } from \"react\"\nimport { PresenceContext } from \"../../context/PresenceContext\"\n\nexport function usePresenceData() {\n const context = useContext(PresenceContext)\n return context ? context.custom : undefined\n}\n"],"names":[],"mappings":";;;;;AAMI;;AAEJ;;"}

View File

@@ -0,0 +1,72 @@
"use client";
import { useContext, useId, useEffect, useCallback } from 'react';
import { PresenceContext } from '../../context/PresenceContext.mjs';
/**
* When a component is the child of `AnimatePresence`, it can use `usePresence`
* to access information about whether it's still present in the React tree.
*
* ```jsx
* import { usePresence } from "framer-motion"
*
* export const Component = () => {
* const [isPresent, safeToRemove] = usePresence()
*
* useEffect(() => {
* !isPresent && setTimeout(safeToRemove, 1000)
* }, [isPresent])
*
* return <div />
* }
* ```
*
* If `isPresent` is `false`, it means that a component has been removed from the tree,
* but `AnimatePresence` won't really remove it until `safeToRemove` has been called.
*
* @public
*/
function usePresence(subscribe = true) {
const context = useContext(PresenceContext);
if (context === null)
return [true, null];
const { isPresent, onExitComplete, register } = context;
// It's safe to call the following hooks conditionally (after an early return) because the context will always
// either be null or non-null for the lifespan of the component.
const id = useId();
useEffect(() => {
if (subscribe) {
return register(id);
}
}, [subscribe]);
const safeToRemove = useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]);
return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
}
/**
* Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
* There is no `safeToRemove` function.
*
* ```jsx
* import { useIsPresent } from "framer-motion"
*
* export const Component = () => {
* const isPresent = useIsPresent()
*
* useEffect(() => {
* !isPresent && console.log("I've been removed!")
* }, [isPresent])
*
* return <div />
* }
* ```
*
* @public
*/
function useIsPresent() {
return isPresent(useContext(PresenceContext));
}
function isPresent(context) {
return context === null ? true : context.isPresent;
}
export { isPresent, useIsPresent, usePresence };
//# sourceMappingURL=use-presence.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"use-presence.mjs","sources":["../../../../src/components/AnimatePresence/use-presence.ts"],"sourcesContent":["\"use client\"\n\nimport { useCallback, useContext, useEffect, useId } from \"react\"\nimport {\n PresenceContext,\n PresenceContextProps,\n} from \"../../context/PresenceContext\"\n\nexport type SafeToRemove = () => void\n\ntype AlwaysPresent = [true, null]\n\ntype Present = [true]\n\ntype NotPresent = [false, SafeToRemove]\n\n/**\n * When a component is the child of `AnimatePresence`, it can use `usePresence`\n * to access information about whether it's still present in the React tree.\n *\n * ```jsx\n * import { usePresence } from \"framer-motion\"\n *\n * export const Component = () => {\n * const [isPresent, safeToRemove] = usePresence()\n *\n * useEffect(() => {\n * !isPresent && setTimeout(safeToRemove, 1000)\n * }, [isPresent])\n *\n * return <div />\n * }\n * ```\n *\n * If `isPresent` is `false`, it means that a component has been removed from the tree,\n * but `AnimatePresence` won't really remove it until `safeToRemove` has been called.\n *\n * @public\n */\nexport function usePresence(\n subscribe: boolean = true\n): AlwaysPresent | Present | NotPresent {\n const context = useContext(PresenceContext)\n\n if (context === null) return [true, null]\n\n const { isPresent, onExitComplete, register } = context\n\n // It's safe to call the following hooks conditionally (after an early return) because the context will always\n // either be null or non-null for the lifespan of the component.\n\n const id = useId()\n useEffect(() => {\n if (subscribe) {\n return register(id)\n }\n }, [subscribe])\n\n const safeToRemove = useCallback(\n () => subscribe && onExitComplete && onExitComplete(id),\n [id, onExitComplete, subscribe]\n )\n\n return !isPresent && onExitComplete ? [false, safeToRemove] : [true]\n}\n\n/**\n * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.\n * There is no `safeToRemove` function.\n *\n * ```jsx\n * import { useIsPresent } from \"framer-motion\"\n *\n * export const Component = () => {\n * const isPresent = useIsPresent()\n *\n * useEffect(() => {\n * !isPresent && console.log(\"I've been removed!\")\n * }, [isPresent])\n *\n * return <div />\n * }\n * ```\n *\n * @public\n */\nexport function useIsPresent() {\n return isPresent(useContext(PresenceContext))\n}\n\nexport function isPresent(context: PresenceContextProps | null) {\n return context === null ? true : context.isPresent\n}\n"],"names":[],"mappings":";;;;AAgBA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG;AAGF;;AAEsB;;;;AAOtB;;;AAGQ;;AAER;;AAOA;AACJ;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;;AAEC;AACJ;AAEM;AACF;AACJ;;"}

View File

@@ -0,0 +1,15 @@
import { Children, isValidElement } from 'react';
const getChildKey = (child) => child.key || "";
function onlyElements(children) {
const filtered = [];
// We use forEach here instead of map as map mutates the component key by preprending `.$`
Children.forEach(children, (child) => {
if (isValidElement(child))
filtered.push(child);
});
return filtered;
}
export { getChildKey, onlyElements };
//# sourceMappingURL=utils.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.mjs","sources":["../../../../src/components/AnimatePresence/utils.ts"],"sourcesContent":["import { isValidElement, Children, ReactElement, ReactNode } from \"react\"\n\nexport type ComponentKey = string | number\n\nexport const getChildKey = (child: ReactElement<any>): ComponentKey =>\n child.key || \"\"\n\nexport function onlyElements(children: ReactNode): ReactElement<any>[] {\n const filtered: ReactElement<any>[] = []\n\n // We use forEach here instead of map as map mutates the component key by preprending `.$`\n Children.forEach(children, (child) => {\n if (isValidElement(child)) filtered.push(child)\n })\n\n return filtered\n}\n"],"names":[],"mappings":";;AAIO,MAAM,WAAW,GAAG,CAAC,KAAwB,KAChD,KAAK,CAAC,GAAG,IAAI;AAEX,SAAU,YAAY,CAAC,QAAmB,EAAA;IAC5C,MAAM,QAAQ,GAAwB,EAAE;;IAGxC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAI;QACjC,IAAI,cAAc,CAAC,KAAK,CAAC;AAAE,YAAA,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;AACnD,IAAA,CAAC,CAAC;AAEF,IAAA,OAAO,QAAQ;AACnB;;;;"}