react-morpheus 0.1.0 → 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 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
- onClose={() => setExpanded(false)}
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
- onOpen?: (() => void) | undefined;
81
- onClose?: (() => void) | undefined;
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
- onClose?: (() => void) | undefined;
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, onOpen, onClose, collapsedContent, expandedContent, className, overlayColor, overlayOpacity, overlayBlur, spring, }: MorphProps): react.JSX.Element;
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
- onClose,
294
+ onCloseIntent,
295
295
  color,
296
296
  opacity,
297
297
  blur
298
298
  }) {
299
- return /* @__PURE__ */ jsx(AnimatePresence, { children: expanded && onClose ? /* @__PURE__ */ jsx(
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: onClose
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
- onOpen,
464
- onClose,
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
- onClose,
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", onClick: onOpen, children: collapsedContent }) : /* @__PURE__ */ jsxs(Fragment, { children: [
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.0",
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": false,
7
+ "sideEffects": [
8
+ "**/*.css"
9
+ ],
8
10
  "files": [
9
11
  "dist",
10
12
  "README.md"
@@ -12,18 +14,24 @@
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": "tsup src/index.ts --format esm --dts --external react --external react-dom --external framer-motion --clean",
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
- "release:major": "bun run scripts/release.ts major"
33
+ "release:major": "bun run scripts/release.ts major",
34
+ "release:patch": "bun run scripts/release.ts patch"
27
35
  },
28
36
  "peerDependencies": {
29
37
  "framer-motion": "^12.42.2",
@@ -31,13 +39,23 @@
31
39
  "react-dom": "^19.0.0"
32
40
  },
33
41
  "devDependencies": {
42
+ "@tailwindcss/cli": "^4.3.3",
34
43
  "@types/bun": "^1.3.14",
35
44
  "@types/react": "^19.2.17",
36
45
  "@types/react-dom": "^19.2.3",
37
46
  "framer-motion": "^12.42.2",
38
47
  "react": "^19.2.7",
39
48
  "react-dom": "^19.2.7",
49
+ "tailwindcss": "^4.3.3",
40
50
  "tsup": "^8.5.1",
41
51
  "typescript": "^6.0.3"
42
- }
52
+ },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+ssh://git@github.com/shivekkhurana/react-morpheus.git"
56
+ },
57
+ "bugs": {
58
+ "url": "https://github.com/shivekkhurana/react-morpheus/issues"
59
+ },
60
+ "homepage": "https://github.com/shivekkhurana/react-morpheus#readme"
43
61
  }