Skip to content

Commit b60b112

Browse files
committed
Change API of useWindowManager to take an object
1 parent d68e68c commit b60b112

5 files changed

Lines changed: 91 additions & 45 deletions

File tree

README.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
**A React hook for opening, managing and communicating with a child window in a type-safe manner.**
66

7-
![QA](https://img.shields.io/github/actions/workflow/status/davidsteiner/react-presentation-hook/ci.yml)
8-
![NPM](https://img.shields.io/npm/v/react-presentation-hook)
9-
![GitHub License](https://img.shields.io/github/license/davidsteiner/react-presentation-hook)
7+
[![QA](https://img.shields.io/github/actions/workflow/status/davidsteiner/react-presentation-hook/ci.yml)](https://github.com/davidsteiner/react-presentation-hook/actions)
8+
[![NPM](https://img.shields.io/npm/v/react-presentation-hook)](https://www.npmjs.com/package/react-presentation-hook)
9+
[![GitHub License](https://img.shields.io/github/license/davidsteiner/react-presentation-hook)](LICENSE)
1010

1111
</div>
1212

@@ -16,6 +16,14 @@ This library can be used for presentations, where the presented view needs to be
1616

1717
While the primary use-case is presentations, this is a generic implementation which can be used for any scenario where a controlled additional window is needed with bi-directional communication with the parent window.
1818

19+
## Installation
20+
21+
Install `react-presentation-hook` using your favourite package manager.
22+
23+
```shell
24+
npm i react-presentation-hook
25+
```
26+
1927
## How to use it?
2028

2129
The library provides two convenient hooks for opening a child window, establishing
@@ -24,3 +32,32 @@ in either direction.
2432

2533
These two hooks are `useWindowManager` and `useChildWindowManager` for the parent
2634
window and the child window respectively.
35+
36+
### In the parent window
37+
38+
Implement a message handler function, and pass it to the hook:
39+
40+
```typescript
41+
function ParentComponent() {
42+
const onMessage = useCallback(() => {
43+
...
44+
}, [...]);
45+
46+
const { openChildWindow, closeChildWindow, isConnected, sendMessage } =
47+
useWindowManager<PresentationState, ParentMessage, ChildMessage>({
48+
onMessage,
49+
});
50+
}
51+
```
52+
53+
You can use `openChildWindow` and `closeChildWindow` to open and close the child window
54+
respectively.
55+
56+
`isConnected` indicates whether the child window is open and a connection
57+
has been established. `sendMessage` can be used to send messages
58+
to the child window. It will be undefined if a connection has not been
59+
established.
60+
61+
By default, the child window will be automatically closed when the
62+
parent window closes. To leave it open, pass `{ onMessage, closeWithParent: false }`
63+
to `useWindowManager`.

examples/vite-tanstack-app/src/routes/index.tsx

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createFileRoute } from "@tanstack/react-router";
22

3-
import { useState } from "react";
3+
import { useCallback, useEffect, useState } from "react";
44
import { useWindowManager } from "react-presentation-hook";
55

66
import { MonitorUp, MonitorX } from "lucide-react";
@@ -23,65 +23,68 @@ function Index() {
2323
}
2424
);
2525

26-
const onMessage = ({ type }: ChildMessage) => {
27-
switch (type) {
28-
case "move-next": {
29-
onNext();
30-
break;
31-
}
32-
case "move-back": {
33-
onBack();
34-
break;
35-
}
36-
}
37-
};
38-
39-
const { openChildWindow, closeChildWindow, isConnected, sendMessage } =
40-
useWindowManager<PresentationState, ParentMessage, ChildMessage>(onMessage);
41-
42-
const onOpen = () => {
43-
openChildWindow({
44-
url: "/presented-view",
45-
initialState: presentationState,
46-
});
47-
};
48-
49-
const onNext = () => {
26+
const onBack = useCallback(() => {
5027
setPresentationState((previousState) => {
51-
const newIndex = previousState.currentIndex + 1;
28+
const newIndex = previousState.currentIndex - 1;
5229
if (newIndex < slides.length) {
53-
const newState = {
30+
return {
5431
currentIndex: newIndex,
5532
currentSlide: slides[newIndex],
5633
length: slides.length,
5734
};
58-
if (sendMessage) {
59-
sendMessage({ newState });
60-
}
61-
return newState;
6235
}
6336
return previousState;
6437
});
65-
};
38+
}, []);
6639

67-
const onBack = () => {
40+
const onNext = useCallback(() => {
6841
setPresentationState((previousState) => {
69-
const newIndex = previousState.currentIndex - 1;
42+
const newIndex = previousState.currentIndex + 1;
7043
if (newIndex < slides.length) {
71-
const newState = {
44+
return {
7245
currentIndex: newIndex,
7346
currentSlide: slides[newIndex],
7447
length: slides.length,
7548
};
76-
if (sendMessage) {
77-
sendMessage({ newState });
78-
}
79-
return newState;
8049
}
8150
return previousState;
8251
});
52+
}, []);
53+
54+
const onMessage = useCallback(
55+
({ type }: ChildMessage) => {
56+
switch (type) {
57+
case "move-next": {
58+
onNext();
59+
break;
60+
}
61+
case "move-back": {
62+
onBack();
63+
break;
64+
}
65+
}
66+
},
67+
[onBack, onNext]
68+
);
69+
70+
const { openChildWindow, closeChildWindow, isConnected, sendMessage } =
71+
useWindowManager<PresentationState, ParentMessage, ChildMessage>({
72+
onMessage,
73+
});
74+
75+
const onOpen = () => {
76+
openChildWindow({
77+
url: "/presented-view",
78+
initialState: presentationState,
79+
});
8380
};
8481

82+
useEffect(() => {
83+
if (sendMessage) {
84+
sendMessage({ newState: presentationState });
85+
}
86+
}, [presentationState, sendMessage]);
87+
8588
return (
8689
<div className="p-4 my-8 flex flex-col bg-gradient-to-b from-gray-50 to-gray-100 rounded-lg">
8790
<div className="h-96 pb-8">

lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-presentation-hook",
3-
"version": "0.0.6",
3+
"version": "0.1.0",
44
"type": "module",
55
"repository": {
66
"type": "git",

lib/src/use-window-manager.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,17 @@ type ChildWindowState<TInitialState, TParentMessage> =
4444
| ReadyState<TInitialState, TParentMessage>
4545
| UnopenedState;
4646

47+
export type WindowManagerOptions<TCustomInboundPayload> = {
48+
onMessage: (message: TCustomInboundPayload) => void;
49+
closeWithParent?: boolean;
50+
};
51+
4752
export function useWindowManager<
4853
TInitialState,
4954
TCustomOutboundPayload,
5055
TCustomInboundPayload,
51-
>(onMessage: (message: TCustomInboundPayload) => void, closeWithParent = true) {
56+
>(options: WindowManagerOptions<TCustomInboundPayload>) {
57+
const { onMessage, closeWithParent = true } = options;
5258
const [childWindowState, setChildWindowState] = useState<
5359
ChildWindowState<TInitialState, TCustomOutboundPayload>
5460
>({ type: "unopened" });

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)