@react-aria/utils 3.8.0 → 3.10.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/main.js +173 -45
- package/dist/main.js.map +1 -1
- package/dist/module.js +163 -43
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +3 -0
- package/src/mergeProps.ts +24 -27
- package/src/useDescription.ts +2 -1
- package/src/useEvent.ts +39 -0
- package/src/useGlobalListeners.ts +8 -2
- package/src/useId.ts +28 -13
- package/src/useObjectRef.ts +46 -0
- package/src/useSyncRef.ts +1 -1
- package/src/useValueEffect.ts +65 -0
- package/src/useViewportSize.ts +7 -1
package/dist/module.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { clamp, snapValueToStep } from "@react-stately/utils";
|
|
2
2
|
export { clamp, snapValueToStep };
|
|
3
3
|
import _clsx from "clsx";
|
|
4
|
+
import _babelRuntimeHelpersEsmExtends from "@babel/runtime/helpers/esm/extends";
|
|
4
5
|
import { useSSRSafeId } from "@react-aria/ssr";
|
|
5
|
-
import _react, { useEffect, useRef, useState
|
|
6
|
+
import _react, { useCallback, useEffect, useRef, useState } from "react";
|
|
6
7
|
// During SSR, React emits a warning when calling useLayoutEffect.
|
|
7
8
|
// Since neither useLayoutEffect nor useEffect run on the server,
|
|
8
9
|
// we can suppress this by replace it with a noop on the server.
|
|
@@ -17,7 +18,8 @@ export function useId(defaultId) {
|
|
|
17
18
|
let isRendering = useRef(true);
|
|
18
19
|
isRendering.current = true;
|
|
19
20
|
let [value, setValue] = useState(defaultId);
|
|
20
|
-
let nextId = useRef(null);
|
|
21
|
+
let nextId = useRef(null);
|
|
22
|
+
let res = useSSRSafeId(value); // don't memo this, we want it new each render so that the Effects always run
|
|
21
23
|
|
|
22
24
|
let updateValue = val => {
|
|
23
25
|
if (!isRendering.current) {
|
|
@@ -27,9 +29,16 @@ export function useId(defaultId) {
|
|
|
27
29
|
}
|
|
28
30
|
};
|
|
29
31
|
|
|
32
|
+
$f8b5fdd96fb429d7102983f777c41307$var$idsUpdaterMap.set(res, updateValue);
|
|
30
33
|
useLayoutEffect(() => {
|
|
31
34
|
isRendering.current = false;
|
|
32
35
|
}, [updateValue]);
|
|
36
|
+
useLayoutEffect(() => {
|
|
37
|
+
let r = res;
|
|
38
|
+
return () => {
|
|
39
|
+
$f8b5fdd96fb429d7102983f777c41307$var$idsUpdaterMap.delete(r);
|
|
40
|
+
};
|
|
41
|
+
}, [res]);
|
|
33
42
|
useEffect(() => {
|
|
34
43
|
let newId = nextId.current;
|
|
35
44
|
|
|
@@ -38,8 +47,6 @@ export function useId(defaultId) {
|
|
|
38
47
|
nextId.current = null;
|
|
39
48
|
}
|
|
40
49
|
}, [setValue, updateValue]);
|
|
41
|
-
let res = useSSRSafeId(value);
|
|
42
|
-
$f8b5fdd96fb429d7102983f777c41307$var$idsUpdaterMap.set(res, updateValue);
|
|
43
50
|
return res;
|
|
44
51
|
}
|
|
45
52
|
/**
|
|
@@ -71,18 +78,24 @@ export function mergeIds(idA, idB) {
|
|
|
71
78
|
/**
|
|
72
79
|
* Used to generate an id, and after render, check if that id is rendered so we know
|
|
73
80
|
* if we can use it in places such as labelledby.
|
|
81
|
+
* @param depArray - When to recalculate if the id is in the DOM.
|
|
74
82
|
*/
|
|
75
83
|
|
|
76
|
-
export function useSlotId() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
export function useSlotId(depArray) {
|
|
85
|
+
if (depArray === void 0) {
|
|
86
|
+
depArray = [];
|
|
87
|
+
}
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
let id = useId();
|
|
90
|
+
let [resolvedId, setResolvedId] = useValueEffect(id);
|
|
91
|
+
let updateId = useCallback(() => {
|
|
92
|
+
setResolvedId(function* () {
|
|
93
|
+
yield id;
|
|
94
|
+
yield document.getElementById(id) ? id : null;
|
|
95
|
+
});
|
|
96
|
+
}, [id, setResolvedId]);
|
|
97
|
+
useLayoutEffect(updateId, [id, updateId, ...depArray]);
|
|
98
|
+
return resolvedId;
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
/*
|
|
@@ -122,32 +135,30 @@ export function chain() {
|
|
|
122
135
|
* @param args - Multiple sets of props to merge together.
|
|
123
136
|
*/
|
|
124
137
|
export function mergeProps() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
args[_key] = arguments[_key];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
for (let props of args) {
|
|
132
|
-
for (let key in result) {
|
|
133
|
-
// Chain events
|
|
134
|
-
if (/^on[A-Z]/.test(key) && typeof result[key] === 'function' && typeof props[key] === 'function') {
|
|
135
|
-
result[key] = chain(result[key], props[key]); // Merge classnames, sometimes classNames are empty string which eval to false, so we just need to do a type check
|
|
136
|
-
} else if (key === 'className' && typeof result.className === 'string' && typeof props.className === 'string') {
|
|
137
|
-
result[key] = _clsx(result.className, props.className);
|
|
138
|
-
} else if (key === 'UNSAFE_className' && typeof result.UNSAFE_className === 'string' && typeof props.UNSAFE_className === 'string') {
|
|
139
|
-
result[key] = _clsx(result.UNSAFE_className, props.UNSAFE_className);
|
|
140
|
-
} else if (key === 'id' && result.id && props.id) {
|
|
141
|
-
result.id = mergeIds(result.id, props.id); // Override others
|
|
142
|
-
} else {
|
|
143
|
-
result[key] = props[key] !== undefined ? props[key] : result[key];
|
|
144
|
-
}
|
|
145
|
-
} // Add props from b that are not in a
|
|
138
|
+
// Start with a base clone of the first argument. This is a lot faster than starting
|
|
139
|
+
// with an empty object and adding properties as we go.
|
|
140
|
+
let result = _babelRuntimeHelpersEsmExtends({}, arguments.length <= 0 ? undefined : arguments[0]);
|
|
146
141
|
|
|
142
|
+
for (let i = 1; i < arguments.length; i++) {
|
|
143
|
+
let props = i < 0 || arguments.length <= i ? undefined : arguments[i];
|
|
147
144
|
|
|
148
145
|
for (let key in props) {
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
let a = result[key];
|
|
147
|
+
let b = props[key]; // Chain events
|
|
148
|
+
|
|
149
|
+
if (typeof a === 'function' && typeof b === 'function' && // This is a lot faster than a regex.
|
|
150
|
+
key[0] === 'o' && key[1] === 'n' && key.charCodeAt(2) >=
|
|
151
|
+
/* 'A' */
|
|
152
|
+
65 && key.charCodeAt(2) <=
|
|
153
|
+
/* 'Z' */
|
|
154
|
+
90) {
|
|
155
|
+
result[key] = chain(a, b); // Merge classnames, sometimes classNames are empty string which eval to false, so we just need to do a type check
|
|
156
|
+
} else if ((key === 'className' || key === 'UNSAFE_className') && typeof a === 'string' && typeof b === 'string') {
|
|
157
|
+
result[key] = _clsx(a, b);
|
|
158
|
+
} else if (key === 'id' && a && b) {
|
|
159
|
+
result.id = mergeIds(a, b); // Override others
|
|
160
|
+
} else {
|
|
161
|
+
result[key] = b !== undefined ? b : a;
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
164
|
}
|
|
@@ -175,7 +186,7 @@ export function filterDOMProps(props, opts) {
|
|
|
175
186
|
let filteredProps = {};
|
|
176
187
|
|
|
177
188
|
for (const prop in props) {
|
|
178
|
-
if (Object.prototype.hasOwnProperty.call(props, prop) && ($f6a965352cabf1a7c37e8c1337e5eab$var$DOMPropNames.has(prop) || labelable && $f6a965352cabf1a7c37e8c1337e5eab$var$labelablePropNames.has(prop) ||
|
|
189
|
+
if (Object.prototype.hasOwnProperty.call(props, prop) && ($f6a965352cabf1a7c37e8c1337e5eab$var$DOMPropNames.has(prop) || labelable && $f6a965352cabf1a7c37e8c1337e5eab$var$labelablePropNames.has(prop) || propNames != null && propNames.has(prop) || $f6a965352cabf1a7c37e8c1337e5eab$var$propRe.test(prop))) {
|
|
179
190
|
filteredProps[prop] = props[prop];
|
|
180
191
|
}
|
|
181
192
|
}
|
|
@@ -563,15 +574,24 @@ export function useDrag1D(props) {
|
|
|
563
574
|
export function useGlobalListeners() {
|
|
564
575
|
let globalListeners = useRef(new Map());
|
|
565
576
|
let addGlobalListener = useCallback((eventTarget, type, listener, options) => {
|
|
577
|
+
// Make sure we remove the listener after it is called with the `once` option.
|
|
578
|
+
let fn = options != null && options.once ? function () {
|
|
579
|
+
globalListeners.current.delete(listener);
|
|
580
|
+
listener(...arguments);
|
|
581
|
+
} : listener;
|
|
566
582
|
globalListeners.current.set(listener, {
|
|
567
583
|
type,
|
|
568
584
|
eventTarget,
|
|
585
|
+
fn,
|
|
569
586
|
options
|
|
570
587
|
});
|
|
571
588
|
eventTarget.addEventListener(type, listener, options);
|
|
572
589
|
}, []);
|
|
573
590
|
let removeGlobalListener = useCallback((eventTarget, type, listener, options) => {
|
|
574
|
-
|
|
591
|
+
var _globalListeners$curr;
|
|
592
|
+
|
|
593
|
+
let fn = ((_globalListeners$curr = globalListeners.current.get(listener)) == null ? void 0 : _globalListeners$curr.fn) || listener;
|
|
594
|
+
eventTarget.removeEventListener(type, fn, options);
|
|
575
595
|
globalListeners.current.delete(listener);
|
|
576
596
|
}, []);
|
|
577
597
|
let removeAllGlobalListeners = useCallback(() => {
|
|
@@ -623,6 +643,37 @@ export function useLabels(props, defaultLabel) {
|
|
|
623
643
|
'aria-labelledby': labelledBy
|
|
624
644
|
};
|
|
625
645
|
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Offers an object ref for a given callback ref or an object ref. Especially
|
|
649
|
+
* helfpul when passing forwarded refs (created using `React.forwardRef`) to
|
|
650
|
+
* React Aria Hooks.
|
|
651
|
+
*
|
|
652
|
+
* @param forwardedRef The original ref intended to be used.
|
|
653
|
+
* @returns An object ref that updates the given ref.
|
|
654
|
+
* @see https://reactjs.org/docs/forwarding-refs.html
|
|
655
|
+
*/
|
|
656
|
+
export function useObjectRef(forwardedRef) {
|
|
657
|
+
const objRef = useRef();
|
|
658
|
+
/**
|
|
659
|
+
* We're using `useLayoutEffect` here instead of `useEffect` because we want
|
|
660
|
+
* to make sure that the `ref` value is up to date before other places in the
|
|
661
|
+
* the execution cycle try to read it.
|
|
662
|
+
*/
|
|
663
|
+
|
|
664
|
+
useLayoutEffect(() => {
|
|
665
|
+
if (!forwardedRef) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (typeof forwardedRef === 'function') {
|
|
670
|
+
forwardedRef(objRef.current);
|
|
671
|
+
} else {
|
|
672
|
+
forwardedRef.current = objRef.current;
|
|
673
|
+
}
|
|
674
|
+
}, [forwardedRef]);
|
|
675
|
+
return objRef;
|
|
676
|
+
}
|
|
626
677
|
// Like useEffect, but only called for updates after the initial render.
|
|
627
678
|
export function useUpdateEffect(effect, dependencies) {
|
|
628
679
|
const isInitialMount = useRef(true);
|
|
@@ -677,7 +728,7 @@ export function useResizeObserver(options) {
|
|
|
677
728
|
// Syncs ref from context with ref passed to hook
|
|
678
729
|
export function useSyncRef(context, ref) {
|
|
679
730
|
useLayoutEffect(() => {
|
|
680
|
-
if (context && context.ref) {
|
|
731
|
+
if (context && context.ref && ref) {
|
|
681
732
|
context.ref.current = ref.current;
|
|
682
733
|
return () => {
|
|
683
734
|
context.ref.current = null;
|
|
@@ -705,7 +756,15 @@ export function useViewportSize() {
|
|
|
705
756
|
useEffect(() => {
|
|
706
757
|
// Use visualViewport api to track available height even on iOS virtual keyboard opening
|
|
707
758
|
let onResize = () => {
|
|
708
|
-
setSize(
|
|
759
|
+
setSize(size => {
|
|
760
|
+
let newSize = $d662329747d896105af008c761523$var$getViewportSize();
|
|
761
|
+
|
|
762
|
+
if (newSize.width === size.width && newSize.height === size.height) {
|
|
763
|
+
return size;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return newSize;
|
|
767
|
+
});
|
|
709
768
|
};
|
|
710
769
|
|
|
711
770
|
if (!$d662329747d896105af008c761523$var$visualViewport) {
|
|
@@ -736,8 +795,7 @@ let $c8aa524f123a75a64d51e06d16b9568$var$descriptionId = 0;
|
|
|
736
795
|
const $c8aa524f123a75a64d51e06d16b9568$var$descriptionNodes = new Map();
|
|
737
796
|
export function useDescription(description) {
|
|
738
797
|
let [id, setId] = useState(null);
|
|
739
|
-
|
|
740
|
-
_useLayoutEffect(() => {
|
|
798
|
+
useLayoutEffect(() => {
|
|
741
799
|
if (!description) {
|
|
742
800
|
return;
|
|
743
801
|
}
|
|
@@ -769,7 +827,6 @@ export function useDescription(description) {
|
|
|
769
827
|
}
|
|
770
828
|
};
|
|
771
829
|
}, [description]);
|
|
772
|
-
|
|
773
830
|
return {
|
|
774
831
|
'aria-describedby': description ? id : undefined
|
|
775
832
|
};
|
|
@@ -808,4 +865,67 @@ export function isChrome() {
|
|
|
808
865
|
export function isAndroid() {
|
|
809
866
|
return $b0986c1243f71db8e992f67117a1ed9$var$testUserAgent(/Android/);
|
|
810
867
|
}
|
|
868
|
+
export function useEvent(ref, event, handler, options) {
|
|
869
|
+
let handlerRef = useRef(handler);
|
|
870
|
+
handlerRef.current = handler;
|
|
871
|
+
let isDisabled = handler == null;
|
|
872
|
+
useEffect(() => {
|
|
873
|
+
if (isDisabled) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
let element = ref.current;
|
|
878
|
+
|
|
879
|
+
let handler = e => handlerRef.current.call(this, e);
|
|
880
|
+
|
|
881
|
+
element.addEventListener(event, handler, options);
|
|
882
|
+
return () => {
|
|
883
|
+
element.removeEventListener(event, handler, options);
|
|
884
|
+
};
|
|
885
|
+
}, [ref, event, options, isDisabled]);
|
|
886
|
+
}
|
|
887
|
+
// This hook works like `useState`, but when setting the value, you pass a generator function
|
|
888
|
+
// that can yield multiple values. Each yielded value updates the state and waits for the next
|
|
889
|
+
// layout effect, then continues the generator. This allows sequential updates to state to be
|
|
890
|
+
// written linearly.
|
|
891
|
+
export function useValueEffect(defaultValue) {
|
|
892
|
+
let [value, setValue] = useState(defaultValue);
|
|
893
|
+
let valueRef = useRef(value);
|
|
894
|
+
let effect = useRef(null);
|
|
895
|
+
valueRef.current = value; // Store the function in a ref so we can always access the current version
|
|
896
|
+
// which has the proper `value` in scope.
|
|
897
|
+
|
|
898
|
+
let nextRef = useRef(null);
|
|
899
|
+
|
|
900
|
+
nextRef.current = () => {
|
|
901
|
+
// Run the generator to the next yield.
|
|
902
|
+
let newValue = effect.current.next(); // If the generator is done, reset the effect.
|
|
903
|
+
|
|
904
|
+
if (newValue.done) {
|
|
905
|
+
effect.current = null;
|
|
906
|
+
return;
|
|
907
|
+
} // If the value is the same as the current value,
|
|
908
|
+
// then continue to the next yield. Otherwise,
|
|
909
|
+
// set the value in state and wait for the next layout effect.
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
if (value === newValue.value) {
|
|
913
|
+
nextRef.current();
|
|
914
|
+
} else {
|
|
915
|
+
setValue(newValue.value);
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
useLayoutEffect(() => {
|
|
920
|
+
// If there is an effect currently running, continue to the next yield.
|
|
921
|
+
if (effect.current) {
|
|
922
|
+
nextRef.current();
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
let queue = useCallback(fn => {
|
|
926
|
+
effect.current = fn(valueRef.current);
|
|
927
|
+
nextRef.current();
|
|
928
|
+
}, [effect, nextRef]);
|
|
929
|
+
return [value, queue];
|
|
930
|
+
}
|
|
811
931
|
//# sourceMappingURL=module.js.map
|