Skip to content

Commit 5af645b

Browse files
committed
feat: Added use-disclosure-data.
1 parent 04f027b commit 5af645b

File tree

5 files changed

+205
-3
lines changed

5 files changed

+205
-3
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/
6262
- [x] ~~use-debounced-state~~ use-debounced-signal
6363
- [x] use-debounced-value
6464
- [x] use-did-update
65-
- [ ] use-disclosure
65+
- [x] use-disclosure (✨ Improved, slightly better than mantine thanks to `set` for passing to stuff like `onOpenChange`)
6666
- [x] use-document-title
6767
- [x] use-document-visibility
6868
- [ ] use-event-listener
@@ -127,7 +127,8 @@ Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/
127127

128128
### New in Bagon Hooks
129129

130-
- [x] use-keyboard
130+
- [x] use-keyboard (✨ Runs even on single keys as opposed useHotkeys that only runs on combinations, so more general usecases)
131+
- [x] use-disclosure-data (✨ Improved, an alternative to use-disclosure for data-driven disclosures. I use it)
131132

132133
### Others
133134

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
```tsx
2+
import { useDisclosureData } from 'bagon-hooks';
3+
4+
import { For, Show } from 'solid-js';
5+
6+
export function UseDisclosureDataExample() {
7+
const items = [
8+
{ id: '1', title: 'Item 1' },
9+
{ id: '2', title: 'Item 2' },
10+
];
11+
12+
// If you have multiple modals,
13+
// I recommend prefixing `data`, `open`, `handlers` with the same name.
14+
// i.e. editModalData, editModalOpen, editModalHandlers
15+
const [data, open, handlers] = useDisclosureData<(typeof items)[number]>(null);
16+
17+
return (
18+
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
19+
<ul class="flex flex-col gap-2">
20+
<For each={items}>
21+
{item => (
22+
<li>
23+
<button
24+
onClick={() => {
25+
handlers.open(item);
26+
}}
27+
class="rounded bg-blue-500 px-4 py-2 text-white"
28+
>
29+
Open Dialog for {item.title}
30+
</button>
31+
</li>
32+
)}
33+
</For>
34+
</ul>
35+
36+
<Show when={open()}>
37+
<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
38+
<div class="rounded-lg bg-white p-6 shadow-lg">
39+
<h2 class="mb-4 text-lg font-bold">{data()?.title || 'Dialog Title'}</h2>
40+
<p class="mb-4">This is an example dialog using useDisclosure and data: {data()?.id}</p>
41+
<button onClick={handlers.close} class="rounded bg-gray-500 px-4 py-2 text-white">
42+
Close
43+
</button>
44+
</div>
45+
</div>
46+
</Show>
47+
</div>
48+
);
49+
}
50+
```
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useDisclosureData } from 'src';
2+
3+
import { For, Show } from 'solid-js';
4+
import { useMDXComponents } from 'solid-jsx';
5+
import { ExampleBase } from '../example-base';
6+
import Code from './use-disclosure-data.code.mdx';
7+
8+
export function UseDisclosureDataExample() {
9+
const items = [
10+
{ id: '1', title: 'Item 1' },
11+
{ id: '2', title: 'Item 2' },
12+
];
13+
14+
// If you have multiple modals,
15+
// I recommend prefixing `data`, `open`, `handlers` with the same name.
16+
// i.e. editModalData, editModalOpen, editModalHandlers
17+
const [data, open, handlers] = useDisclosureData<(typeof items)[number]>(null);
18+
19+
// @ts-ignore
20+
const components: any = useMDXComponents();
21+
return (
22+
<ExampleBase
23+
title="useDisclosureData"
24+
description="For complex usecases where we only need to pass data to the disclosure. I.e. Edit Modals, Confirm Alerts with details about the item clicked, etc."
25+
code={<Code components={components} />}
26+
>
27+
<div class="flex h-full w-full items-center justify-center gap-x-1 rounded-md border p-3 py-10 text-center text-sm">
28+
<ul class="flex flex-col gap-2">
29+
<For each={items}>
30+
{item => (
31+
<li>
32+
<button
33+
onClick={() => {
34+
handlers.open(item);
35+
}}
36+
class="rounded bg-blue-500 px-4 py-2 text-white"
37+
>
38+
Open Dialog for {item.title}
39+
</button>
40+
</li>
41+
)}
42+
</For>
43+
</ul>
44+
45+
<Show when={open()}>
46+
<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
47+
<div class="rounded-lg bg-white p-6 shadow-lg">
48+
<h2 class="mb-4 text-lg font-bold">{data()?.title || 'Dialog Title'}</h2>
49+
<p class="mb-4">
50+
This is an example dialog using useDisclosure and data: {data()?.id}
51+
</p>
52+
<button onClick={handlers.close} class="rounded bg-gray-500 px-4 py-2 text-white">
53+
Close
54+
</button>
55+
</div>
56+
</div>
57+
</Show>
58+
</div>
59+
</ExampleBase>
60+
);
61+
}

