react-morpheus 0.1.9 → 0.1.11

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
@@ -1,5 +1,7 @@
1
1
  # React Morpheus
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/react-morpheus.svg)](https://www.npmjs.com/package/react-morpheus)
4
+
3
5
  A controlled React component for morphing one UI state into another.
4
6
 
5
7
  React Morpheus exists for interfaces where a control should become the next
@@ -17,17 +19,21 @@ https://github.com/user-attachments/assets/f90e7096-f587-4793-8e80-476cf844569d
17
19
  ## Installation
18
20
 
19
21
  ```sh
20
- bun add react-morpheus
22
+ bun add react-morpheus cuelume
21
23
  ```
22
24
 
23
25
  ```sh
24
- npm i react-morpheus
26
+ npm i react-morpheus cuelume
25
27
  ```
26
28
 
27
29
  ```sh
28
- yarn add react-morpheus
30
+ yarn add react-morpheus cuelume
29
31
  ```
30
32
 
33
+ [Cuelume](https://github.com/Danilaa1/cuelume) is a required peer dependency;
34
+ sound playback remains opt-in. It synthesizes cues with the Web Audio API, so
35
+ React Morpheus does not bundle audio files.
36
+
31
37
  ## Usage
32
38
 
33
39
  Import the component and stylesheet, keep the open state in your app, and pass
@@ -55,6 +61,8 @@ function Example() {
55
61
  anchor={MorphAnchor.TopMiddle}
56
62
  expanded={expanded}
57
63
  onClose={() => setExpanded(false)}
64
+ openSound="bloom"
65
+ closeSound="droplet"
58
66
  overlayColor="#0f172a"
59
67
  overlayOpacity={0.18}
60
68
  overlayBlur={3}
@@ -81,6 +89,31 @@ the source component's own click handler. Overlay clicks call `onClose`; update
81
89
  your controlled state there. Use `beforeClose` and `afterClose` for work that
82
90
  should run around the closing animation.
83
91
 
92
+ ## Sounds
93
+
94
+ Use `openSound` and `closeSound` to add Cuelume cues to the controlled state
95
+ transition. React Morpheus calls Cuelume directly; you do not need to call
96
+ `bind()` or `play()` yourself.
97
+
98
+ ```tsx
99
+ <Morpheus
100
+ expanded={expanded}
101
+ openSound="bloom"
102
+ closeSound="droplet"
103
+ // ...the remaining props
104
+ />
105
+ ```
106
+
107
+ Each configured cue plays once when `expanded` changes. The initial render is
108
+ silent, including when `expanded` is initially `true`. Omit either prop to keep
109
+ that direction silent.
110
+
111
+ Both props accept Cuelume's `SoundName` values:
112
+
113
+ `arrival`, `bloom`, `chime`, `droplet`, `error`, `loading`, `page`, `press`,
114
+ `pulse`, `ready`, `release`, `scan`, `sparkle`, `success`, `tick`, `toggle`, and
115
+ `whisper`.
116
+
84
117
  ## Props
85
118
 
86
119
  | Prop | Type | Required | Notes |
@@ -90,6 +123,8 @@ should run around the closing animation.
90
123
  | `onClose` | `() => void` | Optional | Called when the overlay requests closing. Use it to update your controlled open state. |
91
124
  | `beforeClose` | `() => void` | Optional | Called when the closing animation starts, after `expanded` has changed to `false`. Useful for close-intent side effects. |
92
125
  | `afterClose` | `() => void` | Optional | Called after the closing animation completes. |
126
+ | `openSound` | `SoundName` | Optional | Cuelume sound played once when `expanded` changes to `true`. |
127
+ | `closeSound` | `SoundName` | Optional | Cuelume sound played once when `expanded` changes to `false`. |
93
128
  | `collapsedContent` | `ReactNode` | Required | The source surface Morpheus measures, renders, and uses as the opener. |
94
129
  | `expandedContent` | `ReactNode` | Required | The destination surface Morpheus measures and animates into. |
95
130
  | `className` | `string` | Optional | Classes applied to the root wrapper. Useful for block layout and responsive width constraints. |
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, RefObject } from 'react';
3
+ import { SoundName } from 'cuelume';
3
4
  import { Transition } from 'framer-motion';
4
5
 
5
6
  declare enum MorphAnchor {
@@ -81,6 +82,8 @@ type MorphProps = {
81
82
  onClose?: (() => void) | undefined;
82
83
  beforeClose?: (() => void) | undefined;
83
84
  afterClose?: (() => void) | undefined;
85
+ openSound?: SoundName | undefined;
86
+ closeSound?: SoundName | undefined;
84
87
  collapsedContent: ReactNode;
85
88
  expandedContent: ReactNode;
86
89
  className?: string;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/component.tsx
2
+ import { play } from "cuelume";
2
3
  import { motion as motion2 } from "framer-motion";
3
4
  import {
4
5
  createContext,
@@ -652,6 +653,8 @@ function MorpheusRoot({
652
653
  onClose,
653
654
  beforeClose,
654
655
  afterClose,
656
+ openSound,
657
+ closeSound,
655
658
  collapsedContent,
656
659
  expandedContent,
657
660
  className = "rmorph:relative rmorph:inline-block rmorph:align-top",
@@ -682,6 +685,9 @@ function MorpheusRoot({
682
685
  const beforeCloseRef = useRef2(beforeClose);
683
686
  const afterCloseRef = useRef2(afterClose);
684
687
  const previousExpandedRef = useRef2(expanded);
688
+ const previousSoundExpandedRef = useRef2(expanded);
689
+ const openSoundRef = useRef2(openSound);
690
+ const closeSoundRef = useRef2(closeSound);
685
691
  const portalStartFrameRef = useRef2(null);
686
692
  const handoffFirstFrameRef = useRef2(null);
687
693
  const handoffSecondFrameRef = useRef2(null);
@@ -691,6 +697,18 @@ function MorpheusRoot({
691
697
  expandedRef.current = expanded;
692
698
  beforeCloseRef.current = beforeClose;
693
699
  afterCloseRef.current = afterClose;
700
+ openSoundRef.current = openSound;
701
+ closeSoundRef.current = closeSound;
702
+ useEffect(() => {
703
+ if (previousSoundExpandedRef.current === expanded) {
704
+ return;
705
+ }
706
+ previousSoundExpandedRef.current = expanded;
707
+ const sound = expanded ? openSoundRef.current : closeSoundRef.current;
708
+ if (sound) {
709
+ play(sound);
710
+ }
711
+ }, [expanded]);
694
712
  const cancelPortalFrames = useCallback(() => {
695
713
  if (portalStartFrameRef.current !== null) {
696
714
  cancelAnimationFrame(portalStartFrameRef.current);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-morpheus",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "A controlled React component for morphing one UI surface into another.",
5
5
  "keywords": [
6
6
  "react",
@@ -38,7 +38,7 @@
38
38
  "scripts": {
39
39
  "prepare": "bun run build",
40
40
  "build": "bun run build:js && bun run build:css",
41
- "build:js": "tsup src/index.ts --format esm --dts --external react --external react-dom --external framer-motion --clean",
41
+ "build:js": "tsup src/index.ts --format esm --dts --external react --external react-dom --external framer-motion --external cuelume --clean",
42
42
  "build:css": "tailwindcss -i src/style.css -o dist/style.css --minify",
43
43
  "check": "tsc --noEmit",
44
44
  "test": "bun test",
@@ -48,6 +48,7 @@
48
48
  "release:patch": "bun run scripts/release.ts patch"
49
49
  },
50
50
  "peerDependencies": {
51
+ "cuelume": "^0.2.2",
51
52
  "framer-motion": "^12.42.2",
52
53
  "react": "^19.0.0",
53
54
  "react-dom": "^19.0.0"