@reshaped/headless 3.10.1 → 3.11.0-canary.1
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/dist/hooks/tests/useElementId.stories.d.ts +11 -0
- package/dist/hooks/tests/useElementId.stories.js +24 -0
- package/dist/hooks/useElementId.d.ts +2 -0
- package/dist/hooks/useElementId.js +8 -0
- package/dist/hooks/useIsDismissible.d.ts +12 -0
- package/dist/hooks/useIsDismissible.js +47 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { expect, waitFor } from "storybook/test";
|
|
3
|
+
import useElementId from "../useElementId.js";
|
|
4
|
+
export default {
|
|
5
|
+
title: "Hooks/useElementId",
|
|
6
|
+
parameters: {
|
|
7
|
+
chromatic: { disableSnapshot: true },
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
const Component = (props) => {
|
|
11
|
+
const id = useElementId(props.id);
|
|
12
|
+
return _jsx("div", { id: id, children: id });
|
|
13
|
+
};
|
|
14
|
+
export const id = {
|
|
15
|
+
name: "passed id",
|
|
16
|
+
render: () => {
|
|
17
|
+
return _jsx(Component, { id: "hey" });
|
|
18
|
+
},
|
|
19
|
+
play: async () => {
|
|
20
|
+
waitFor(() => {
|
|
21
|
+
expect(document.querySelector("#hey")).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton hook to check if multiple elements can be dismissed and returns flag only for the latest one
|
|
3
|
+
* Example: Use to only close the latest opened Flyout/Modal
|
|
4
|
+
*/
|
|
5
|
+
import React from "react";
|
|
6
|
+
type Ref = React.RefObject<HTMLElement | null>;
|
|
7
|
+
declare const useIsDismissible: (args: {
|
|
8
|
+
active?: boolean;
|
|
9
|
+
contentRef: Ref;
|
|
10
|
+
triggerRef?: Ref;
|
|
11
|
+
}) => () => boolean;
|
|
12
|
+
export default useIsDismissible;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton hook to check if multiple elements can be dismissed and returns flag only for the latest one
|
|
3
|
+
* Example: Use to only close the latest opened Flyout/Modal
|
|
4
|
+
*/
|
|
5
|
+
import React from "react";
|
|
6
|
+
import useElementId from "./useElementId.js";
|
|
7
|
+
let queue = {};
|
|
8
|
+
let latestId = null;
|
|
9
|
+
const removeFromQueue = (id) => {
|
|
10
|
+
// Ignore removal of non-existing ids when called on component mount with active: false
|
|
11
|
+
if (!queue[id])
|
|
12
|
+
return;
|
|
13
|
+
if (id === latestId)
|
|
14
|
+
latestId = queue[id].parentId;
|
|
15
|
+
delete queue[id];
|
|
16
|
+
// Clear up all unused ids after the queue is resolved
|
|
17
|
+
if (latestId === null)
|
|
18
|
+
queue = {};
|
|
19
|
+
};
|
|
20
|
+
const addToQueue = (id, contentRef, triggerRef) => {
|
|
21
|
+
queue[id] = { parentId: latestId, triggerRef, contentRef };
|
|
22
|
+
latestId = id;
|
|
23
|
+
};
|
|
24
|
+
const useIsDismissible = (args) => {
|
|
25
|
+
const { active, contentRef, triggerRef } = args;
|
|
26
|
+
const id = useElementId();
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
if (!active)
|
|
29
|
+
return;
|
|
30
|
+
addToQueue(id, contentRef, triggerRef);
|
|
31
|
+
return () => removeFromQueue(id);
|
|
32
|
+
}, [active, id, contentRef, triggerRef]);
|
|
33
|
+
return React.useCallback(() => {
|
|
34
|
+
if (!active)
|
|
35
|
+
return true;
|
|
36
|
+
const latest = latestId ? queue[latestId] : undefined;
|
|
37
|
+
const latestTrigger = latest?.triggerRef?.current;
|
|
38
|
+
const prev = latest?.parentId ? queue[latest.parentId] : undefined;
|
|
39
|
+
const prevContent = prev?.contentRef.current;
|
|
40
|
+
const nested = prevContent && latestTrigger && prevContent.contains(latestTrigger);
|
|
41
|
+
// Don't block independently rendered components that are not nested in each other
|
|
42
|
+
if (triggerRef?.current && !nested)
|
|
43
|
+
return true;
|
|
44
|
+
return !latestId || latestId === id;
|
|
45
|
+
}, [id, active, triggerRef]);
|
|
46
|
+
};
|
|
47
|
+
export default useIsDismissible;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,7 @@ export { default as useIsomorphicLayoutEffect } from "./hooks/useIsomorphicLayou
|
|
|
10
10
|
export { default as useOnClickOutside } from "./hooks/useOnClickOutside";
|
|
11
11
|
export { default as useScrollLock } from "./hooks/useScrollLock";
|
|
12
12
|
export { default as useToggle } from "./hooks/useToggle";
|
|
13
|
+
export { default as useElementId } from "./hooks/useElementId";
|
|
14
|
+
export { default as useIsDismissible } from "./hooks/useIsDismissible";
|
|
13
15
|
export type { ClassName } from "@reshaped/utilities";
|
|
14
16
|
export type { Attributes, CSSVariable, StyleAttribute } from "./types/global";
|
package/dist/index.js
CHANGED
|
@@ -13,3 +13,5 @@ export { default as useIsomorphicLayoutEffect } from "./hooks/useIsomorphicLayou
|
|
|
13
13
|
export { default as useOnClickOutside } from "./hooks/useOnClickOutside.js";
|
|
14
14
|
export { default as useScrollLock } from "./hooks/useScrollLock.js";
|
|
15
15
|
export { default as useToggle } from "./hooks/useToggle.js";
|
|
16
|
+
export { default as useElementId } from "./hooks/useElementId.js";
|
|
17
|
+
export { default as useIsDismissible } from "./hooks/useIsDismissible.js";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reshaped/headless",
|
|
3
3
|
"description": "Headless components and utilities for React",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.11.0-canary.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://reshaped.so",
|
|
7
7
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"react-dom": "^18 || ^19"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@reshaped/utilities": "3.
|
|
42
|
+
"@reshaped/utilities": "3.11.0-canary.1"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"clean": "rm -rf dist",
|