react-morpheus 0.1.1 → 0.1.3
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.
- package/README.md +6 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +29 -13
- package/dist/style.css +2 -0
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -5,9 +5,13 @@ A controlled React component for morphing one UI state into another.
|
|
|
5
5
|
`Morpheus` does not manage its own open state. Keep the `expanded` boolean in
|
|
6
6
|
your app state, render your trigger wherever it belongs, and pass the same
|
|
7
7
|
source content to `collapsedContent` so Morpheus can measure and morph from it.
|
|
8
|
+
Overlay clicks and Escape key presses call `onCloseIntent`; update your
|
|
9
|
+
controlled state there. Use `afterClose` for work that should run after the
|
|
10
|
+
closing animation completes.
|
|
8
11
|
|
|
9
12
|
```tsx
|
|
10
13
|
import { Morpheus, MorphAnchor } from "react-morpheus";
|
|
14
|
+
import "react-morpheus/style.css";
|
|
11
15
|
|
|
12
16
|
function Example() {
|
|
13
17
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -24,7 +28,8 @@ function Example() {
|
|
|
24
28
|
direction="bottom"
|
|
25
29
|
anchor={MorphAnchor.TopMiddle}
|
|
26
30
|
expanded={expanded}
|
|
27
|
-
|
|
31
|
+
onCloseIntent={() => setExpanded(false)}
|
|
32
|
+
afterClose={() => console.log("closed")}
|
|
28
33
|
collapsedContent={source}
|
|
29
34
|
expandedContent={<div>Expanded content</div>}
|
|
30
35
|
/>
|
package/dist/index.d.ts
CHANGED
|
@@ -77,8 +77,8 @@ type MorphProps = {
|
|
|
77
77
|
direction: MorphDirection;
|
|
78
78
|
anchor?: MorphAnchor | undefined;
|
|
79
79
|
expanded: boolean;
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
onCloseIntent?: (() => void) | undefined;
|
|
81
|
+
afterClose?: (() => void) | undefined;
|
|
82
82
|
collapsedContent: ReactNode;
|
|
83
83
|
expandedContent: ReactNode;
|
|
84
84
|
className?: string;
|
|
@@ -89,7 +89,7 @@ type MorphProps = {
|
|
|
89
89
|
};
|
|
90
90
|
type MorphOverlayProps = {
|
|
91
91
|
expanded: boolean;
|
|
92
|
-
|
|
92
|
+
onCloseIntent?: (() => void) | undefined;
|
|
93
93
|
color: string;
|
|
94
94
|
opacity: number;
|
|
95
95
|
blur: number;
|
|
@@ -126,14 +126,13 @@ type MorphContentLayersProps = {
|
|
|
126
126
|
sourceOpacityTransition: Transition;
|
|
127
127
|
targetTransition: Transition;
|
|
128
128
|
targetOpacityTransition: Transition;
|
|
129
|
-
onOpen?: (() => void) | undefined;
|
|
130
129
|
};
|
|
131
130
|
type MorphLayerInteractivityProps = {
|
|
132
131
|
"aria-hidden": boolean;
|
|
133
132
|
inert?: boolean;
|
|
134
133
|
};
|
|
135
134
|
|
|
136
|
-
declare function Morpheus({ direction, anchor, expanded,
|
|
135
|
+
declare function Morpheus({ direction, anchor, expanded, onCloseIntent, afterClose, collapsedContent, expandedContent, className, overlayColor, overlayOpacity, overlayBlur, spring, }: MorphProps): react.JSX.Element;
|
|
137
136
|
|
|
138
137
|
declare const defaultAnchorByDirection: Record<MorphDirection, MorphAnchor>;
|
|
139
138
|
declare const morphSpringPresets: {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/component.tsx
|
|
2
2
|
import { motion as motion2 } from "framer-motion";
|
|
3
|
-
import { useEffect, useState as useState2 } from "react";
|
|
3
|
+
import { useEffect, useRef as useRef2, useState as useState2 } from "react";
|
|
4
4
|
|
|
5
5
|
// src/measurement.ts
|
|
6
6
|
import { useLayoutEffect, useRef, useState } from "react";
|
|
@@ -291,12 +291,12 @@ function getOverlayBackgroundColor(color, opacity) {
|
|
|
291
291
|
}
|
|
292
292
|
function MorphOverlay({
|
|
293
293
|
expanded,
|
|
294
|
-
|
|
294
|
+
onCloseIntent,
|
|
295
295
|
color,
|
|
296
296
|
opacity,
|
|
297
297
|
blur
|
|
298
298
|
}) {
|
|
299
|
-
return /* @__PURE__ */ jsx(AnimatePresence, { children: expanded &&
|
|
299
|
+
return /* @__PURE__ */ jsx(AnimatePresence, { children: expanded && onCloseIntent ? /* @__PURE__ */ jsx(
|
|
300
300
|
motion.button,
|
|
301
301
|
{
|
|
302
302
|
type: "button",
|
|
@@ -317,7 +317,7 @@ function MorphOverlay({
|
|
|
317
317
|
backgroundColor: getOverlayBackgroundColor(color, opacity),
|
|
318
318
|
WebkitBackdropFilter: `blur(${blur}px)`
|
|
319
319
|
},
|
|
320
|
-
onClick:
|
|
320
|
+
onClick: onCloseIntent
|
|
321
321
|
}
|
|
322
322
|
) : null });
|
|
323
323
|
}
|
|
@@ -399,8 +399,7 @@ function MorphContentLayers({
|
|
|
399
399
|
sourceTransition,
|
|
400
400
|
sourceOpacityTransition,
|
|
401
401
|
targetTransition,
|
|
402
|
-
targetOpacityTransition
|
|
403
|
-
onOpen
|
|
402
|
+
targetOpacityTransition
|
|
404
403
|
}) {
|
|
405
404
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
406
405
|
/* @__PURE__ */ jsx2(
|
|
@@ -419,7 +418,6 @@ function MorphContentLayers({
|
|
|
419
418
|
...sourceTransition,
|
|
420
419
|
opacity: sourceOpacityTransition
|
|
421
420
|
},
|
|
422
|
-
onClick: expanded ? void 0 : onOpen,
|
|
423
421
|
style: {
|
|
424
422
|
width: collapsedLayerSize.width,
|
|
425
423
|
height: collapsedLayerSize.height,
|
|
@@ -460,8 +458,8 @@ function Morpheus({
|
|
|
460
458
|
direction,
|
|
461
459
|
anchor,
|
|
462
460
|
expanded,
|
|
463
|
-
|
|
464
|
-
|
|
461
|
+
onCloseIntent,
|
|
462
|
+
afterClose,
|
|
465
463
|
collapsedContent,
|
|
466
464
|
expandedContent,
|
|
467
465
|
className = "relative inline-block align-top",
|
|
@@ -473,6 +471,7 @@ function Morpheus({
|
|
|
473
471
|
const measurements = useMorphMeasurements();
|
|
474
472
|
const [animationEnabled, setAnimationEnabled] = useState2(false);
|
|
475
473
|
const [shellOverflowVisible, setShellOverflowVisible] = useState2(false);
|
|
474
|
+
const hasOpenedRef = useRef2(expanded);
|
|
476
475
|
useEffect(() => {
|
|
477
476
|
if (!measurements.ready) {
|
|
478
477
|
return;
|
|
@@ -480,6 +479,18 @@ function Morpheus({
|
|
|
480
479
|
const frame = requestAnimationFrame(() => setAnimationEnabled(true));
|
|
481
480
|
return () => cancelAnimationFrame(frame);
|
|
482
481
|
}, [measurements.ready]);
|
|
482
|
+
useEffect(() => {
|
|
483
|
+
if (!expanded || !onCloseIntent) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const handleKeyDown = (event) => {
|
|
487
|
+
if (event.key === "Escape" && !event.repeat) {
|
|
488
|
+
onCloseIntent();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
492
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
493
|
+
}, [expanded, onCloseIntent]);
|
|
483
494
|
const motionState = getMorphMotionState({
|
|
484
495
|
expanded,
|
|
485
496
|
direction,
|
|
@@ -496,7 +507,7 @@ function Morpheus({
|
|
|
496
507
|
MorphOverlay,
|
|
497
508
|
{
|
|
498
509
|
expanded,
|
|
499
|
-
|
|
510
|
+
onCloseIntent,
|
|
500
511
|
color: overlayColor,
|
|
501
512
|
opacity: overlayOpacity,
|
|
502
513
|
blur: overlayBlur
|
|
@@ -512,7 +523,7 @@ function Morpheus({
|
|
|
512
523
|
expandedContent
|
|
513
524
|
}
|
|
514
525
|
),
|
|
515
|
-
/* @__PURE__ */ jsx2("div", { className: "relative mx-auto block w-fit align-top", children: shouldRenderStaticSource ? /* @__PURE__ */ jsx2("div", { className: "inline-block align-top",
|
|
526
|
+
/* @__PURE__ */ jsx2("div", { className: "relative mx-auto block w-fit align-top", children: shouldRenderStaticSource ? /* @__PURE__ */ jsx2("div", { className: "inline-block align-top", children: collapsedContent }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
516
527
|
/* @__PURE__ */ jsx2(
|
|
517
528
|
"div",
|
|
518
529
|
{
|
|
@@ -535,7 +546,13 @@ function Morpheus({
|
|
|
535
546
|
spring: motionState.activeSpring,
|
|
536
547
|
onMorphComplete: () => {
|
|
537
548
|
if (expanded) {
|
|
549
|
+
hasOpenedRef.current = true;
|
|
538
550
|
setShellOverflowVisible(true);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (hasOpenedRef.current) {
|
|
554
|
+
hasOpenedRef.current = false;
|
|
555
|
+
afterClose?.();
|
|
539
556
|
}
|
|
540
557
|
},
|
|
541
558
|
onMorphStart: () => setShellOverflowVisible(false),
|
|
@@ -555,8 +572,7 @@ function Morpheus({
|
|
|
555
572
|
sourceTransition: motionState.sourceTransition,
|
|
556
573
|
sourceOpacityTransition: motionState.sourceOpacityTransition,
|
|
557
574
|
targetTransition: motionState.targetTransition,
|
|
558
|
-
targetOpacityTransition: motionState.targetOpacityTransition
|
|
559
|
-
onOpen
|
|
575
|
+
targetOpacityTransition: motionState.targetOpacityTransition
|
|
560
576
|
}
|
|
561
577
|
)
|
|
562
578
|
}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer utilities{.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:0}.inset-x-0{inset-inline:0}.top-0{top:0}.z-0{z-index:0}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-inline:auto}.block{display:block}.hidden{display:none}.inline-block{display:inline-block}.w-fit{width:fit-content}.cursor-default{cursor:default}.align-top{vertical-align:top}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.\[\&\>\*\:first-child\]\:\!border-transparent>:first-child{border-color:#0000!important}.\[\&\>\*\:first-child\]\:\!bg-transparent>:first-child{background-color:#0000!important}.\[\&\>\*\:first-child\]\:\!shadow-none>:first-child{--tw-shadow:0 0 #0000!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-morpheus",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "A React component for morphing one surface into other.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"sideEffects":
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css"
|
|
9
|
+
],
|
|
8
10
|
"files": [
|
|
9
11
|
"dist",
|
|
10
12
|
"README.md"
|
|
@@ -12,16 +14,21 @@
|
|
|
12
14
|
"main": "./dist/index.js",
|
|
13
15
|
"module": "./dist/index.js",
|
|
14
16
|
"types": "./dist/index.d.ts",
|
|
17
|
+
"style": "./dist/style.css",
|
|
15
18
|
"exports": {
|
|
16
19
|
".": {
|
|
17
20
|
"types": "./dist/index.d.ts",
|
|
18
21
|
"import": "./dist/index.js"
|
|
19
22
|
},
|
|
23
|
+
"./style.css": "./dist/style.css",
|
|
20
24
|
"./package.json": "./package.json"
|
|
21
25
|
},
|
|
22
26
|
"scripts": {
|
|
23
|
-
"build": "
|
|
27
|
+
"build": "bun run build:js && bun run build:css",
|
|
28
|
+
"build:js": "tsup src/index.ts --format esm --dts --external react --external react-dom --external framer-motion --clean",
|
|
29
|
+
"build:css": "tailwindcss -i src/style.css -o dist/style.css --minify",
|
|
24
30
|
"check": "tsc --noEmit",
|
|
31
|
+
"publish:npm": "bun run scripts/publish.ts",
|
|
25
32
|
"release:minor": "bun run scripts/release.ts minor",
|
|
26
33
|
"release:major": "bun run scripts/release.ts major",
|
|
27
34
|
"release:patch": "bun run scripts/release.ts patch"
|
|
@@ -32,12 +39,14 @@
|
|
|
32
39
|
"react-dom": "^19.0.0"
|
|
33
40
|
},
|
|
34
41
|
"devDependencies": {
|
|
42
|
+
"@tailwindcss/cli": "^4.3.3",
|
|
35
43
|
"@types/bun": "^1.3.14",
|
|
36
44
|
"@types/react": "^19.2.17",
|
|
37
45
|
"@types/react-dom": "^19.2.3",
|
|
38
46
|
"framer-motion": "^12.42.2",
|
|
39
47
|
"react": "^19.2.7",
|
|
40
48
|
"react-dom": "^19.2.7",
|
|
49
|
+
"tailwindcss": "^4.3.3",
|
|
41
50
|
"tsup": "^8.5.1",
|
|
42
51
|
"typescript": "^6.0.3"
|
|
43
52
|
},
|