@altano/use-element-observer 2.0.2 → 4.0.0
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/index.d.ts +47 -26
- package/dist/index.js +91 -78
- package/package.json +19 -20
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,53 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type ReactElementWithRef = React.ReactElement<{
|
|
5
|
+
ref: React.Ref<Element>;
|
|
6
|
+
}>;
|
|
7
|
+
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/useElementObserver.d.ts
|
|
3
10
|
interface Context {
|
|
4
|
-
|
|
11
|
+
mountedElements: Set<Element>;
|
|
5
12
|
}
|
|
6
13
|
declare const ElementObserverContext: React.Context<Context | null>;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
type Options = ChildOptions & {
|
|
15
|
+
selector: string;
|
|
16
|
+
};
|
|
17
|
+
type ChildOptions = {
|
|
18
|
+
useWrapperDiv: false;
|
|
19
|
+
tree: ReactElementWithRef;
|
|
20
|
+
} | {
|
|
21
|
+
useWrapperDiv: true;
|
|
22
|
+
tree: React.ReactNode;
|
|
23
|
+
} | {
|
|
24
|
+
useWrapperDiv?: undefined;
|
|
25
|
+
tree: React.ReactNode;
|
|
26
|
+
};
|
|
12
27
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
declare function useElementObserver({
|
|
28
|
+
* Allows observing mount/unmount of elements that match a given selector in the
|
|
29
|
+
* given React tree. Tree must be static: only the mounting of the tree itself
|
|
30
|
+
* is monitored. If the tree's DOM is dynamically changed those mounts and
|
|
31
|
+
* unmounts will not be detected.
|
|
32
|
+
*
|
|
33
|
+
* @param tree The React tree to observe.
|
|
34
|
+
* @param onMount The callback that will be passed any elements in the tree that
|
|
35
|
+
* match the given selector, as they mount.
|
|
36
|
+
* @param onUnmount The callback that will be passed any elements in the tree
|
|
37
|
+
* that match the given selector, as they unmount.
|
|
38
|
+
* @param selector The css selector used to select which elements are observed.
|
|
39
|
+
* @param useWrapperDiv If `true`, a 'div' will wrap the observed children. If
|
|
40
|
+
* `false`, `children` must be a single child element that can take a ref (which
|
|
41
|
+
* will be overwritten).
|
|
42
|
+
* @returns A tuple of: (1) set of elements and (2) the observed tree to be
|
|
43
|
+
* rendered.
|
|
44
|
+
*/
|
|
45
|
+
declare function useElementObserver({
|
|
46
|
+
tree,
|
|
47
|
+
selector,
|
|
48
|
+
useWrapperDiv
|
|
49
|
+
}: Options): [Set<Element>, React.ReactElement];
|
|
31
50
|
|
|
32
|
-
|
|
51
|
+
//#endregion
|
|
52
|
+
export { ChildOptions, ElementObserverContext, Options, ReactElementWithRef, useElementObserver };
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,87 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
import React3 from "react";
|
|
1
|
+
import React, { useCallback, useContext, useEffect, useRef } from "react";
|
|
3
2
|
import { useSet } from "@uidotdev/usehooks";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
onMount(ref);
|
|
22
|
-
return () => onUnmount(ref);
|
|
23
|
-
}, [newRef, onMount, onUnmount]);
|
|
24
|
-
return newRef;
|
|
5
|
+
//#region src/useMountRef.tsx
|
|
6
|
+
function useMountRef({ onMount, onUnmount }) {
|
|
7
|
+
const newRef = useRef(null);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const ref = newRef.current;
|
|
10
|
+
if (ref == null) return () => {};
|
|
11
|
+
onMount(ref);
|
|
12
|
+
return () => onUnmount(ref);
|
|
13
|
+
}, [
|
|
14
|
+
newRef,
|
|
15
|
+
onMount,
|
|
16
|
+
onUnmount
|
|
17
|
+
]);
|
|
18
|
+
return newRef;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
function ObserveChildWithWrapperDiv({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/Observer.tsx
|
|
23
|
+
function ObserveChildWithWrapperDiv({ children, onMount, onUnmount }) {
|
|
24
|
+
const ref = useMountRef({
|
|
25
|
+
onMount,
|
|
26
|
+
onUnmount
|
|
27
|
+
});
|
|
28
|
+
return /* @__PURE__ */ jsx("div", {
|
|
29
|
+
style: { display: "inherit" },
|
|
30
|
+
ref,
|
|
31
|
+
children
|
|
32
|
+
});
|
|
36
33
|
}
|
|
37
|
-
function ObserveChild({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const child = React2.Children.only(children);
|
|
44
|
-
return React2.cloneElement(child, { ref: newRef });
|
|
34
|
+
function ObserveChild({ children, onMount, onUnmount }) {
|
|
35
|
+
const newRef = useMountRef({
|
|
36
|
+
onMount,
|
|
37
|
+
onUnmount
|
|
38
|
+
});
|
|
39
|
+
return React.cloneElement(children, { ref: newRef });
|
|
45
40
|
}
|
|
46
|
-
function Observer({
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
const onUnmount = useCallback(
|
|
65
|
-
() => mountedElements.clear(),
|
|
66
|
-
[mountedElements]
|
|
67
|
-
);
|
|
68
|
-
const Component = useWrapperDiv ? ObserveChildWithWrapperDiv : ObserveChild;
|
|
69
|
-
return /* @__PURE__ */ jsx(Component, { onMount, onUnmount, children });
|
|
41
|
+
function Observer({ children, selector, useWrapperDiv }) {
|
|
42
|
+
const context = useContext(ElementObserverContext);
|
|
43
|
+
if (context == null) throw new Error("Observer context was null");
|
|
44
|
+
const { mountedElements } = context;
|
|
45
|
+
const onMount = useCallback((ref) => {
|
|
46
|
+
Array.from(ref.querySelectorAll(selector)).forEach((e) => mountedElements.add(e));
|
|
47
|
+
}, [selector, mountedElements]);
|
|
48
|
+
const onUnmount = useCallback(() => mountedElements.clear(), [mountedElements]);
|
|
49
|
+
return useWrapperDiv ? /* @__PURE__ */ jsx(ObserveChildWithWrapperDiv, {
|
|
50
|
+
onMount,
|
|
51
|
+
onUnmount,
|
|
52
|
+
children
|
|
53
|
+
}) : /* @__PURE__ */ jsx(ObserveChild, {
|
|
54
|
+
onMount,
|
|
55
|
+
onUnmount,
|
|
56
|
+
children
|
|
57
|
+
});
|
|
70
58
|
}
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/useElementObserver.tsx
|
|
62
|
+
const ElementObserverContext = React.createContext(null);
|
|
63
|
+
/**
|
|
64
|
+
* Allows observing mount/unmount of elements that match a given selector in the
|
|
65
|
+
* given React tree. Tree must be static: only the mounting of the tree itself
|
|
66
|
+
* is monitored. If the tree's DOM is dynamically changed those mounts and
|
|
67
|
+
* unmounts will not be detected.
|
|
68
|
+
*
|
|
69
|
+
* @param tree The React tree to observe.
|
|
70
|
+
* @param onMount The callback that will be passed any elements in the tree that
|
|
71
|
+
* match the given selector, as they mount.
|
|
72
|
+
* @param onUnmount The callback that will be passed any elements in the tree
|
|
73
|
+
* that match the given selector, as they unmount.
|
|
74
|
+
* @param selector The css selector used to select which elements are observed.
|
|
75
|
+
* @param useWrapperDiv If `true`, a 'div' will wrap the observed children. If
|
|
76
|
+
* `false`, `children` must be a single child element that can take a ref (which
|
|
77
|
+
* will be overwritten).
|
|
78
|
+
* @returns A tuple of: (1) set of elements and (2) the observed tree to be
|
|
79
|
+
* rendered.
|
|
80
|
+
*/
|
|
81
|
+
function useElementObserver({ tree, selector, useWrapperDiv }) {
|
|
82
|
+
const mountedElements = useSet();
|
|
83
|
+
const observedTree = /* @__PURE__ */ jsx(ElementObserverContext.Provider, {
|
|
84
|
+
value: { mountedElements },
|
|
85
|
+
children: useWrapperDiv == null || useWrapperDiv === true ? /* @__PURE__ */ jsx(Observer, {
|
|
86
|
+
selector,
|
|
87
|
+
useWrapperDiv: true,
|
|
88
|
+
children: tree
|
|
89
|
+
}) : /* @__PURE__ */ jsx(Observer, {
|
|
90
|
+
selector,
|
|
91
|
+
useWrapperDiv: false,
|
|
92
|
+
children: tree
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
return [mountedElements, observedTree];
|
|
83
96
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
99
|
+
export { ElementObserverContext, useElementObserver };
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altano/use-element-observer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Intersection-Observer-based Scroll Spy React Hook",
|
|
5
5
|
"homepage": "https://github.com/altano/npm-packages/tree/main/packages/use-element-observer",
|
|
6
6
|
"repository": {
|
|
@@ -26,34 +26,33 @@
|
|
|
26
26
|
"@uidotdev/usehooks": "^2.4.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@arethetypeswrong/cli": "^0.
|
|
30
|
-
"@testing-library/
|
|
31
|
-
"@
|
|
32
|
-
"@types/react": "^
|
|
33
|
-
"@
|
|
34
|
-
"@vitest/coverage-v8": "^3.0.9",
|
|
29
|
+
"@arethetypeswrong/cli": "^0.17.4",
|
|
30
|
+
"@testing-library/react": "^16.2.0",
|
|
31
|
+
"@types/react": "^19.0.12",
|
|
32
|
+
"@types/react-dom": "^19.0.4",
|
|
33
|
+
"@vitest/coverage-v8": "^3.1.4",
|
|
35
34
|
"depcheck": "^1.4.7",
|
|
36
|
-
"eslint": "^9.
|
|
37
|
-
"jsdom": "^
|
|
38
|
-
"prettier": "^3.
|
|
39
|
-
"react": "^
|
|
35
|
+
"eslint": "^9.23.0",
|
|
36
|
+
"jsdom": "^26.0.0",
|
|
37
|
+
"prettier": "^3.5.3",
|
|
38
|
+
"react": "^19.1.0",
|
|
39
|
+
"tsdown": "^0.12.3",
|
|
40
40
|
"typescript": "^5.8.2",
|
|
41
|
-
"vite": "^6.2.
|
|
42
|
-
"vitest": "^3.
|
|
43
|
-
"@altano/build-config": "0.0.
|
|
44
|
-
"@altano/testing": "0.0.
|
|
45
|
-
"@altano/tsconfig": "0.0.
|
|
46
|
-
"@altano/vitest-plugins": "1.0.0"
|
|
41
|
+
"vite": "^6.2.6",
|
|
42
|
+
"vitest": "^3.1.4",
|
|
43
|
+
"@altano/build-config": "0.0.1",
|
|
44
|
+
"@altano/testing": "0.0.1",
|
|
45
|
+
"@altano/tsconfig": "0.0.1"
|
|
47
46
|
},
|
|
48
47
|
"peerDependencies": {
|
|
49
|
-
"react": "
|
|
48
|
+
"react": "19.x.x"
|
|
50
49
|
},
|
|
51
50
|
"publishConfig": {
|
|
52
51
|
"access": "public"
|
|
53
52
|
},
|
|
54
53
|
"scripts": {
|
|
55
|
-
"build": "
|
|
56
|
-
"check:exports": "attw --pack . --
|
|
54
|
+
"build": "tsdown --config ../build-config/tsdown.config.browser.ts",
|
|
55
|
+
"check:exports": "attw --pack . --profile esm-only --quiet",
|
|
57
56
|
"check:types:src": "tsc --noEmit",
|
|
58
57
|
"check:types:test": "tsc --noEmit --project ./tests/tsconfig.json",
|
|
59
58
|
"check:unused-dependencies": "depcheck",
|