@uxf/core-react 11.20.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/README.md +247 -0
- package/components/hide.d.ts +7 -0
- package/components/hide.js +14 -0
- package/components/show.d.ts +7 -0
- package/components/show.js +14 -0
- package/hooks/_use-simulated-button.d.ts +16 -0
- package/hooks/_use-simulated-button.js +49 -0
- package/hooks/use-anchor-props.d.ts +8 -0
- package/hooks/use-anchor-props.js +65 -0
- package/hooks/use-body-scroll-lock.d.ts +6 -0
- package/hooks/use-body-scroll-lock.js +35 -0
- package/hooks/use-button-props.d.ts +6 -0
- package/hooks/use-button-props.js +59 -0
- package/hooks/use-clickable-props.d.ts +8 -0
- package/hooks/use-clickable-props.js +43 -0
- package/hooks/use-focus-return.d.ts +1 -0
- package/hooks/use-focus-return.js +35 -0
- package/hooks/use-focus-trap.d.ts +2 -0
- package/hooks/use-focus-trap.js +146 -0
- package/hooks/use-input-focus.d.ts +7 -0
- package/hooks/use-input-focus.js +27 -0
- package/hooks/use-is-mounted.d.ts +1 -0
- package/hooks/use-is-mounted.js +10 -0
- package/hooks/use-isomorphic-layout-effect.d.ts +2 -0
- package/hooks/use-isomorphic-layout-effect.js +6 -0
- package/hooks/use-key.d.ts +10 -0
- package/hooks/use-key.js +28 -0
- package/hooks/use-latest.d.ts +2 -0
- package/hooks/use-latest.js +10 -0
- package/hooks/use-min-window-width.d.ts +1 -0
- package/hooks/use-min-window-width.js +29 -0
- package/hooks/use-mouse-drag-to-scroll.d.ts +2 -0
- package/hooks/use-mouse-drag-to-scroll.js +39 -0
- package/hooks/use-on-mount.d.ts +2 -0
- package/hooks/use-on-mount.js +10 -0
- package/hooks/use-on-unmount.d.ts +2 -0
- package/hooks/use-on-unmount.js +15 -0
- package/hooks/use-on-update.d.ts +2 -0
- package/hooks/use-on-update.js +15 -0
- package/hooks/use-pagination.d.ts +2 -0
- package/hooks/use-pagination.js +20 -0
- package/hooks/use-previous.d.ts +2 -0
- package/hooks/use-previous.js +12 -0
- package/hooks/use-raf-state.d.ts +2 -0
- package/hooks/use-raf-state.js +18 -0
- package/hooks/use-window-scroll.d.ts +4 -0
- package/hooks/use-window-scroll.js +22 -0
- package/hooks/use-window-size.d.ts +4 -0
- package/hooks/use-window-size.js +19 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# UXF Core-React
|
|
2
|
+
|
|
3
|
+
## Hooks
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { useBodyScrollLock } from "@uxf/core-react/hooks/use-body-scroll-lock";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
|
|
9
|
+
const innerRef = useRef<HTMLDivElement>(null);
|
|
10
|
+
const [isOpen, setIsOpen] = useState<boolean>();
|
|
11
|
+
|
|
12
|
+
const clearAllOnclose = false;
|
|
13
|
+
|
|
14
|
+
useBodyScrollLock<HTMLDivElement>(innerRef, isOpen, {
|
|
15
|
+
allowTouchMove: undefined, // https://github.com/willmcpo/body-scroll-lock#allowtouchmove
|
|
16
|
+
clearAllOnClose: false, // optionally call clearAllBodyScrollLocks method on unmount
|
|
17
|
+
reserveScrollBarGap: undefined, // https://github.com/willmcpo/body-scroll-lock#reservescrollbargap
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
<div ref={innerRef}>Element which activates scroll lock on its parent elements.</div>
|
|
21
|
+
```
|
|
22
|
+
```tsx
|
|
23
|
+
import { useIsMounted } from "@uxf/core-react/hooks/use-is-mounted";
|
|
24
|
+
|
|
25
|
+
const isMounted = useIsMounted();
|
|
26
|
+
```
|
|
27
|
+
```tsx
|
|
28
|
+
import { useIsomorphicLayoutEffect } from "@uxf/core-react/hooks/use-isomorphic-layout-effect";
|
|
29
|
+
|
|
30
|
+
useIsomorphicLayoutEffect(() => {/* code */}, [/* deps */]);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { useKey } from "@uxf/core-react/hooks/use-key";
|
|
35
|
+
import { useRef } from "react";
|
|
36
|
+
|
|
37
|
+
const targetRef = useRef<HTMLDivElement>(null);
|
|
38
|
+
const disabled = false; // eg. for passing disabled state
|
|
39
|
+
|
|
40
|
+
useKey<HTMLDivElement>("Enter", () => console.log("callback"), {
|
|
41
|
+
disabled,
|
|
42
|
+
targetRef, // if not provided, then `document` will be used
|
|
43
|
+
type: "keydown"
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
<div ref={targetRef} tabIndex={0}>Element with callback triggerable by enter key.</div>
|
|
47
|
+
```
|
|
48
|
+
```tsx
|
|
49
|
+
import { useMinWindowWidth } from "@uxf/core-react/hooks/use-min-window-width";
|
|
50
|
+
|
|
51
|
+
const isDesktop = useMinWindowWidth(1200);
|
|
52
|
+
const isDesktopWithDebounce = useMinWindowWidth(1200, 200); // will be updated every 200 ms
|
|
53
|
+
|
|
54
|
+
const example = isDesktop ? "desktop" : "tablet";
|
|
55
|
+
const debouncedExample = isDesktopWithDebounce ? "debouncedDesktop" : "debouncedTablet";
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { usePagination } from "@uxf/core-react/hooks/use-pagination";
|
|
60
|
+
|
|
61
|
+
const paginationItems = usePagination({ page: 1, count: 10 })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { useRafState } from "@uxf/core-react/hooks/use-raf-state";
|
|
66
|
+
|
|
67
|
+
const [state, setState] = useRafState<boolean>(false);
|
|
68
|
+
```
|
|
69
|
+
```tsx
|
|
70
|
+
import { useOnUnmount } from "@uxf/core-react/hooks/use-on-unmount";
|
|
71
|
+
|
|
72
|
+
const exampleCallback = () => {};
|
|
73
|
+
|
|
74
|
+
useOnUnmount(exampleCallback());
|
|
75
|
+
```
|
|
76
|
+
```tsx
|
|
77
|
+
import { useOnUpdate } from "@uxf/core-react/hooks/use-on-update";
|
|
78
|
+
|
|
79
|
+
useOnUpdate(() => {/* code */}, [/* deps */]);
|
|
80
|
+
```
|
|
81
|
+
```tsx
|
|
82
|
+
import { useWindowScroll } from "@uxf/core-react/hooks/use-window-scroll";
|
|
83
|
+
|
|
84
|
+
const windowScroll = useWindowScroll();
|
|
85
|
+
|
|
86
|
+
const example = windowScroll && windowScroll.y > 100 ? "scroled" : "on top";
|
|
87
|
+
```
|
|
88
|
+
```tsx
|
|
89
|
+
import { useWindowSize } from "@uxf/core-react/hooks/use-window-size";
|
|
90
|
+
|
|
91
|
+
const windowSize = useWindowSize();
|
|
92
|
+
|
|
93
|
+
const example = windowSize && windowSize.width > 1200 ? "desktop" : "tablet";
|
|
94
|
+
```
|
|
95
|
+
```tsx
|
|
96
|
+
import { useFocusTrap } from "@uxf/core-react/hooks/use-focus-trap";
|
|
97
|
+
import { useState } from "react";
|
|
98
|
+
|
|
99
|
+
const [active, setActive] = useState<boolean>();
|
|
100
|
+
|
|
101
|
+
const focusTrapRef = useFocusTrap(active);
|
|
102
|
+
|
|
103
|
+
<div ref={focusTrapRef}>Element which trap focus inside if `active` is truthy.</div>
|
|
104
|
+
```
|
|
105
|
+
```tsx
|
|
106
|
+
import { useFocusReturn } from "@uxf/core-react/hooks/use-focus-return";
|
|
107
|
+
import { useState } from "react";
|
|
108
|
+
|
|
109
|
+
const [active, setActive] = useState<boolean>();
|
|
110
|
+
|
|
111
|
+
// Returns focus to last active element, e.g. in Modal or Popover
|
|
112
|
+
useFocusReturn(active);
|
|
113
|
+
```
|
|
114
|
+
```tsx
|
|
115
|
+
import { useAnchorProps } from "@uxf/core-react/hooks/use-anchor-props";
|
|
116
|
+
import { AnchorHTMLAttributes } from "react";
|
|
117
|
+
|
|
118
|
+
// extends <a /> by `analyticsCallback`, `disabled`, `loading`, `submit` props
|
|
119
|
+
const anchorProps = useAnchorProps<AnchorHTMLAttributes<HTMLAnchorElement>>({
|
|
120
|
+
analyticsCallback: () => console.log("analytics"),
|
|
121
|
+
disabled: false,
|
|
122
|
+
href: "https://www.google.com/",
|
|
123
|
+
loading: false,
|
|
124
|
+
onClick: () => console.log("success"),
|
|
125
|
+
type: "submit", // simulate <button type="submit" /> function
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
<a {...anchorProps}>Click me</a>
|
|
129
|
+
|
|
130
|
+
// example with generics
|
|
131
|
+
import { UseAnchorProps, useAnchorProps } from "@uxf/core-react/hooks/use-anchor-props";
|
|
132
|
+
import { AnchorHTMLAttributes } from "react";
|
|
133
|
+
|
|
134
|
+
interface Props extends UseAnchorProps, AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
135
|
+
customProp?: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const anchorProps = useAnchorProps<Props>({
|
|
139
|
+
customProp: true,
|
|
140
|
+
loading: false,
|
|
141
|
+
href: "https://www.google.com/",
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
<a {...anchorProps}>Click me</a>
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
```tsx
|
|
148
|
+
import { useButtonProps } from "@uxf/core-react/hooks/use-button-props";
|
|
149
|
+
import { ButtonHTMLAttributes } from "react";
|
|
150
|
+
|
|
151
|
+
// extends <button /> by `analyticsCallback` and `loading` props
|
|
152
|
+
const buttonProps = useButtonProps<ButtonHTMLAttributes<HTMLButtonElement>>({
|
|
153
|
+
analyticsCallback: () => console.log("analytics"),
|
|
154
|
+
disabled: false,
|
|
155
|
+
loading: false,
|
|
156
|
+
onClick: () => console.log("success"),
|
|
157
|
+
type: "submit",
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
<button {...buttonProps}>Click me</button>
|
|
161
|
+
|
|
162
|
+
// example with generics
|
|
163
|
+
import { UseButtonProps, useButtonProps } from "@uxf/core-react/hooks/use-button-props";
|
|
164
|
+
import { ButtonHTMLAttributes } from "react";
|
|
165
|
+
|
|
166
|
+
interface Props extends UseButtonProps, ButtonHTMLAttributes<HTMLButtonElement> {
|
|
167
|
+
customProp?: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const buttonProps = useButtonProps<Props>({
|
|
171
|
+
customProp: true,
|
|
172
|
+
loading: false,
|
|
173
|
+
type: "submit",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
<button {...buttonProps}>Click me</button>
|
|
177
|
+
```
|
|
178
|
+
```tsx
|
|
179
|
+
import { useClickableProps } from "@uxf/core-react/hooks/use-clickable-props";
|
|
180
|
+
import { HTMLAttributes } from "react";
|
|
181
|
+
|
|
182
|
+
// extends any HTML element by `analyticsCallback`, `disabled`, `loading`, `submit` props
|
|
183
|
+
const clickableProps = useClickableProps<HTMLAttributes<HTMLDivElement>>({
|
|
184
|
+
analyticsCallback: () => console.log("analytics"),
|
|
185
|
+
disabled: false,
|
|
186
|
+
loading: false,
|
|
187
|
+
onClick: () => console.log("success"),
|
|
188
|
+
type: "submit", // simulate <button type="submit" /> function
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
<div {...clickableProps}>Click me</div>
|
|
192
|
+
|
|
193
|
+
// example with generics
|
|
194
|
+
import { UseClickableProps, useClickableProps } from "@uxf/core-react/hooks/use-clickable-props";
|
|
195
|
+
import { HTMLAttributes } from "react";
|
|
196
|
+
|
|
197
|
+
interface Props extends UseClickableProps, HTMLAttributes<HTMLDivElement> {
|
|
198
|
+
customProp?: boolean;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const buttonProps = useClickableProps<Props>({
|
|
202
|
+
customProp: true,
|
|
203
|
+
hidden: false,
|
|
204
|
+
loading: false,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
<button {...buttonProps}>Click me</button>
|
|
208
|
+
```
|
|
209
|
+
```tsx
|
|
210
|
+
import { useMouseDragToScroll } from "@uxf/core-react/hooks/use-mouse-drag-to-scroll";
|
|
211
|
+
|
|
212
|
+
const targetRef = useRef<HTMLDivElement>(null);
|
|
213
|
+
|
|
214
|
+
const style = useMouseDragToScroll(scrollRef);
|
|
215
|
+
|
|
216
|
+
<div style={style}>Drag to scroll</div>
|
|
217
|
+
```
|
|
218
|
+
```tsx
|
|
219
|
+
import { useInputFocus } from "@uxf/core-react/hooks/use-input-focus";
|
|
220
|
+
|
|
221
|
+
const focusRef = useRef<HTMLInputElement>(null); // or HTMLTextAreaElement
|
|
222
|
+
const { onBlur, onFocus } = props;
|
|
223
|
+
|
|
224
|
+
const input = useInputFocus(focusRef, onBlur, onFocus);
|
|
225
|
+
|
|
226
|
+
<div>Input is {input.focused}</div>
|
|
227
|
+
<button onClick={input.focus}>Focus input</button>
|
|
228
|
+
<input onBlur={input.onBlur} onFocus={input.onFocus} ref={focusRef} />
|
|
229
|
+
```
|
|
230
|
+
```tsx
|
|
231
|
+
import { useLatest } from "@uxf/core-react/hooks/use-latest";
|
|
232
|
+
|
|
233
|
+
const latestState = useLatest(someUnstableWhatever);
|
|
234
|
+
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
latestState.current(); // use newest state of 'someUnstableWhatever' without affecting this effetct update
|
|
237
|
+
}, [latestState])
|
|
238
|
+
```
|
|
239
|
+
```tsx
|
|
240
|
+
import { usePrevious } from "@uxf/core-react/hooks/use-previous";
|
|
241
|
+
|
|
242
|
+
const previousState = usePrevious(someUnstableWhatever);
|
|
243
|
+
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
previousState.current(); // use state of 'someUnstableWhatever' from previous render without affecting this effetct update
|
|
246
|
+
}, [previousState])
|
|
247
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Hide = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function Hide(props) {
|
|
9
|
+
if (props.when) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return react_1.default.createElement(react_1.default.Fragment, null, props.children);
|
|
13
|
+
}
|
|
14
|
+
exports.Hide = Hide;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Show = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function Show(props) {
|
|
9
|
+
if (!props.when) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return react_1.default.createElement(react_1.default.Fragment, null, props.children);
|
|
13
|
+
}
|
|
14
|
+
exports.Show = Show;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { KeyboardEventHandler, MouseEventHandler } from "react";
|
|
2
|
+
interface Props<T extends HTMLElement> {
|
|
3
|
+
analyticsCallback?: () => void;
|
|
4
|
+
isClickable: boolean;
|
|
5
|
+
isHyperlink: boolean;
|
|
6
|
+
onClick?: MouseEventHandler<T>;
|
|
7
|
+
onKeyDown?: KeyboardEventHandler<T>;
|
|
8
|
+
onKeyUp?: KeyboardEventHandler<T>;
|
|
9
|
+
submit?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function _useSimulatedButton<T extends HTMLElement>({ analyticsCallback, isClickable, isHyperlink, onClick, onKeyDown, onKeyUp, submit, }: Props<T>): {
|
|
12
|
+
onClick: MouseEventHandler<T>;
|
|
13
|
+
onKeyDown: KeyboardEventHandler<T>;
|
|
14
|
+
onKeyUp: KeyboardEventHandler<T>;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._useSimulatedButton = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function _useSimulatedButton({ analyticsCallback, isClickable, isHyperlink, onClick, onKeyDown, onKeyUp, submit, }) {
|
|
6
|
+
const _onClick = (0, react_1.useCallback)((e) => {
|
|
7
|
+
var _a;
|
|
8
|
+
if ((isClickable || isHyperlink) && analyticsCallback) {
|
|
9
|
+
analyticsCallback();
|
|
10
|
+
}
|
|
11
|
+
if (isClickable) {
|
|
12
|
+
if (submit) {
|
|
13
|
+
const closestForm = (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.closest("form");
|
|
14
|
+
if (closestForm) {
|
|
15
|
+
closestForm.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (onClick) {
|
|
19
|
+
onClick(e);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
}
|
|
25
|
+
}, [analyticsCallback, isClickable, isHyperlink, onClick, submit]);
|
|
26
|
+
const _onKeyUp = (0, react_1.useCallback)((e) => {
|
|
27
|
+
if (isClickable && (e.key === "Enter" || e.key === " ")) {
|
|
28
|
+
e.target.dispatchEvent(new MouseEvent("click", {
|
|
29
|
+
bubbles: true,
|
|
30
|
+
buttons: 1,
|
|
31
|
+
cancelable: true,
|
|
32
|
+
view: window,
|
|
33
|
+
}));
|
|
34
|
+
if (onKeyUp) {
|
|
35
|
+
onKeyUp(e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}, [isClickable, onKeyUp]);
|
|
39
|
+
const _onKeyDown = (0, react_1.useCallback)((e) => {
|
|
40
|
+
if (isClickable && e.key === " ") {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
if (onKeyDown) {
|
|
43
|
+
onKeyDown(e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, [isClickable, onKeyDown]);
|
|
47
|
+
return { onClick: _onClick, onKeyDown: _onKeyDown, onKeyUp: _onKeyUp };
|
|
48
|
+
}
|
|
49
|
+
exports._useSimulatedButton = _useSimulatedButton;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AnchorHTMLAttributes } from "react";
|
|
2
|
+
export interface UseAnchorProps {
|
|
3
|
+
analyticsCallback?: () => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
type?: "submit";
|
|
7
|
+
}
|
|
8
|
+
export declare function useAnchorProps<T extends AnchorHTMLAttributes<HTMLAnchorElement>>({ ["aria-busy"]: ariaBusy, ["aria-disabled"]: ariaDisabled, analyticsCallback, className, disabled, download, href, hrefLang, loading, media, onClick, onKeyDown, onKeyUp, ping, referrerPolicy, rel, role, tabIndex, target, type, ...restProps }: UseAnchorProps & T): T;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAnchorProps = void 0;
|
|
4
|
+
const classes_1 = require("@uxf/core/constants/classes");
|
|
5
|
+
const cx_1 = require("@uxf/core/utils/cx");
|
|
6
|
+
const _use_simulated_button_1 = require("./_use-simulated-button");
|
|
7
|
+
function useAnchorProps({ ["aria-busy"]: ariaBusy, ["aria-disabled"]: ariaDisabled, analyticsCallback, className, disabled, download, href, hrefLang, loading, media, onClick, onKeyDown, onKeyUp, ping, referrerPolicy, rel, role, tabIndex, target, type, ...restProps }) {
|
|
8
|
+
const isBusy = loading ? loading : Boolean(ariaBusy);
|
|
9
|
+
const isDisabled = disabled ? disabled : Boolean(ariaDisabled);
|
|
10
|
+
const isBusyOrDisabled = isBusy || isDisabled;
|
|
11
|
+
const submit = type === "submit";
|
|
12
|
+
const isButton = submit || !!onClick;
|
|
13
|
+
const tabIndexInteractive = isBusyOrDisabled ? -1 : tabIndex;
|
|
14
|
+
const _className = [isBusy && classes_1.CLASSES.IS_LOADING, disabled && classes_1.CLASSES.IS_DISABLED, className];
|
|
15
|
+
const simulatedButton = (0, _use_simulated_button_1._useSimulatedButton)({
|
|
16
|
+
analyticsCallback,
|
|
17
|
+
isClickable: !isBusyOrDisabled || isButton,
|
|
18
|
+
isHyperlink: !isBusyOrDisabled || !!href,
|
|
19
|
+
onClick,
|
|
20
|
+
onKeyDown,
|
|
21
|
+
onKeyUp,
|
|
22
|
+
submit,
|
|
23
|
+
});
|
|
24
|
+
if (href) {
|
|
25
|
+
return {
|
|
26
|
+
"aria-busy": isBusy,
|
|
27
|
+
"aria-disabled": isDisabled,
|
|
28
|
+
className: (0, cx_1.cx)(classes_1.CLASSES.IS_HOVERABLE, ..._className),
|
|
29
|
+
download,
|
|
30
|
+
href,
|
|
31
|
+
hrefLang,
|
|
32
|
+
media,
|
|
33
|
+
onClick: simulatedButton.onClick,
|
|
34
|
+
onKeyDown: simulatedButton.onKeyDown,
|
|
35
|
+
onKeyUp: simulatedButton.onKeyUp,
|
|
36
|
+
ping,
|
|
37
|
+
referrerPolicy,
|
|
38
|
+
rel: target === "_blank" ? (rel ? `noopener noreferrer ${rel}` : "noopener noreferrer") : rel,
|
|
39
|
+
role: role || (isButton ? "button" : undefined),
|
|
40
|
+
tabIndex: tabIndexInteractive,
|
|
41
|
+
target,
|
|
42
|
+
...restProps,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (isButton) {
|
|
46
|
+
return {
|
|
47
|
+
"aria-busy": isBusy,
|
|
48
|
+
"aria-disabled": isDisabled,
|
|
49
|
+
className: (0, cx_1.cx)(classes_1.CLASSES.IS_HOVERABLE, ..._className),
|
|
50
|
+
onClick: simulatedButton.onClick,
|
|
51
|
+
onKeyDown: simulatedButton.onKeyDown,
|
|
52
|
+
onKeyUp: simulatedButton.onKeyUp,
|
|
53
|
+
role: role || "button",
|
|
54
|
+
tabIndex: tabIndexInteractive !== null && tabIndexInteractive !== void 0 ? tabIndexInteractive : 0,
|
|
55
|
+
...restProps,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
className: (0, cx_1.cx)(..._className),
|
|
60
|
+
role: role || "none",
|
|
61
|
+
tabIndex,
|
|
62
|
+
...restProps,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
exports.useAnchorProps = useAnchorProps;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BodyScrollOptions } from "@uxf/core/utils/bodyScrollLock";
|
|
2
|
+
import { RefObject } from "react";
|
|
3
|
+
export interface UseBodyScrollLockOptions extends BodyScrollOptions {
|
|
4
|
+
clearAllOnClose?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function useBodyScrollLock<T extends HTMLElement>(containerRef: RefObject<T>, isOpen: boolean, { allowTouchMove, clearAllOnClose, reserveScrollBarGap }?: Partial<UseBodyScrollLockOptions>): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useBodyScrollLock = void 0;
|
|
4
|
+
const bodyScrollLock_1 = require("@uxf/core/utils/bodyScrollLock");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
function useBodyScrollLock(containerRef, isOpen, { allowTouchMove, clearAllOnClose, reserveScrollBarGap } = {}) {
|
|
7
|
+
(0, react_1.useEffect)(() => {
|
|
8
|
+
const node = containerRef.current;
|
|
9
|
+
if (isOpen && node) {
|
|
10
|
+
(0, bodyScrollLock_1.disableBodyScroll)(node, {
|
|
11
|
+
allowTouchMove: allowTouchMove ||
|
|
12
|
+
((element) => {
|
|
13
|
+
var _a;
|
|
14
|
+
while (element !== document.body) {
|
|
15
|
+
if (element.getAttribute("data-body-scroll-lock-ignore") !== null) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
element = (_a = element.parentElement) !== null && _a !== void 0 ? _a : element;
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}),
|
|
22
|
+
reserveScrollBarGap,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return () => {
|
|
26
|
+
if (node) {
|
|
27
|
+
(0, bodyScrollLock_1.enableBodyScroll)(node);
|
|
28
|
+
}
|
|
29
|
+
if (clearAllOnClose) {
|
|
30
|
+
(0, bodyScrollLock_1.clearAllBodyScrollLocks)();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}, [allowTouchMove, clearAllOnClose, containerRef, isOpen, reserveScrollBarGap]);
|
|
34
|
+
}
|
|
35
|
+
exports.useBodyScrollLock = useBodyScrollLock;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ButtonHTMLAttributes } from "react";
|
|
2
|
+
export interface UseButtonProps {
|
|
3
|
+
analyticsCallback?: () => void;
|
|
4
|
+
loading?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function useButtonProps<T extends ButtonHTMLAttributes<HTMLButtonElement>>({ ["aria-busy"]: ariaBusy, ["aria-disabled"]: ariaDisabled, autoFocus, analyticsCallback, className, disabled, formAction, formEncType, formMethod, formNoValidate, formTarget, loading, name, onClick, role, tabIndex, type, value, ...restProps }: UseButtonProps & T): T;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useButtonProps = void 0;
|
|
4
|
+
const classes_1 = require("@uxf/core/constants/classes");
|
|
5
|
+
const cx_1 = require("@uxf/core/utils/cx");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
function useButtonProps({ ["aria-busy"]: ariaBusy, ["aria-disabled"]: ariaDisabled, autoFocus, analyticsCallback, className, disabled, formAction, formEncType, formMethod, formNoValidate, formTarget, loading, name, onClick, role, tabIndex, type, value, ...restProps }) {
|
|
8
|
+
const isBusy = loading ? loading : Boolean(ariaBusy);
|
|
9
|
+
const isDisabled = disabled ? disabled : Boolean(ariaDisabled);
|
|
10
|
+
const isBusyOrDisabled = isBusy || isDisabled;
|
|
11
|
+
const _className = [isBusy && classes_1.CLASSES.IS_LOADING, isDisabled && classes_1.CLASSES.IS_DISABLED, className];
|
|
12
|
+
const _onClick = (0, react_1.useCallback)((e) => {
|
|
13
|
+
if (isBusyOrDisabled) {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
if (analyticsCallback) {
|
|
18
|
+
analyticsCallback();
|
|
19
|
+
}
|
|
20
|
+
if (onClick) {
|
|
21
|
+
onClick(e);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}, [analyticsCallback, isBusyOrDisabled, onClick]);
|
|
25
|
+
if (onClick || type === "reset" || type === "submit") {
|
|
26
|
+
let submitProps = {};
|
|
27
|
+
if (type === "submit") {
|
|
28
|
+
submitProps = {
|
|
29
|
+
formAction,
|
|
30
|
+
formEncType,
|
|
31
|
+
formMethod,
|
|
32
|
+
formNoValidate,
|
|
33
|
+
formTarget,
|
|
34
|
+
name,
|
|
35
|
+
value,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
"aria-busy": isBusy,
|
|
40
|
+
"aria-disabled": ariaDisabled,
|
|
41
|
+
autoFocus,
|
|
42
|
+
className: (0, cx_1.cx)(classes_1.CLASSES.IS_HOVERABLE, ..._className),
|
|
43
|
+
disabled,
|
|
44
|
+
onClick: _onClick,
|
|
45
|
+
role,
|
|
46
|
+
tabIndex: isBusyOrDisabled ? -1 : tabIndex,
|
|
47
|
+
type,
|
|
48
|
+
...submitProps,
|
|
49
|
+
...restProps,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
className: (0, cx_1.cx)(..._className),
|
|
54
|
+
role,
|
|
55
|
+
tabIndex: tabIndex !== null && tabIndex !== void 0 ? tabIndex : -1,
|
|
56
|
+
...restProps,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
exports.useButtonProps = useButtonProps;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { HTMLAttributes } from "react";
|
|
2
|
+
export interface UseClickableProps {
|
|
3
|
+
analyticsCallback?: () => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
type?: "submit";
|
|
7
|
+
}
|
|
8
|
+
export declare function useClickableProps<T extends HTMLAttributes<HTMLElement>>({ ["aria-busy"]: ariaBusy, ["aria-disabled"]: ariaDisabled, analyticsCallback, className, disabled, loading, onClick, onKeyDown, onKeyUp, role, tabIndex, type, ...restProps }: UseClickableProps & T): T;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useClickableProps = void 0;
|
|
4
|
+
const classes_1 = require("@uxf/core/constants/classes");
|
|
5
|
+
const cx_1 = require("@uxf/core/utils/cx");
|
|
6
|
+
const _use_simulated_button_1 = require("./_use-simulated-button");
|
|
7
|
+
function useClickableProps({ ["aria-busy"]: ariaBusy, ["aria-disabled"]: ariaDisabled, analyticsCallback, className, disabled, loading, onClick, onKeyDown, onKeyUp, role, tabIndex, type, ...restProps }) {
|
|
8
|
+
const isBusy = loading ? loading : Boolean(ariaBusy);
|
|
9
|
+
const isDisabled = disabled ? disabled : Boolean(ariaDisabled);
|
|
10
|
+
const isBusyOrDisabled = isBusy || isDisabled;
|
|
11
|
+
const submit = type === "submit";
|
|
12
|
+
const isButton = submit || !!onClick;
|
|
13
|
+
const _className = [isBusy && classes_1.CLASSES.IS_LOADING, isDisabled && classes_1.CLASSES.IS_DISABLED, className];
|
|
14
|
+
const simulatedButton = (0, _use_simulated_button_1._useSimulatedButton)({
|
|
15
|
+
analyticsCallback,
|
|
16
|
+
isClickable: !isBusyOrDisabled || isButton,
|
|
17
|
+
isHyperlink: false,
|
|
18
|
+
onClick,
|
|
19
|
+
onKeyDown,
|
|
20
|
+
onKeyUp,
|
|
21
|
+
submit,
|
|
22
|
+
});
|
|
23
|
+
if (isButton) {
|
|
24
|
+
return {
|
|
25
|
+
"aria-busy": isBusy,
|
|
26
|
+
"aria-disabled": isDisabled,
|
|
27
|
+
className: (0, cx_1.cx)(classes_1.CLASSES.IS_HOVERABLE, ..._className),
|
|
28
|
+
onClick: simulatedButton.onClick,
|
|
29
|
+
onKeyDown: simulatedButton.onKeyDown,
|
|
30
|
+
onKeyUp: simulatedButton.onKeyUp,
|
|
31
|
+
role: role || "button",
|
|
32
|
+
tabIndex: isBusyOrDisabled ? -1 : tabIndex !== null && tabIndex !== void 0 ? tabIndex : 0,
|
|
33
|
+
...restProps,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
className: (0, cx_1.cx)(..._className),
|
|
38
|
+
role,
|
|
39
|
+
tabIndex,
|
|
40
|
+
...restProps,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
exports.useClickableProps = useClickableProps;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useFocusReturn(opened: boolean, transitionDuration?: number, shouldReturnFocus?: boolean): () => void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFocusReturn = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const use_on_update_1 = require("./use-on-update");
|
|
6
|
+
function useFocusReturn(opened, transitionDuration = 0, shouldReturnFocus = true) {
|
|
7
|
+
const lastActiveElement = (0, react_1.useRef)();
|
|
8
|
+
const returnFocus = () => {
|
|
9
|
+
const node = lastActiveElement.current;
|
|
10
|
+
if (node && "focus" in node && typeof node.focus === "function") {
|
|
11
|
+
node.focus();
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
(0, use_on_update_1.useOnUpdate)(() => {
|
|
15
|
+
let timeout = -1;
|
|
16
|
+
const clearFocusTimeout = (e) => {
|
|
17
|
+
if (e.key === "Tab") {
|
|
18
|
+
clearTimeout(timeout);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
document.addEventListener("keydown", clearFocusTimeout);
|
|
22
|
+
if (opened) {
|
|
23
|
+
lastActiveElement.current = document.activeElement;
|
|
24
|
+
}
|
|
25
|
+
else if (shouldReturnFocus) {
|
|
26
|
+
timeout = window.setTimeout(returnFocus, transitionDuration + 10);
|
|
27
|
+
}
|
|
28
|
+
return () => {
|
|
29
|
+
clearTimeout(timeout);
|
|
30
|
+
document.removeEventListener("keydown", clearFocusTimeout);
|
|
31
|
+
};
|
|
32
|
+
}, [opened]);
|
|
33
|
+
return returnFocus;
|
|
34
|
+
}
|
|
35
|
+
exports.useFocusReturn = useFocusReturn;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFocusTrap = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const use_on_unmount_1 = require("./use-on-unmount");
|
|
6
|
+
const TABBABLE_NODES = /input|select|textarea|button|object/;
|
|
7
|
+
const FOCUS_SELECTOR = "a, input, select, textarea, button, object, [tabindex]";
|
|
8
|
+
function hidden(element) {
|
|
9
|
+
return element.style.display === "none";
|
|
10
|
+
}
|
|
11
|
+
function visible(element) {
|
|
12
|
+
let parentElement = element;
|
|
13
|
+
while (parentElement) {
|
|
14
|
+
if (parentElement === document.body) {
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
if (hidden(parentElement)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
parentElement = parentElement.parentElement;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function getElementTabIndex(element) {
|
|
25
|
+
const tabIndex = element.getAttribute("tabindex");
|
|
26
|
+
if (tabIndex === null) {
|
|
27
|
+
return NaN;
|
|
28
|
+
}
|
|
29
|
+
return parseInt(tabIndex, 10);
|
|
30
|
+
}
|
|
31
|
+
function focusable(element) {
|
|
32
|
+
const nodeName = element.nodeName.toLowerCase();
|
|
33
|
+
const isTabIndexNotNaN = !Number.isNaN(getElementTabIndex(element));
|
|
34
|
+
const res = (TABBABLE_NODES.test(nodeName) && !element.disabled) ||
|
|
35
|
+
(element instanceof HTMLAnchorElement ? Boolean(element.href) || isTabIndexNotNaN : isTabIndexNotNaN);
|
|
36
|
+
return res && visible(element);
|
|
37
|
+
}
|
|
38
|
+
function tabbable(element) {
|
|
39
|
+
const tabIndex = getElementTabIndex(element);
|
|
40
|
+
const isTabIndexNaN = Number.isNaN(tabIndex);
|
|
41
|
+
return (isTabIndexNaN || tabIndex >= 0) && focusable(element);
|
|
42
|
+
}
|
|
43
|
+
function findTabbableDescendants(element) {
|
|
44
|
+
return Array.from(element.querySelectorAll(FOCUS_SELECTOR)).filter(tabbable);
|
|
45
|
+
}
|
|
46
|
+
function scopeTab(node, event) {
|
|
47
|
+
const _tabbable = findTabbableDescendants(node);
|
|
48
|
+
if (!_tabbable.length) {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const finalTabbable = _tabbable[event.shiftKey ? 0 : _tabbable.length - 1];
|
|
53
|
+
const leavingFinalTabbable = finalTabbable === document.activeElement || node === document.activeElement;
|
|
54
|
+
if (!leavingFinalTabbable) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
const target = _tabbable[event.shiftKey ? _tabbable.length - 1 : 0];
|
|
59
|
+
if (target) {
|
|
60
|
+
target.focus();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function createAriaHider(containerNode, selector = "body > :not(script)") {
|
|
64
|
+
const rootNodes = Array.from(document.querySelectorAll(selector)).map((node) => {
|
|
65
|
+
if (node.contains(containerNode)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const ariaHidden = node.getAttribute("aria-hidden");
|
|
69
|
+
if (ariaHidden === null || ariaHidden === "false") {
|
|
70
|
+
node.setAttribute("aria-hidden", "true");
|
|
71
|
+
}
|
|
72
|
+
return { node, ariaHidden };
|
|
73
|
+
});
|
|
74
|
+
return () => {
|
|
75
|
+
rootNodes.forEach((item) => {
|
|
76
|
+
if (!item) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (item.ariaHidden === null) {
|
|
80
|
+
item.node.removeAttribute("aria-hidden");
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
item.node.setAttribute("aria-hidden", item.ariaHidden);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function useFocusTrap(active = true) {
|
|
89
|
+
const ref = (0, react_1.useRef)();
|
|
90
|
+
const restoreAria = (0, react_1.useRef)();
|
|
91
|
+
const timer = (0, react_1.useRef)();
|
|
92
|
+
const setRef = (0, react_1.useCallback)((node) => {
|
|
93
|
+
if (!active) {
|
|
94
|
+
ref.current = null;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (restoreAria.current) {
|
|
98
|
+
restoreAria.current();
|
|
99
|
+
}
|
|
100
|
+
if (node) {
|
|
101
|
+
const processNode = (_node) => {
|
|
102
|
+
restoreAria.current = createAriaHider(_node);
|
|
103
|
+
let focusElement = node.querySelector("[data-autofocus]");
|
|
104
|
+
if (!focusElement) {
|
|
105
|
+
const children = Array.from(node.querySelectorAll(FOCUS_SELECTOR));
|
|
106
|
+
focusElement = children.find(tabbable) || children.find(focusable) || null;
|
|
107
|
+
if (!focusElement && focusable(node)) {
|
|
108
|
+
focusElement = node;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (focusElement) {
|
|
112
|
+
focusElement.focus();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
// Delay processing the HTML node by a frame. This ensures focus is assigned correctly.
|
|
116
|
+
timer.current = window.setTimeout(() => {
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
118
|
+
if (node.ownerDocument) {
|
|
119
|
+
processNode(node);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
ref.current = node;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
ref.current = null;
|
|
126
|
+
}
|
|
127
|
+
}, [active]);
|
|
128
|
+
(0, react_1.useEffect)(() => {
|
|
129
|
+
if (!active) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const handler = (e) => {
|
|
133
|
+
const node = ref.current;
|
|
134
|
+
if (e.key === "Tab" && node) {
|
|
135
|
+
scopeTab(node, e);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
document.addEventListener("keydown", handler);
|
|
139
|
+
return () => {
|
|
140
|
+
document.removeEventListener("keydown", handler);
|
|
141
|
+
};
|
|
142
|
+
}, [active]);
|
|
143
|
+
(0, use_on_unmount_1.useOnUnmount)(() => clearTimeout(timer.current));
|
|
144
|
+
return setRef;
|
|
145
|
+
}
|
|
146
|
+
exports.useFocusTrap = useFocusTrap;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FocusEventHandler, MouseEventHandler, RefObject } from "react";
|
|
2
|
+
export declare function useInputFocus<T extends HTMLInputElement | HTMLTextAreaElement | HTMLElement>(focusRef: RefObject<T>, onBlur?: FocusEventHandler<T>, onFocus?: FocusEventHandler<T>): {
|
|
3
|
+
focused: boolean;
|
|
4
|
+
focus: MouseEventHandler<HTMLElement>;
|
|
5
|
+
onBlur: FocusEventHandler<T>;
|
|
6
|
+
onFocus: FocusEventHandler<T>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useInputFocus = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useInputFocus(focusRef, onBlur, onFocus) {
|
|
6
|
+
const [focused, setFocused] = (0, react_1.useState)(false);
|
|
7
|
+
const focus = (0, react_1.useCallback)(() => {
|
|
8
|
+
const node = focusRef.current;
|
|
9
|
+
if (node) {
|
|
10
|
+
node.focus();
|
|
11
|
+
}
|
|
12
|
+
}, [focusRef]);
|
|
13
|
+
const _onFocus = (0, react_1.useCallback)((e) => {
|
|
14
|
+
setFocused(true);
|
|
15
|
+
if (onFocus) {
|
|
16
|
+
onFocus(e);
|
|
17
|
+
}
|
|
18
|
+
}, [onFocus]);
|
|
19
|
+
const _onBlur = (0, react_1.useCallback)((e) => {
|
|
20
|
+
setFocused(false);
|
|
21
|
+
if (onBlur) {
|
|
22
|
+
onBlur(e);
|
|
23
|
+
}
|
|
24
|
+
}, [onBlur]);
|
|
25
|
+
return { focused, focus, onBlur: _onBlur, onFocus: _onFocus };
|
|
26
|
+
}
|
|
27
|
+
exports.useInputFocus = useInputFocus;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useIsMounted(): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useIsMounted = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useIsMounted() {
|
|
6
|
+
const [isMounted, setIsMounted] = (0, react_1.useState)(false);
|
|
7
|
+
(0, react_1.useEffect)(() => setIsMounted(true), []);
|
|
8
|
+
return isMounted;
|
|
9
|
+
}
|
|
10
|
+
exports.useIsMounted = useIsMounted;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useIsomorphicLayoutEffect = void 0;
|
|
4
|
+
const isBrowser_1 = require("@uxf/core/utils/isBrowser");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
exports.useIsomorphicLayoutEffect = isBrowser_1.isBrowser ? react_1.useLayoutEffect : react_1.useEffect;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RefObject } from "react";
|
|
2
|
+
type KeyPredicate = (e: KeyboardEvent) => boolean;
|
|
3
|
+
export type KeyFilter = null | undefined | string | KeyPredicate;
|
|
4
|
+
export interface UseKeyOptions<T extends HTMLElement> {
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
targetRef?: RefObject<T>;
|
|
7
|
+
type: "keydown" | "keyup" | "keypress";
|
|
8
|
+
}
|
|
9
|
+
export declare function useKey<T extends HTMLElement>(keyFilter: KeyFilter, callback: (e: KeyboardEvent) => void, { disabled, targetRef, type }?: Partial<UseKeyOptions<T>>): void;
|
|
10
|
+
export {};
|
package/hooks/use-key.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useKey = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const createKeyPredicate = (keyFilter) => typeof keyFilter === "function"
|
|
6
|
+
? keyFilter
|
|
7
|
+
: typeof keyFilter === "string"
|
|
8
|
+
? (e) => e.key === keyFilter
|
|
9
|
+
: () => false;
|
|
10
|
+
function useKey(keyFilter, callback, { disabled, targetRef, type = "keydown" } = {}) {
|
|
11
|
+
(0, react_1.useEffect)(() => {
|
|
12
|
+
if (disabled) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const thisNode = (targetRef ? targetRef.current : document);
|
|
16
|
+
const predicate = createKeyPredicate(keyFilter);
|
|
17
|
+
const handler = (e) => {
|
|
18
|
+
if (predicate(e)) {
|
|
19
|
+
callback(e);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
thisNode === null || thisNode === void 0 ? void 0 : thisNode.addEventListener(type, handler);
|
|
23
|
+
return () => {
|
|
24
|
+
thisNode === null || thisNode === void 0 ? void 0 : thisNode.removeEventListener(type, handler);
|
|
25
|
+
};
|
|
26
|
+
}, [callback, disabled, keyFilter, targetRef, type]);
|
|
27
|
+
}
|
|
28
|
+
exports.useKey = useKey;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useLatest = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useLatest(current) {
|
|
6
|
+
const storedValue = (0, react_1.useRef)(current);
|
|
7
|
+
storedValue.current = current;
|
|
8
|
+
return storedValue;
|
|
9
|
+
}
|
|
10
|
+
exports.useLatest = useLatest;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useMinWindowWidth(minWidth: number, debounce?: number): boolean | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMinWindowWidth = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const use_raf_state_1 = require("./use-raf-state");
|
|
6
|
+
function useMinWindowWidth(minWidth, debounce = 0) {
|
|
7
|
+
const [state, setState] = (0, use_raf_state_1.useRafState)();
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
const stateHandler = () => setState(window.innerWidth >= minWidth);
|
|
10
|
+
let timer;
|
|
11
|
+
const handler = () => {
|
|
12
|
+
if (debounce > 0) {
|
|
13
|
+
window.clearTimeout(timer);
|
|
14
|
+
timer = window.setTimeout(stateHandler, debounce);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
stateHandler();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
stateHandler();
|
|
21
|
+
window.addEventListener("resize", handler);
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener("resize", handler);
|
|
24
|
+
window.clearTimeout(timer);
|
|
25
|
+
};
|
|
26
|
+
}, [debounce, minWidth, setState]);
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
exports.useMinWindowWidth = useMinWindowWidth;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMouseDragToScroll = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useMouseDragToScroll(containerRef) {
|
|
6
|
+
const position = (0, react_1.useRef)({ left: 0, top: 0, x: 0, y: 0 });
|
|
7
|
+
const [style, setStyle] = (0, react_1.useState)();
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
const node = containerRef.current;
|
|
10
|
+
if (!node) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const mouseMoveHandler = (e) => {
|
|
14
|
+
const dx = e.clientX - position.current.x;
|
|
15
|
+
const dy = e.clientY - position.current.y;
|
|
16
|
+
node.scrollLeft = position.current.left - dx;
|
|
17
|
+
node.scrollTop = position.current.top - dy;
|
|
18
|
+
};
|
|
19
|
+
const mouseUpHandler = () => {
|
|
20
|
+
setStyle((prev) => ({ ...prev, cursor: "grab", userSelect: "unset" }));
|
|
21
|
+
document.removeEventListener("mousemove", mouseMoveHandler);
|
|
22
|
+
document.removeEventListener("mouseup", mouseUpHandler);
|
|
23
|
+
};
|
|
24
|
+
const mouseDownHandler = (e) => {
|
|
25
|
+
position.current = { left: node.scrollLeft, top: node.scrollTop, x: e.clientX, y: e.clientY };
|
|
26
|
+
setStyle((prev) => ({ ...prev, cursor: "grabbing", userSelect: "none" }));
|
|
27
|
+
document.addEventListener("mousemove", mouseMoveHandler);
|
|
28
|
+
document.addEventListener("mouseup", mouseUpHandler);
|
|
29
|
+
};
|
|
30
|
+
node.addEventListener("mousedown", mouseDownHandler);
|
|
31
|
+
return () => {
|
|
32
|
+
node.removeEventListener("mousedown", mouseDownHandler);
|
|
33
|
+
document.removeEventListener("mousedown", mouseMoveHandler);
|
|
34
|
+
document.removeEventListener("mousedown", mouseUpHandler);
|
|
35
|
+
};
|
|
36
|
+
}, [containerRef]);
|
|
37
|
+
return style;
|
|
38
|
+
}
|
|
39
|
+
exports.useMouseDragToScroll = useMouseDragToScroll;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOnMount = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useOnMount(effect) {
|
|
6
|
+
(0, react_1.useEffect)(() => {
|
|
7
|
+
return effect();
|
|
8
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
9
|
+
}
|
|
10
|
+
exports.useOnMount = useOnMount;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOnUnmount = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useOnUnmount(effect) {
|
|
6
|
+
const effectRef = (0, react_1.useRef)(effect);
|
|
7
|
+
// update the ref each render so if it changed the newest callback will be invoked
|
|
8
|
+
effectRef.current = effect;
|
|
9
|
+
(0, react_1.useEffect)(() => {
|
|
10
|
+
return () => {
|
|
11
|
+
effectRef.current();
|
|
12
|
+
};
|
|
13
|
+
}, []);
|
|
14
|
+
}
|
|
15
|
+
exports.useOnUnmount = useOnUnmount;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOnUpdate = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useOnUpdate(effect, deps) {
|
|
6
|
+
const firstRun = (0, react_1.useRef)(true);
|
|
7
|
+
(0, react_1.useEffect)(() => {
|
|
8
|
+
if (firstRun.current) {
|
|
9
|
+
firstRun.current = false;
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
return effect();
|
|
13
|
+
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
|
14
|
+
}
|
|
15
|
+
exports.useOnUpdate = useOnUpdate;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePagination = void 0;
|
|
4
|
+
const pagination_1 = require("@uxf/core/utils/pagination");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const usePagination = (config) => {
|
|
7
|
+
return (0, react_1.useMemo)(() => (0, pagination_1.paginationGenerate)(config),
|
|
8
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9
|
+
[
|
|
10
|
+
config.boundaryCount,
|
|
11
|
+
config.siblingCount,
|
|
12
|
+
config.count,
|
|
13
|
+
config.page,
|
|
14
|
+
config.hideNextButton,
|
|
15
|
+
config.hidePrevButton,
|
|
16
|
+
config.showFirstButton,
|
|
17
|
+
config.showLastButton,
|
|
18
|
+
]);
|
|
19
|
+
};
|
|
20
|
+
exports.usePagination = usePagination;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePrevious = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function usePrevious(current) {
|
|
6
|
+
const storedValue = (0, react_1.useRef)(current);
|
|
7
|
+
(0, react_1.useEffect)(() => {
|
|
8
|
+
storedValue.current = current;
|
|
9
|
+
});
|
|
10
|
+
return storedValue;
|
|
11
|
+
}
|
|
12
|
+
exports.usePrevious = usePrevious;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useRafState = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const use_on_unmount_1 = require("./use-on-unmount");
|
|
6
|
+
function useRafState(initialState) {
|
|
7
|
+
const frame = (0, react_1.useRef)(0);
|
|
8
|
+
const [state, setState] = (0, react_1.useState)(initialState);
|
|
9
|
+
const setRafState = (0, react_1.useCallback)((value) => {
|
|
10
|
+
cancelAnimationFrame(frame.current);
|
|
11
|
+
frame.current = requestAnimationFrame(() => {
|
|
12
|
+
setState(value);
|
|
13
|
+
});
|
|
14
|
+
}, []);
|
|
15
|
+
(0, use_on_unmount_1.useOnUnmount)(() => cancelAnimationFrame(frame.current));
|
|
16
|
+
return [state, setRafState];
|
|
17
|
+
}
|
|
18
|
+
exports.useRafState = useRafState;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useWindowScroll = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const use_raf_state_1 = require("./use-raf-state");
|
|
6
|
+
function useWindowScroll() {
|
|
7
|
+
const [state, setState] = (0, use_raf_state_1.useRafState)();
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
const handler = () => setState({
|
|
10
|
+
x: window.scrollX,
|
|
11
|
+
y: window.scrollY,
|
|
12
|
+
});
|
|
13
|
+
handler();
|
|
14
|
+
window.addEventListener("scroll", handler, {
|
|
15
|
+
capture: false,
|
|
16
|
+
passive: true,
|
|
17
|
+
});
|
|
18
|
+
return () => window.removeEventListener("scroll", handler);
|
|
19
|
+
}, [setState]);
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
exports.useWindowScroll = useWindowScroll;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useWindowSize = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const use_raf_state_1 = require("./use-raf-state");
|
|
6
|
+
function useWindowSize() {
|
|
7
|
+
const [state, setState] = (0, use_raf_state_1.useRafState)();
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
const handler = () => setState({
|
|
10
|
+
width: window.innerWidth,
|
|
11
|
+
height: window.innerHeight,
|
|
12
|
+
});
|
|
13
|
+
handler();
|
|
14
|
+
window.addEventListener("resize", handler);
|
|
15
|
+
return () => window.removeEventListener("resize", handler);
|
|
16
|
+
}, [setState]);
|
|
17
|
+
return state;
|
|
18
|
+
}
|
|
19
|
+
exports.useWindowSize = useWindowSize;
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uxf/core-react",
|
|
3
|
+
"version": "11.20.0",
|
|
4
|
+
"description": "UXF Core",
|
|
5
|
+
"author": "UX Fans s.r.o",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -P tsconfig.json",
|
|
12
|
+
"typecheck": "tsc --noEmit --skipLibCheck"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@uxf/core": "11.20.0"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"react": ">=18.2.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "18.2.63",
|
|
22
|
+
"react": "18.2.0"
|
|
23
|
+
}
|
|
24
|
+
}
|