@koine/react 1.0.87 → 1.0.89
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/Autocomplete/AutocompleteMui.d.ts +1 -1
- package/Calendar/CalendarDaygridNav.d.ts +1 -1
- package/Calendar/CalendarDaygridTable.d.ts +1 -1
- package/Carousel/CarouselCss.d.ts +1 -6
- package/Details/Details.d.ts +1 -283
- package/Dialog/DialogMui.d.ts +1 -298
- package/Dialog/css/bare.d.ts +1 -298
- package/Dialog/m/bare.d.ts +1 -298
- package/Dialog/sc/bare.d.ts +1 -298
- package/Dialog/sc/framer.d.ts +1 -298
- package/Dialog/sc/framerMaterial.d.ts +1 -298
- package/Dialog/sc/material.d.ts +1 -298
- package/Dialog/tw/bare.d.ts +7 -304
- package/Dialog/tw/elegant.d.ts +7 -304
- package/Dialog/tw/framer.d.ts +3 -300
- package/Dialog/tw/framerMaterial.d.ts +5 -302
- package/Dialog/tw/material.d.ts +7 -304
- package/Form/Form.d.ts +2 -28
- package/Form/sc/bare.d.ts +2 -27
- package/Forms/Checkbox/Checkbox.d.ts +1 -1
- package/Forms/Field/FieldControl.d.ts +1 -1
- package/Forms/Input/Input.d.ts +1 -1
- package/Forms/Password/Password.d.ts +1 -1
- package/Forms/Radio/Radio.d.ts +1 -1
- package/Forms/Switch/Switch.d.ts +1 -1
- package/MenuItem/useMenuItem.d.ts +1 -1
- package/Tabs/TabsMui.d.ts +1 -279
- package/Tabs/tw/bare.d.ts +1 -279
- package/Tabs/tw/material.d.ts +7 -285
- package/helpers/createUseMediaQueryWidth.d.ts +6 -0
- package/helpers/createUseMediaQueryWidth.js +169 -0
- package/helpers/index.d.ts +1 -0
- package/helpers/index.js +1 -0
- package/helpers/mergeRefs.d.ts +3 -0
- package/helpers/mergeRefs.js +13 -0
- package/hooks/index.d.ts +3 -1
- package/hooks/index.js +3 -1
- package/hooks/useFixedOffset.d.ts +8 -0
- package/hooks/useFixedOffset.js +50 -0
- package/hooks/useMeasure.d.ts +27 -0
- package/hooks/useMeasure.js +163 -0
- package/hooks/useSmoothScroll.d.ts +2 -0
- package/hooks/useSmoothScroll.js +26 -0
- package/node/{hooks/useMediaQueryWidthCreator.js → helpers/createUseMediaQueryWidth.js} +4 -4
- package/node/helpers/index.js +1 -0
- package/node/helpers/mergeRefs.js +17 -0
- package/node/hooks/index.js +3 -1
- package/node/hooks/useFixedOffset.js +54 -0
- package/node/hooks/useMeasure.js +167 -0
- package/node/hooks/useSmoothScroll.js +30 -0
- package/package.json +11 -10
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type GetMediaQueryWidthResolversBreakpoints } from "@koine/utils";
|
|
2
|
+
declare type _MediaQuerWidthDefExplicit<TBreakpoint extends string> = `min-${TBreakpoint}` | `max-${TBreakpoint}` | `up-${TBreakpoint}` | `down-${TBreakpoint}` | `between-${TBreakpoint}_${TBreakpoint}` | `only-${TBreakpoint}`;
|
|
3
|
+
export declare type MediaQuerWidthDef<TBreakpoint extends string> = `${TBreakpoint}` | _MediaQuerWidthDefExplicit<TBreakpoint>;
|
|
4
|
+
export declare type MediaQueryWidth<TBreakpoint extends string> = `@${MediaQuerWidthDef<TBreakpoint>}`;
|
|
5
|
+
export declare function createUseMediaQueryWidth<TBreakpointsConfig extends GetMediaQueryWidthResolversBreakpoints>(customBreakpoints: TBreakpointsConfig): <TBreakpoints extends Extract<keyof TBreakpointsConfig, string>>(media: `@${TBreakpoints}` | `@min-${TBreakpoints}` | `@max-${TBreakpoints}` | `@up-${TBreakpoints}` | `@down-${TBreakpoints}` | `@between-${TBreakpoints}_${TBreakpoints}` | `@only-${TBreakpoints}`) => boolean;
|
|
6
|
+
export default createUseMediaQueryWidth;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from "react";
|
|
2
|
+
import { isBrowser, isUndefined, getMediaQueryWidthResolvers, } from "@koine/utils";
|
|
3
|
+
export function createUseMediaQueryWidth(customBreakpoints) {
|
|
4
|
+
var queryResolvers = getMediaQueryWidthResolvers(customBreakpoints);
|
|
5
|
+
return function useMediaQueryWidth(media) {
|
|
6
|
+
var definition = media.substring(1);
|
|
7
|
+
var _a = definition.split("-"), rule = _a[0], ruleBreakpoint = _a[1];
|
|
8
|
+
if (isUndefined(ruleBreakpoint)) {
|
|
9
|
+
ruleBreakpoint = rule;
|
|
10
|
+
}
|
|
11
|
+
if (isUndefined(rule)) {
|
|
12
|
+
rule = "min";
|
|
13
|
+
}
|
|
14
|
+
// with the hook creator approach these breakpoint types cannot be deduced
|
|
15
|
+
// const [br1, br2] = ruleBreakpoint.split("-") as Split<
|
|
16
|
+
// typeof ruleBreakpoint,
|
|
17
|
+
// "-"
|
|
18
|
+
// >;
|
|
19
|
+
var _b = ruleBreakpoint.split("_"), br1 = _b[0], br2 = _b[1];
|
|
20
|
+
var query = queryResolvers[rule](br1, br2);
|
|
21
|
+
var mq = useMemo(function () { return (isBrowser ? window.matchMedia(query) : { matches: false }); }, [query]);
|
|
22
|
+
var _c = useState(mq.matches), matches = _c[0], setMatches = _c[1];
|
|
23
|
+
useEffect(function () {
|
|
24
|
+
var mq = window.matchMedia(query);
|
|
25
|
+
var handleChange = function (event) {
|
|
26
|
+
setMatches(event.matches);
|
|
27
|
+
};
|
|
28
|
+
setMatches(mq.matches);
|
|
29
|
+
// Safari < 14 can't use addEventListener on a MediaQueryList
|
|
30
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList#Browser_compatibility
|
|
31
|
+
if (!mq.addEventListener) {
|
|
32
|
+
// Update the state whenever the media query match state changes
|
|
33
|
+
mq.addListener(handleChange);
|
|
34
|
+
// Clean up on unmount and if the query changes
|
|
35
|
+
return function () {
|
|
36
|
+
mq.removeListener(handleChange);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
mq.addEventListener("change", handleChange);
|
|
40
|
+
return function () {
|
|
41
|
+
mq.removeEventListener("change", handleChange);
|
|
42
|
+
};
|
|
43
|
+
}, [query]);
|
|
44
|
+
return matches;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export default createUseMediaQueryWidth;
|
|
48
|
+
//// without creator it would be:
|
|
49
|
+
//// ---------------------------------------------------------------------------
|
|
50
|
+
// import { useState, useEffect, useMemo } from "react";
|
|
51
|
+
// import { isBrowser, type Split } from "@koine/utils";
|
|
52
|
+
// import { breakpoints as themeBreakpoints } from "@/config/theme/breakpoints";
|
|
53
|
+
// type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl";
|
|
54
|
+
// type Breakpoints = Record<Breakpoint, number>;
|
|
55
|
+
// type MediaQuery =
|
|
56
|
+
// | `max:${Breakpoint}`
|
|
57
|
+
// | `min:${Breakpoint}`
|
|
58
|
+
// | `down:${Breakpoint}`
|
|
59
|
+
// | `up:${Breakpoint}`
|
|
60
|
+
// | `between:${Breakpoint}-${Breakpoint}`
|
|
61
|
+
// | `only:${Breakpoint}`;
|
|
62
|
+
// const breakpoints: Breakpoints = {
|
|
63
|
+
// xs: 0,
|
|
64
|
+
// ...themeBreakpoints,
|
|
65
|
+
// };
|
|
66
|
+
// const sortedBreakpointsNames = (
|
|
67
|
+
// Object.keys(breakpoints).map((key) => {
|
|
68
|
+
// const br = key as keyof typeof breakpoints;
|
|
69
|
+
// return [br, breakpoints[br]];
|
|
70
|
+
// }) as [Breakpoint, number][]
|
|
71
|
+
// )
|
|
72
|
+
// .sort((a, b) => a[1] - b[1])
|
|
73
|
+
// .map((item) => item[0]);
|
|
74
|
+
// const getNextBreakpoint = (breakpoint: Breakpoint) => {
|
|
75
|
+
// const index = sortedBreakpointsNames.indexOf(breakpoint);
|
|
76
|
+
// return sortedBreakpointsNames[index + 1];
|
|
77
|
+
// };
|
|
78
|
+
// /**
|
|
79
|
+
// * It behaves the same as `(min-width: ${value}px)`
|
|
80
|
+
// * where value is the given breakpoint value.
|
|
81
|
+
// * For ease of use this can be used both as a function `min("md")` and as an
|
|
82
|
+
// * object literal `min.md`.
|
|
83
|
+
// */
|
|
84
|
+
// const min = (br: Breakpoint) => `(min-width: ${breakpoints[br]}px)`;
|
|
85
|
+
// /**
|
|
86
|
+
// * It behaves the same as `(max-width: ${value}px)`
|
|
87
|
+
// * where value is the given breakpoint value.
|
|
88
|
+
// * For ease of use this can be used both as a function `max("md")` and as an
|
|
89
|
+
// * object literal `max.md`.
|
|
90
|
+
// */
|
|
91
|
+
// const max = (br: Breakpoint) => `(max-width: ${breakpoints[br] - 0.02}px)`;
|
|
92
|
+
// /**
|
|
93
|
+
// * It behaves the same as `min`
|
|
94
|
+
// * @inheritdoc {max}
|
|
95
|
+
// */
|
|
96
|
+
// const up = min;
|
|
97
|
+
// /**
|
|
98
|
+
// * It behaves similarly to `max` but you will use the "next" breakpoint,
|
|
99
|
+
// * specifying CSS that will apply from the given breakpoint and down.
|
|
100
|
+
// */
|
|
101
|
+
// const down = (br: Breakpoint) => {
|
|
102
|
+
// const brNext = getNextBreakpoint(br);
|
|
103
|
+
// // TODO: if br does not exists otherwise throw Error
|
|
104
|
+
// return brNext && `(max-width: ${breakpoints[brNext] - 0.02}px)`;
|
|
105
|
+
// };
|
|
106
|
+
// /**
|
|
107
|
+
// * Media query between the two given breakpoints
|
|
108
|
+
// */
|
|
109
|
+
// const between = (br1: Breakpoint, br2?: Breakpoint) => {
|
|
110
|
+
// return br2
|
|
111
|
+
// ? `(min-width: ${breakpoints[br1]}px) and (max-width: ${
|
|
112
|
+
// breakpoints[br2] - 0.02
|
|
113
|
+
// }px)`
|
|
114
|
+
// : min(br1);
|
|
115
|
+
// };
|
|
116
|
+
// /**
|
|
117
|
+
// * Media query to apply from the given breakpoint until the next, just for its
|
|
118
|
+
// * full range
|
|
119
|
+
// */
|
|
120
|
+
// const only = (br: Breakpoint) => {
|
|
121
|
+
// const brNext = getNextBreakpoint(br);
|
|
122
|
+
// return brNext ? between(br, brNext) : min(br);
|
|
123
|
+
// };
|
|
124
|
+
// const queryResolvers = {
|
|
125
|
+
// max,
|
|
126
|
+
// min,
|
|
127
|
+
// down,
|
|
128
|
+
// up,
|
|
129
|
+
// between,
|
|
130
|
+
// only,
|
|
131
|
+
// };
|
|
132
|
+
// export function useMqWidth(media: MediaQuery) {
|
|
133
|
+
// const [rule = "min", ruleBreakpoint] = media.split(":") as Split<
|
|
134
|
+
// MediaQuery,
|
|
135
|
+
// ":"
|
|
136
|
+
// >;
|
|
137
|
+
// const [br1, br2] = ruleBreakpoint.split("-") as Split<
|
|
138
|
+
// typeof ruleBreakpoint,
|
|
139
|
+
// "-"
|
|
140
|
+
// >;
|
|
141
|
+
// const query = queryResolvers[rule](br1, br2);
|
|
142
|
+
// const mq = useMemo(
|
|
143
|
+
// () => (isBrowser ? window.matchMedia(query) : { matches: false }),
|
|
144
|
+
// [query]
|
|
145
|
+
// );
|
|
146
|
+
// const [matches, setMatches] = useState(mq.matches);
|
|
147
|
+
// useEffect(() => {
|
|
148
|
+
// const mq = window.matchMedia(query);
|
|
149
|
+
// const handleChange = (event: MediaQueryListEvent) => {
|
|
150
|
+
// setMatches(event.matches);
|
|
151
|
+
// };
|
|
152
|
+
// setMatches(mq.matches);
|
|
153
|
+
// // Safari < 14 can't use addEventListener on a MediaQueryList
|
|
154
|
+
// // https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList#Browser_compatibility
|
|
155
|
+
// if (!mq.addEventListener) {
|
|
156
|
+
// // Update the state whenever the media query match state changes
|
|
157
|
+
// mq.addListener(handleChange);
|
|
158
|
+
// // Clean up on unmount and if the query changes
|
|
159
|
+
// return () => {
|
|
160
|
+
// mq.removeListener(handleChange);
|
|
161
|
+
// };
|
|
162
|
+
// }
|
|
163
|
+
// mq.addEventListener("change", handleChange);
|
|
164
|
+
// return () => {
|
|
165
|
+
// mq.removeEventListener("change", handleChange);
|
|
166
|
+
// };
|
|
167
|
+
// }, [query]);
|
|
168
|
+
// return matches;
|
|
169
|
+
// }
|
package/helpers/index.d.ts
CHANGED
package/helpers/index.js
CHANGED
package/hooks/index.d.ts
CHANGED
|
@@ -2,14 +2,16 @@ export * from "./useAsyncFn";
|
|
|
2
2
|
export * from "./useDateLocale";
|
|
3
3
|
export * from "./useEffectOnce";
|
|
4
4
|
export * from "./useFirstMountState";
|
|
5
|
+
export * from "./useFixedOffset";
|
|
5
6
|
export * from "./useFocus";
|
|
6
7
|
export * from "./useId";
|
|
7
8
|
export * from "./useIsomorphicLayoutEffect";
|
|
8
|
-
export * from "./
|
|
9
|
+
export * from "./useMeasure";
|
|
9
10
|
export * from "./useMount";
|
|
10
11
|
export * from "./useMountedState";
|
|
11
12
|
export * from "./usePrevious";
|
|
12
13
|
export * from "./useScrollPosition";
|
|
14
|
+
export * from "./useSmoothScroll";
|
|
13
15
|
export * from "./useTraceUpdate";
|
|
14
16
|
export * from "./useUpdateEffect";
|
|
15
17
|
export * from "./useWindowSize";
|
package/hooks/index.js
CHANGED
|
@@ -2,15 +2,17 @@ export * from "./useAsyncFn";
|
|
|
2
2
|
export * from "./useDateLocale";
|
|
3
3
|
export * from "./useEffectOnce";
|
|
4
4
|
export * from "./useFirstMountState";
|
|
5
|
+
export * from "./useFixedOffset";
|
|
5
6
|
export * from "./useFocus";
|
|
6
7
|
export * from "./useId";
|
|
7
8
|
export * from "./useIsomorphicLayoutEffect";
|
|
8
|
-
export * from "./
|
|
9
|
+
export * from "./useMeasure";
|
|
9
10
|
export * from "./useMount";
|
|
10
11
|
export * from "./useMountedState";
|
|
11
12
|
export * from "./usePrevious";
|
|
12
13
|
export * from "./useScrollPosition";
|
|
13
14
|
// export * from "./useScrollTo";
|
|
15
|
+
export * from "./useSmoothScroll";
|
|
14
16
|
export * from "./useTraceUpdate";
|
|
15
17
|
export * from "./useUpdateEffect";
|
|
16
18
|
export * from "./useWindowSize";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* Maybe use [ResizeObserver polyfill](https://github.com/juggle/resize-observer)
|
|
4
|
+
*
|
|
5
|
+
* @see https://web.dev/resize-observer/
|
|
6
|
+
*/
|
|
7
|
+
export declare function useFixedOffset(): import("react").MutableRefObject<number>;
|
|
8
|
+
export default useFixedOffset;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from "react";
|
|
2
|
+
import { injectCss, calculateFixedOffset, listenResize, $each, } from "@koine/dom";
|
|
3
|
+
import { debounce } from "@koine/utils";
|
|
4
|
+
var observer;
|
|
5
|
+
var inject = function (value) {
|
|
6
|
+
injectCss("useFixedOffset", "html{scroll-padding-top: ".concat(value, "px}"));
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Maybe use [ResizeObserver polyfill](https://github.com/juggle/resize-observer)
|
|
10
|
+
*
|
|
11
|
+
* @see https://web.dev/resize-observer/
|
|
12
|
+
*/
|
|
13
|
+
export function useFixedOffset() {
|
|
14
|
+
var fixedOffset = useRef(0);
|
|
15
|
+
useLayoutEffect(function () {
|
|
16
|
+
var update = function () {
|
|
17
|
+
var newFixedOffset = calculateFixedOffset();
|
|
18
|
+
fixedOffset.current = newFixedOffset;
|
|
19
|
+
// inject this CSS make the hashed deeplinks position the scroll at the
|
|
20
|
+
// right offset
|
|
21
|
+
inject(newFixedOffset);
|
|
22
|
+
};
|
|
23
|
+
update();
|
|
24
|
+
if (!observer && ResizeObserver) {
|
|
25
|
+
// const elements = $$("[data-fixed]");
|
|
26
|
+
observer = new ResizeObserver(function (entries) {
|
|
27
|
+
var newFixedOffset = 0;
|
|
28
|
+
entries.forEach(function (entry) {
|
|
29
|
+
newFixedOffset += entry.contentRect.height;
|
|
30
|
+
});
|
|
31
|
+
fixedOffset.current = newFixedOffset;
|
|
32
|
+
var updateOnResize = debounce(function () { return inject(newFixedOffset); }, 400);
|
|
33
|
+
updateOnResize();
|
|
34
|
+
});
|
|
35
|
+
$each("[data-fixed]", function ($el) {
|
|
36
|
+
if (observer)
|
|
37
|
+
observer.observe($el);
|
|
38
|
+
});
|
|
39
|
+
return function () {
|
|
40
|
+
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
var listener = listenResize(update);
|
|
45
|
+
return listener;
|
|
46
|
+
}
|
|
47
|
+
}, []);
|
|
48
|
+
return fixedOffset;
|
|
49
|
+
}
|
|
50
|
+
export default useFixedOffset;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface RectReadOnly {
|
|
2
|
+
readonly x: number;
|
|
3
|
+
readonly y: number;
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly top: number;
|
|
7
|
+
readonly right: number;
|
|
8
|
+
readonly bottom: number;
|
|
9
|
+
readonly left: number;
|
|
10
|
+
[key: string]: number;
|
|
11
|
+
}
|
|
12
|
+
declare type HTMLOrSVGElement = HTMLElement | SVGElement;
|
|
13
|
+
export declare type UseMeasureOptions = {
|
|
14
|
+
scroll?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare type UseMeasureReturn = [
|
|
17
|
+
(element: HTMLOrSVGElement | null) => void,
|
|
18
|
+
RectReadOnly,
|
|
19
|
+
() => void
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Use measure hook
|
|
23
|
+
*
|
|
24
|
+
* @borrows [pmndrs/react-use-measure](https://github.com/pmndrs/react-use-measure)
|
|
25
|
+
*/
|
|
26
|
+
export declare function useMeasure(options?: UseMeasureOptions): UseMeasureReturn;
|
|
27
|
+
export default useMeasure;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { __spreadArray } from "tslib";
|
|
2
|
+
import { useEffect, useState, useRef, useMemo } from "react";
|
|
3
|
+
import { debounce } from "@koine/utils";
|
|
4
|
+
import { listenResize, listenScroll, on, off } from "@koine/dom";
|
|
5
|
+
var observer;
|
|
6
|
+
// Adds native resize listener to window
|
|
7
|
+
function useOnWindowResize(onWindowResize) {
|
|
8
|
+
useEffect(function () {
|
|
9
|
+
var listener = listenResize(onWindowResize);
|
|
10
|
+
return listener;
|
|
11
|
+
}, [onWindowResize]);
|
|
12
|
+
}
|
|
13
|
+
function useOnWindowScroll(onScroll, enabled) {
|
|
14
|
+
useEffect(function () {
|
|
15
|
+
if (enabled) {
|
|
16
|
+
var listener = listenScroll(onScroll);
|
|
17
|
+
return listener;
|
|
18
|
+
}
|
|
19
|
+
return function () { return 0; };
|
|
20
|
+
}, [onScroll, enabled]);
|
|
21
|
+
}
|
|
22
|
+
// Returns a list of scroll offsets
|
|
23
|
+
function findScrollContainers(element) {
|
|
24
|
+
var result = [];
|
|
25
|
+
if (!element || element === document.body)
|
|
26
|
+
return result;
|
|
27
|
+
var _a = window.getComputedStyle(element), overflow = _a.overflow, overflowX = _a.overflowX, overflowY = _a.overflowY;
|
|
28
|
+
if ([overflow, overflowX, overflowY].some(function (prop) { return prop === "auto" || prop === "scroll"; }))
|
|
29
|
+
result.push(element);
|
|
30
|
+
return __spreadArray(__spreadArray([], result, true), findScrollContainers(element.parentElement), true);
|
|
31
|
+
}
|
|
32
|
+
var keys = [
|
|
33
|
+
"x",
|
|
34
|
+
"y",
|
|
35
|
+
"top",
|
|
36
|
+
"bottom",
|
|
37
|
+
"left",
|
|
38
|
+
"right",
|
|
39
|
+
"width",
|
|
40
|
+
"height",
|
|
41
|
+
];
|
|
42
|
+
var areBoundsEqual = function (a, b) {
|
|
43
|
+
return keys.every(function (key) { return a[key] === b[key]; });
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Use measure hook
|
|
47
|
+
*
|
|
48
|
+
* @borrows [pmndrs/react-use-measure](https://github.com/pmndrs/react-use-measure)
|
|
49
|
+
*/
|
|
50
|
+
export function useMeasure(options) {
|
|
51
|
+
var _a = (options || {}).scroll /* offsetSize = false */, scroll = _a === void 0 ? false : _a /* offsetSize = false */;
|
|
52
|
+
var _b = useState({
|
|
53
|
+
left: 0,
|
|
54
|
+
top: 0,
|
|
55
|
+
width: 0,
|
|
56
|
+
height: 0,
|
|
57
|
+
bottom: 0,
|
|
58
|
+
right: 0,
|
|
59
|
+
x: 0,
|
|
60
|
+
y: 0,
|
|
61
|
+
}), bounds = _b[0], setBounds = _b[1];
|
|
62
|
+
// keep all state in a ref
|
|
63
|
+
var state = useRef([
|
|
64
|
+
// element
|
|
65
|
+
null,
|
|
66
|
+
// scrollContainers
|
|
67
|
+
null,
|
|
68
|
+
// resizeObserver
|
|
69
|
+
null,
|
|
70
|
+
// lastBounds
|
|
71
|
+
bounds,
|
|
72
|
+
]);
|
|
73
|
+
// make sure to update state only as long as the component is truly mounted
|
|
74
|
+
var mounted = useRef(false);
|
|
75
|
+
useEffect(function () {
|
|
76
|
+
mounted.current = true;
|
|
77
|
+
return function () { return void (mounted.current = false); };
|
|
78
|
+
}, []);
|
|
79
|
+
// memoize handlers, so event-listeners know when they should update
|
|
80
|
+
var _c = useMemo(function () {
|
|
81
|
+
var callback = function () {
|
|
82
|
+
var _args = [];
|
|
83
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
84
|
+
_args[_i] = arguments[_i];
|
|
85
|
+
}
|
|
86
|
+
var _a = state.current, element = _a[0], lastBounds = _a[3];
|
|
87
|
+
if (!element)
|
|
88
|
+
return;
|
|
89
|
+
var size = element.getBoundingClientRect();
|
|
90
|
+
// if (element instanceof HTMLElement && offsetSize) {
|
|
91
|
+
// size.height = element.offsetHeight;
|
|
92
|
+
// size.width = element.offsetWidth;
|
|
93
|
+
// }
|
|
94
|
+
Object.freeze(size);
|
|
95
|
+
if (mounted.current && !areBoundsEqual(lastBounds, size)) {
|
|
96
|
+
state.current[3] = size;
|
|
97
|
+
setBounds(size);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var debouncedCallback = debounce(callback);
|
|
101
|
+
return [callback, debouncedCallback, debouncedCallback];
|
|
102
|
+
}, [setBounds /* , offsetSize */]), forceRefresh = _c[0] /* resizeChange */, scrollChange = _c[2];
|
|
103
|
+
// cleanup current scroll-listeners / observers
|
|
104
|
+
function removeListeners() {
|
|
105
|
+
var _a = state.current, scrollContainers = _a[1], resizeObserver = _a[2];
|
|
106
|
+
if (scrollContainers) {
|
|
107
|
+
scrollContainers.forEach(function (element) {
|
|
108
|
+
return off(element, "scroll", scrollChange);
|
|
109
|
+
});
|
|
110
|
+
state.current[1] = null;
|
|
111
|
+
}
|
|
112
|
+
if (resizeObserver) {
|
|
113
|
+
resizeObserver.disconnect();
|
|
114
|
+
state.current[2] = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// add scroll-listeners / observers
|
|
118
|
+
function addListeners() {
|
|
119
|
+
var _a = state.current, element = _a[0], scrollContainers = _a[1];
|
|
120
|
+
if (!element)
|
|
121
|
+
return;
|
|
122
|
+
if (!observer && ResizeObserver) {
|
|
123
|
+
observer = new ResizeObserver(scrollChange);
|
|
124
|
+
state.current[2] = observer;
|
|
125
|
+
observer.observe(element);
|
|
126
|
+
if (scroll && scrollContainers) {
|
|
127
|
+
scrollContainers.forEach(function (scrollContainer) {
|
|
128
|
+
return on(scrollContainer, "scroll", scrollChange, {
|
|
129
|
+
capture: true,
|
|
130
|
+
passive: true,
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// the ref we expose to the user
|
|
137
|
+
var ref = function (node) {
|
|
138
|
+
if (!node || node === state.current[0])
|
|
139
|
+
return;
|
|
140
|
+
removeListeners();
|
|
141
|
+
state.current[0] = node;
|
|
142
|
+
state.current[1] = findScrollContainers(node);
|
|
143
|
+
addListeners();
|
|
144
|
+
};
|
|
145
|
+
// add general event listeners
|
|
146
|
+
useOnWindowScroll(forceRefresh, Boolean(scroll));
|
|
147
|
+
useOnWindowResize(forceRefresh);
|
|
148
|
+
// respond to changes that are relevant for the listeners
|
|
149
|
+
useEffect(function () {
|
|
150
|
+
removeListeners();
|
|
151
|
+
addListeners();
|
|
152
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
153
|
+
}, [scroll /* , scrollChange, resizeChange */]);
|
|
154
|
+
useEffect(function () {
|
|
155
|
+
// operate on mount, @kuus on the original version there is no call on mount?
|
|
156
|
+
forceRefresh();
|
|
157
|
+
// remove all listeners when the components unmounts
|
|
158
|
+
return removeListeners;
|
|
159
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
160
|
+
}, []);
|
|
161
|
+
return [ref, bounds, forceRefresh];
|
|
162
|
+
}
|
|
163
|
+
export default useMeasure;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { isNumber } from "@koine/utils";
|
|
3
|
+
import { scrollTo } from "@koine/dom";
|
|
4
|
+
import { useFixedOffset } from "./useFixedOffset";
|
|
5
|
+
export function useSmoothScroll() {
|
|
6
|
+
var fixedOffset = useFixedOffset();
|
|
7
|
+
var scroll = useCallback(function (to, offset, callback) {
|
|
8
|
+
if (offset === void 0) { offset = 0; }
|
|
9
|
+
var top = undefined;
|
|
10
|
+
if (isNumber(to)) {
|
|
11
|
+
top = to;
|
|
12
|
+
}
|
|
13
|
+
else if (to) {
|
|
14
|
+
var el = document.getElementById(to);
|
|
15
|
+
if (el) {
|
|
16
|
+
top = el.getBoundingClientRect().top;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (isNumber(top)) {
|
|
20
|
+
top = top + window.scrollY - (fixedOffset.current + offset);
|
|
21
|
+
scrollTo(top, callback);
|
|
22
|
+
}
|
|
23
|
+
}, [fixedOffset]);
|
|
24
|
+
return scroll;
|
|
25
|
+
}
|
|
26
|
+
export default useSmoothScroll;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createUseMediaQueryWidth = void 0;
|
|
4
4
|
var react_1 = require("react");
|
|
5
5
|
var utils_1 = require("@koine/utils");
|
|
6
|
-
function
|
|
6
|
+
function createUseMediaQueryWidth(customBreakpoints) {
|
|
7
7
|
var queryResolvers = (0, utils_1.getMediaQueryWidthResolvers)(customBreakpoints);
|
|
8
8
|
return function useMediaQueryWidth(media) {
|
|
9
9
|
var definition = media.substring(1);
|
|
@@ -47,8 +47,8 @@ function useMediaQueryWidthCreator(customBreakpoints) {
|
|
|
47
47
|
return matches;
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
exports.
|
|
51
|
-
exports.default =
|
|
50
|
+
exports.createUseMediaQueryWidth = createUseMediaQueryWidth;
|
|
51
|
+
exports.default = createUseMediaQueryWidth;
|
|
52
52
|
//// without creator it would be:
|
|
53
53
|
//// ---------------------------------------------------------------------------
|
|
54
54
|
// import { useState, useEffect, useMemo } from "react";
|
package/node/helpers/index.js
CHANGED
|
@@ -2,4 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
var tslib_1 = require("tslib");
|
|
4
4
|
tslib_1.__exportStar(require("./classed"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./createUseMediaQueryWidth"), exports);
|
|
5
6
|
tslib_1.__exportStar(require("./extend-component"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeRefs = void 0;
|
|
4
|
+
function mergeRefs(refs) {
|
|
5
|
+
return function (value) {
|
|
6
|
+
refs.forEach(function (ref) {
|
|
7
|
+
if (typeof ref === "function") {
|
|
8
|
+
ref(value);
|
|
9
|
+
}
|
|
10
|
+
else if (ref != null) {
|
|
11
|
+
ref.current = value;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
exports.mergeRefs = mergeRefs;
|
|
17
|
+
exports.default = mergeRefs;
|
package/node/hooks/index.js
CHANGED
|
@@ -5,15 +5,17 @@ tslib_1.__exportStar(require("./useAsyncFn"), exports);
|
|
|
5
5
|
tslib_1.__exportStar(require("./useDateLocale"), exports);
|
|
6
6
|
tslib_1.__exportStar(require("./useEffectOnce"), exports);
|
|
7
7
|
tslib_1.__exportStar(require("./useFirstMountState"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./useFixedOffset"), exports);
|
|
8
9
|
tslib_1.__exportStar(require("./useFocus"), exports);
|
|
9
10
|
tslib_1.__exportStar(require("./useId"), exports);
|
|
10
11
|
tslib_1.__exportStar(require("./useIsomorphicLayoutEffect"), exports);
|
|
11
|
-
tslib_1.__exportStar(require("./
|
|
12
|
+
tslib_1.__exportStar(require("./useMeasure"), exports);
|
|
12
13
|
tslib_1.__exportStar(require("./useMount"), exports);
|
|
13
14
|
tslib_1.__exportStar(require("./useMountedState"), exports);
|
|
14
15
|
tslib_1.__exportStar(require("./usePrevious"), exports);
|
|
15
16
|
tslib_1.__exportStar(require("./useScrollPosition"), exports);
|
|
16
17
|
// export * from "./useScrollTo";
|
|
18
|
+
tslib_1.__exportStar(require("./useSmoothScroll"), exports);
|
|
17
19
|
tslib_1.__exportStar(require("./useTraceUpdate"), exports);
|
|
18
20
|
tslib_1.__exportStar(require("./useUpdateEffect"), exports);
|
|
19
21
|
tslib_1.__exportStar(require("./useWindowSize"), exports);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFixedOffset = void 0;
|
|
4
|
+
var react_1 = require("react");
|
|
5
|
+
var dom_1 = require("@koine/dom");
|
|
6
|
+
var utils_1 = require("@koine/utils");
|
|
7
|
+
var observer;
|
|
8
|
+
var inject = function (value) {
|
|
9
|
+
(0, dom_1.injectCss)("useFixedOffset", "html{scroll-padding-top: ".concat(value, "px}"));
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Maybe use [ResizeObserver polyfill](https://github.com/juggle/resize-observer)
|
|
13
|
+
*
|
|
14
|
+
* @see https://web.dev/resize-observer/
|
|
15
|
+
*/
|
|
16
|
+
function useFixedOffset() {
|
|
17
|
+
var fixedOffset = (0, react_1.useRef)(0);
|
|
18
|
+
(0, react_1.useLayoutEffect)(function () {
|
|
19
|
+
var update = function () {
|
|
20
|
+
var newFixedOffset = (0, dom_1.calculateFixedOffset)();
|
|
21
|
+
fixedOffset.current = newFixedOffset;
|
|
22
|
+
// inject this CSS make the hashed deeplinks position the scroll at the
|
|
23
|
+
// right offset
|
|
24
|
+
inject(newFixedOffset);
|
|
25
|
+
};
|
|
26
|
+
update();
|
|
27
|
+
if (!observer && ResizeObserver) {
|
|
28
|
+
// const elements = $$("[data-fixed]");
|
|
29
|
+
observer = new ResizeObserver(function (entries) {
|
|
30
|
+
var newFixedOffset = 0;
|
|
31
|
+
entries.forEach(function (entry) {
|
|
32
|
+
newFixedOffset += entry.contentRect.height;
|
|
33
|
+
});
|
|
34
|
+
fixedOffset.current = newFixedOffset;
|
|
35
|
+
var updateOnResize = (0, utils_1.debounce)(function () { return inject(newFixedOffset); }, 400);
|
|
36
|
+
updateOnResize();
|
|
37
|
+
});
|
|
38
|
+
(0, dom_1.$each)("[data-fixed]", function ($el) {
|
|
39
|
+
if (observer)
|
|
40
|
+
observer.observe($el);
|
|
41
|
+
});
|
|
42
|
+
return function () {
|
|
43
|
+
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
var listener = (0, dom_1.listenResize)(update);
|
|
48
|
+
return listener;
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
51
|
+
return fixedOffset;
|
|
52
|
+
}
|
|
53
|
+
exports.useFixedOffset = useFixedOffset;
|
|
54
|
+
exports.default = useFixedOffset;
|