@flemo/react 1.12.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,6 @@ export interface UseNavigateOptions {
9
9
  router?: RouterTarget;
10
10
  }
11
11
  type RouteOptions = DistanceOptions & UseNavigateOptions & {
12
- layoutId?: string | number;
13
12
  transitionName?: TransitionName;
14
13
  };
15
14
  type PopRouteOptions = DistanceOptions & UseNavigateOptions & {
@@ -0,0 +1,40 @@
1
+ import { PropsWithChildren } from 'react';
2
+ export type LayerProps = PropsWithChildren;
3
+ /**
4
+ * Render an overlay beside the screen instead of inside it, so it can cover
5
+ * the shared bars.
6
+ *
7
+ * A screen that is moving carries a transform, and a transform is both a
8
+ * containing block for `position: fixed` descendants and a stacking context
9
+ * around all of them. The shared bars live outside the screen, as siblings. So
10
+ * a sheet written inside the screen is ONE atom with the screen's content as
11
+ * far as the bars are concerned, and a stacking context cannot be interleaved
12
+ * with an element outside it: "content under the bar, sheet over the bar" is
13
+ * not expressible from in there, at any z-index.
14
+ *
15
+ * ```tsx
16
+ * <Screen sharedBottomBar={<TabBar />}>
17
+ * <Content />
18
+ * <Layer>
19
+ * <BottomSheet open={open} />
20
+ * </Layer>
21
+ * </Screen>
22
+ * ```
23
+ *
24
+ * What leaves the screen is the PAINT ORDER, and only that. The slot keeps its
25
+ * owner's stack position, its status and transition (so it moves with the
26
+ * screen and leaves with it), its animation hold, and its paint-hidden state.
27
+ * It is rendered from inside the screen's own React subtree, so React freezes
28
+ * it with the screen and unmounts it with the screen without being asked.
29
+ *
30
+ * At rest none of this is needed: a screen at rest carries no transform, so a
31
+ * consumer's `position: fixed` overlay already resolves against the viewport
32
+ * and already outranks the bars with a z-index of its own. `<Layer>` is for
33
+ * the overlay that has to survive the screen MOVING under it, and for the one
34
+ * that has to clear chrome an ancestor screen declared.
35
+ *
36
+ * Children keep whatever positioning they had. On the server, and for the
37
+ * first render before the host mounts, this renders nothing.
38
+ */
39
+ declare function Layer({ children }: LayerProps): import('react').ReactPortal | null;
40
+ export default Layer;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The element a `<Layer>` portals into. Null before the outermost screen
3
+ * mounts and on the server, where no host element exists yet — a `<Layer>`
4
+ * renders nothing until it has a target.
5
+ */
6
+ declare const LayerHostContext: import('react').Context<HTMLElement | null>;
7
+ export declare function useLayerHost(): HTMLElement | null;
8
+ /** Everything a slot needs to keep being its screen while sitting outside it. */
9
+ export interface LayerOwner {
10
+ /**
11
+ * The owning screen's stack position. Two screens' overlays then order the
12
+ * way their screens do, rather than by whichever portal mounted first.
13
+ */
14
+ zIndex: number;
15
+ /**
16
+ * The owner's paint state. `visibility: hidden` on the screen container is
17
+ * plain CSS and stops at that container's own descendants, so a slot in
18
+ * another box has to be told. Without it a covered screen's overlay is the
19
+ * one thing still painting — for the whole debounce window before the freeze
20
+ * catches up (see ScreenFreeze.portal.test.tsx).
21
+ */
22
+ paintHidden: boolean;
23
+ /** The transition, status and active flags the compiled slot rule selects on. */
24
+ transitionName: string;
25
+ status: string;
26
+ isActive: boolean;
27
+ /**
28
+ * The owner's animation hold. The slot pauses at the same from-pose as the
29
+ * screen, so a flight that is held opens with its overlay rather than after
30
+ * it.
31
+ */
32
+ animHold: string;
33
+ /**
34
+ * Whether this owner is ALSO the screen that renders the host.
35
+ *
36
+ * An overlay has to travel with whatever is actually moving under it, and
37
+ * that is not always its owner. When an ancestor screen flies — a push on an
38
+ * outer Router, with the owner sitting inside it at rest — the thing that
39
+ * moves is the ancestor, and the host is inside the ancestor's container. So
40
+ * the HOST rides the screen that renders it, and a slot rides its owner only
41
+ * when the two are different screens. Otherwise the pair would both animate
42
+ * and the overlay would travel twice as far as its screen.
43
+ *
44
+ * Measured in a consumer app before this existed: a sheet in a nested screen
45
+ * sat perfectly still while the whole region slid out from under it, because
46
+ * its owner's status was IDLE for the entire flight and the compiled rule had
47
+ * nothing to match.
48
+ */
49
+ rendersHost: boolean;
50
+ /**
51
+ * The owning screen's id, written onto the slot so the gesture driver can
52
+ * find it. A drag does not go through the compiled rules — it writes inline
53
+ * styles frame by frame — and it enumerates what rides along by walking the
54
+ * moving screen's container. A slot is not in that container, so it has to
55
+ * name its owner instead.
56
+ */
57
+ screenId: string;
58
+ /**
59
+ * Register a slot element with its owner, for the lifetime of the slot.
60
+ *
61
+ * The owner needs to know it HAS an escaped overlay, because the dim has to
62
+ * follow the overlay out. A screen's decorator lives in that screen's
63
+ * container and covers everything inside it, so an overlay that left the
64
+ * container leaves the dim behind — measured: an inline sheet is covered by
65
+ * the dim, the same sheet through `<Layer>` is not. And a consumer writes
66
+ * that decorator with `createDecorator`, so it can be anything, including
67
+ * fully opaque. A screen that vanishes under its own decorator while its
68
+ * sheet floats untouched is not a cosmetic difference.
69
+ *
70
+ * The dim copy exists only while a slot does. Rendered unconditionally it
71
+ * would paint over the shared bars for every flight, overlay or not.
72
+ */
73
+ registerSlot: (element: HTMLElement | null) => void;
74
+ }
75
+ declare const LayerOwnerContext: import('react').Context<LayerOwner | null>;
76
+ export declare function useLayerOwner(): LayerOwner | null;
77
+ export { LayerHostContext, LayerOwnerContext };
@@ -0,0 +1,14 @@
1
+ import { ComponentPropsWithRef, JSX, PropsWithChildren } from 'react';
2
+ import { MorphTransitionName } from '@flemo/core';
3
+ export interface MorphProps extends PropsWithChildren<ComponentPropsWithRef<"div">> {
4
+ layoutId: string | number;
5
+ name?: MorphTransitionName;
6
+ as?: keyof JSX.IntrinsicElements;
7
+ }
8
+ declare function Morph({ ref, layoutId, name, as, style, children, ...props }: MorphProps): import('react').ReactElement<{
9
+ "data-flemo-morph-slot": string;
10
+ style: {
11
+ display: string;
12
+ };
13
+ }, string | import('react').JSXElementConstructor<any>>;
14
+ export default Morph;
@@ -0,0 +1,6 @@
1
+ import { FlemoStores } from '@flemo/core';
2
+ export interface MorphLayerProps {
3
+ stores: FlemoStores;
4
+ }
5
+ declare function MorphLayer({ stores }: MorphLayerProps): import("react").JSX.Element;
6
+ export default MorphLayer;
@@ -0,0 +1 @@
1
+ export { default, type MorphLayerProps } from './MorphLayer';
@@ -1,6 +1,12 @@
1
1
  import { ComponentPropsWithoutRef, PropsWithChildren, ReactNode } from 'react';
2
2
  import { SharedBarId } from '@flemo/core';
3
3
  export interface ScreenProps extends PropsWithChildren<Omit<ComponentPropsWithoutRef<"div">, "onPointerDown" | "onPointerMove" | "onPointerUp" | "onPointerCancel">> {
4
+ /**
5
+ * Covered: stop painting this screen, from this commit. Written by the
6
+ * binding, never by a consumer — the prop exists so <Screen> can hand the
7
+ * decision to the container ScreenMotion renders.
8
+ */
9
+ paintHidden?: boolean;
4
10
  statusBarHeight?: string;
5
11
  statusBarColor?: string;
6
12
  systemNavigationBarHeight?: string;
@@ -1,7 +1,19 @@
1
1
  import { ReactNode } from 'react';
2
+ import { ScreenFreezeMode } from '@flemo/core';
2
3
  interface ScreenFreezeProps {
3
4
  freeze: boolean;
5
+ /**
6
+ * WHY this screen is freezing, which decides whether the hide may wait.
7
+ *
8
+ * Only the JUST-COVERED screen can be re-revealed by a pop, so only its hide
9
+ * can be the one a pop has to undo — and the debounce below exists for
10
+ * exactly that round trip. A DEEP screen is never what a pop wakes, so
11
+ * waiting buys it nothing and costs the thing the delay is invisible for
12
+ * right up until it is not: for the length of the wait it is still PAINTING,
13
+ * under whatever is on top of it.
14
+ */
15
+ mode?: ScreenFreezeMode;
4
16
  children: ReactNode;
5
17
  }
6
- declare function ScreenFreeze({ freeze, children }: ScreenFreezeProps): import("react").JSX.Element;
18
+ declare function ScreenFreeze({ freeze, mode, children }: ScreenFreezeProps): import("react").JSX.Element;
7
19
  export default ScreenFreeze;
@@ -1,3 +1,3 @@
1
1
  import { ScreenProps } from './Screen';
2
- declare function ScreenMotion({ children, statusBarHeight, statusBarColor, systemNavigationBarHeight, systemNavigationBarColor, sharedTopBar, sharedTopBarId, sharedBottomBar, sharedBottomBarId, topBar, bottomBar, hideStatusBar, hideSystemNavigationBar, backgroundColor, contentScrollable, ...props }: ScreenProps): import("react").JSX.Element;
2
+ declare function ScreenMotion({ children, statusBarHeight, statusBarColor, systemNavigationBarHeight, systemNavigationBarColor, sharedTopBar, sharedTopBarId, sharedBottomBar, sharedBottomBarId, topBar, bottomBar, hideStatusBar, hideSystemNavigationBar, backgroundColor, contentScrollable, paintHidden, ...props }: ScreenProps): import("react").JSX.Element;
3
3
  export default ScreenMotion;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,2 +1,2 @@
1
- import { Decorator, PartTransition, Transition } from '@flemo/core';
2
- export default function useTransitionStyles(transitions: Transition[], decorators: Decorator[], partTransitions?: PartTransition[]): void;
1
+ import { Decorator, MorphTransition, PartTransition, Transition } from '@flemo/core';
2
+ export default function useTransitionStyles(transitions: Transition[], decorators: Decorator[], partTransitions?: PartTransition[], morphTransitions?: MorphTransition[]): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flemo/react",
3
- "version": "1.12.7",
3
+ "version": "2.0.0",
4
4
  "description": "React bindings for flemo: Router, Route, Screen, and the screen-transition runtime.",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -34,28 +34,28 @@
34
34
  "homepage": "https://flemo.dev",
35
35
  "license": "MIT",
36
36
  "dependencies": {
37
+ "@flemo/core": "2.0.0",
37
38
  "path-to-regexp": "^8.4.2",
38
- "zustand": "^5.0.14",
39
- "@flemo/core": "1.29.0"
39
+ "zustand": "^5.0.15"
40
40
  },
41
41
  "devDependencies": {
42
+ "@flemo/eslint-config": "0.0.0",
43
+ "@flemo/tsconfig": "0.0.0",
42
44
  "@testing-library/jest-dom": "^6.9.1",
43
45
  "@testing-library/react": "^16.3.2",
44
46
  "@types/node": "^24.13.1",
45
47
  "@types/react": "^19.2.18",
46
48
  "@types/react-dom": "^19.2.4",
47
49
  "@vitejs/plugin-react-swc": "^4.3.3",
48
- "@vitest/coverage-v8": "^4.1.10",
50
+ "@vitest/coverage-v8": "^4.1.11",
49
51
  "eslint": "^9.39.5",
50
52
  "jsdom": "^29.1.1",
51
53
  "react": "^19.2.8",
52
54
  "react-dom": "^19.2.8",
53
55
  "typescript": "^6.0.3",
54
- "vite": "^8.2.1",
56
+ "vite": "^8.2.2",
55
57
  "vite-plugin-dts": "^5.0.3",
56
- "vitest": "^4.1.10",
57
- "@flemo/eslint-config": "0.0.0",
58
- "@flemo/tsconfig": "0.0.0"
58
+ "vitest": "^4.1.11"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "react": "^19.2.8",