dev/pages/index/+Page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { UseDebouncedCallbackExample } from 'dev/components/examples/use-debounc
99
import { UseDebouncedSignalExample } from 'dev/components/examples/use-debounced-signal/use-debounced-signal.example';
1010
import { UseDebouncedValueExample } from 'dev/components/examples/use-debounced-value/use-debounced-value.example';
1111
import { UseDidUpdateExample } from 'dev/components/examples/use-did-update/use-did-update.example';
12+
import { UseDisclosureDataExample } from 'dev/components/examples/use-disclosure-data/use-disclosure-data.example';
1213
import { UseDisclosureExample } from 'dev/components/examples/use-disclosure/use-disclosure.example';
1314
import { UseDocumentTitleExample } from 'dev/components/examples/use-document-title/use-document-title.example';
1415
import { UseDocumentVisibilityExample } from 'dev/components/examples/use-document-visibility/use-document-visibility.example';
@@ -189,6 +190,10 @@ export default function HomePage() {
189190
title: 'useDisclosure',
190191
example: <UseDisclosureExample />,
191192
},
193+
{
194+
title: 'useDisclosureData',
195+
example: <UseDisclosureDataExample />,
196+
},
192197
];
193198

194199
const filteredList = createMemo(() => {

src/use-disclosure/use-disclosure.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
import { createSignal } from 'solid-js';
22

3+
/**
4+
* For simple usecases where we only need to open/close a disclosure. I.e. accordions, modals, confirm yes or no alerts, etc.
5+
*
6+
* Example:
7+
* ```
8+
* const [isOpen, actions] = useDisclosure(false);
9+
*
10+
* return (
11+
* <div>
12+
* <button onClick={actions.open}>Open</button>
13+
* <button onClick={actions.close}>Close</button>
14+
* <Modal onOpenChange={(open) => {
15+
* if (open) actions.open();
16+
* else actions.close();
17+
* }} />
18+
* </div>
19+
* )
20+
* ```
21+
*/
322
export function useDisclosure(
423
initialState = false,
524
callbacks?: { onOpen?: () => void; onClose?: () => void },
@@ -31,5 +50,71 @@ export function useDisclosure(
3150
opened() ? close() : open();
3251
};
3352

34-
return [opened, { open, close, toggle }] as const;
53+
const set = (value: boolean) => {
54+
setOpened(value);
55+
};
56+
57+
return [opened, { open, close, toggle, set }] as const;
58+
}
59+
60+
/**
61+
* For complex usecases where we only need to pass data to the disclosure. I.e. Edit Modals, Confirm Alerts with details about the item clicked, etc.
62+
*
63+
* Example:
64+
* ```
65+
* const [editModalData, editModalIsOpen, editModalActions] = useDisclosureData<{ id: string, title: string }>(false);
66+
*
67+
* return (
68+
* <div>
69+
* <button onClick={() => actions.open({ id: "1", title: "Item 1" })}>Open</button>
70+
* <button onClick={actions.close}>Close</button>
71+
* <Modal
72+
* onOpenChange={(open) => {
73+
* if (open) actions.open();
74+
* else actions.close();
75+
* }}
76+
* >
77+
* Do you really want to delete {editModalData?.title}?
78+
* <button
79+
* onClick={() => {
80+
* // delete(editModalData?.id);
81+
* editModalActions.close();
82+
* }}
83+
* >
84+
* Yes
85+
* </button>
86+
* </Modal>
87+
* </div>
88+
* )
89+
* ```
90+
*/
91+
export function useDisclosureData<T>(
92+
initialData: T | null = null,
93+
initialIsOpen: boolean = false,
94+
options?: {
95+
onOpen?: () => void;
96+
onClose?: () => void;
97+
dataDisappearDelay?: number;
98+
},
99+
) {
100+
const [disclosureData, setDisclosureData] = createSignal<T | null>(initialData);
101+
const [isOpen, setIsOpen] = createSignal<boolean>(initialIsOpen);
102+
103+
const open = (data: T | null) => {
104+
setIsOpen(true);
105+
setDisclosureData(() => data);
106+
options?.onOpen?.();
107+
};
108+
109+
const close = () => {
110+
setIsOpen(false);
111+
112+
setTimeout(() => {
113+
setDisclosureData(null);
114+
}, options?.dataDisappearDelay ?? 250); // Only remove data after 250ms (usually when a modal has closed after animation)
115+
116+
options?.onClose?.();
117+
};
118+
119+
return [disclosureData, isOpen, { open, close }] as const;
35120
}

0 commit comments

Comments
 (0)