-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(hooks/useClickOutside): add useClickOutside hook
- Loading branch information
Showing
6 changed files
with
171 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<script lang="ts"> | ||
import { onMount } from "svelte"; | ||
import { useClickOutside } from "@/hooks"; | ||
export let onClick: () => void; | ||
let element: HTMLElement; | ||
const { removeListener, setElementRef } = useClickOutside({ handler: onClick, component: true }); | ||
onMount(() => { | ||
setElementRef(element); | ||
return () => { | ||
removeListener(); | ||
}; | ||
}); | ||
</script> | ||
|
||
<div class="w-full h-32"> | ||
<div bind:this={element}>hello</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { render, screen } from "@testing-library/svelte"; | ||
import userEvent from "@testing-library/user-event"; | ||
|
||
import UseClickOutside from "@/hooks/useClickOutside/useClickOutside.svelte"; | ||
|
||
vi.mock("esm-env", async (importOriginal) => { | ||
const actual: any = await importOriginal(); | ||
|
||
return { | ||
...actual, | ||
BROWSER: true, | ||
}; | ||
}); | ||
|
||
describe("Hooks - useClickOutside", () => { | ||
test("should work on click outside the element", async () => { | ||
const clickMockFn = vi.fn(); | ||
|
||
render(UseClickOutside, { onClick: clickMockFn }); | ||
|
||
const element = screen.getByText(/hello/i); | ||
|
||
await userEvent.click(element); | ||
|
||
expect(clickMockFn).not.toBeCalled(); | ||
|
||
await userEvent.click(document.body); | ||
|
||
expect(clickMockFn).toBeCalled(); | ||
}); | ||
|
||
test("should NOT work after unmount component", async () => { | ||
const clickMockFn = vi.fn(); | ||
|
||
const { unmount } = render(UseClickOutside, { onClick: clickMockFn }); | ||
|
||
await unmount(); | ||
|
||
await userEvent.click(document.body); | ||
|
||
expect(clickMockFn).not.toBeCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,40 @@ | ||
import { onMount } from "svelte"; | ||
|
||
import { useEventListener } from ".."; | ||
|
||
type Handler = (event: MouseEvent) => void; | ||
|
||
export function useClickOutside<T extends HTMLElement = HTMLElement>( | ||
ref: T, | ||
handler: Handler, | ||
mouseEvent: "mousedown" | "mouseup" = "mousedown", | ||
): void { | ||
useEventListener(mouseEvent, (event: MouseEvent) => { | ||
const el = ref; | ||
type Options = { | ||
handler: Handler; | ||
mouseEvent?: "mousedown" | "mouseup"; | ||
component?: boolean; | ||
}; | ||
|
||
export function useClickOutside<T extends HTMLElement = HTMLElement>({ | ||
handler, | ||
mouseEvent = "mousedown", | ||
component = false, | ||
}: Options): { setElementRef: (elm: T) => void; removeListener: () => void } { | ||
let element: T; | ||
|
||
if (!el || el.contains(event.target as Node)) { | ||
const removeListener = useEventListener(mouseEvent, (event: MouseEvent) => { | ||
if (!element || element.contains(event.target as Node)) { | ||
return; | ||
} | ||
|
||
handler(event); | ||
}); | ||
|
||
if (component) { | ||
onMount(() => { | ||
return () => { | ||
removeListener(); | ||
}; | ||
}); | ||
} | ||
|
||
return { | ||
setElementRef: (elm) => (element = elm), | ||
removeListener, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<script lang="ts"> | ||
import { onMount } from "svelte"; | ||
import { Browser, DocTpl, H2, Highlight, Link } from "@/components"; | ||
import { useClickOutside } from "@/hooks"; | ||
import code from "./code-snippet"; | ||
const onClickHandler = () => { | ||
console.log("onClickHandler"); | ||
}; | ||
let element: HTMLElement; | ||
const { removeListener, setElementRef } = useClickOutside({ handler: onClickHandler }); | ||
onMount(() => { | ||
setElementRef(element); | ||
return () => { | ||
removeListener(); | ||
}; | ||
}); | ||
</script> | ||
|
||
<DocTpl title="useClickOutside"> | ||
<div slot="description"> | ||
<p>Hook to catch the click on any part of the site</p> | ||
|
||
<h3>Related hooks</h3> | ||
|
||
<ul class="list-disc pl-6"> | ||
<li><Link href="/guide/useEventListener">useEventListener</Link></li> | ||
</ul> | ||
</div> | ||
|
||
<div slot="visual-example"> | ||
<H2>Visual example</H2> | ||
|
||
<Browser body="p-4 bg-gray-950/50"> | ||
<div class="h-24 w-24 bg-gray-600 p-2" bind:this={element}>click outside here</div> | ||
</Browser> | ||
</div> | ||
|
||
<div slot="code-example"> | ||
<H2>Code example</H2> | ||
|
||
<Highlight {code} /> | ||
</div> | ||
</DocTpl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
export default ` | ||
<!-- javascript --> | ||
<script lang="ts"> | ||
import { onMount } from "svelte"; | ||
import { useClickAnyWhere } from "@dimaslz/svelteuse"; | ||
const someCallback = () => { | ||
console.log("click anywhere!"); | ||
}; | ||
onMount(() => { | ||
const eventClickAnyWhere = useClickAnyWhere(someCallback); | ||
return () => { | ||
eventClickAnyWhere(); | ||
} | ||
}) | ||
</script> | ||
<!-- html --> | ||
<div> | ||
content | ||
</div> | ||
`; |