@react-aria/utils 3.8.1 → 3.11.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 +219 -42
- package/dist/main.js.map +1 -1
- package/dist/module.js +205 -40
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +22 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +4 -0
- package/src/mergeProps.ts +24 -27
- package/src/scrollIntoView.ts +65 -0
- 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/useValueEffect.ts +65 -0
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
|
}
|
|
@@ -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);
|
|
@@ -744,8 +795,7 @@ let $c8aa524f123a75a64d51e06d16b9568$var$descriptionId = 0;
|
|
|
744
795
|
const $c8aa524f123a75a64d51e06d16b9568$var$descriptionNodes = new Map();
|
|
745
796
|
export function useDescription(description) {
|
|
746
797
|
let [id, setId] = useState(null);
|
|
747
|
-
|
|
748
|
-
_useLayoutEffect(() => {
|
|
798
|
+
useLayoutEffect(() => {
|
|
749
799
|
if (!description) {
|
|
750
800
|
return;
|
|
751
801
|
}
|
|
@@ -777,7 +827,6 @@ export function useDescription(description) {
|
|
|
777
827
|
}
|
|
778
828
|
};
|
|
779
829
|
}, [description]);
|
|
780
|
-
|
|
781
830
|
return {
|
|
782
831
|
'aria-describedby': description ? id : undefined
|
|
783
832
|
};
|
|
@@ -816,4 +865,120 @@ export function isChrome() {
|
|
|
816
865
|
export function isAndroid() {
|
|
817
866
|
return $b0986c1243f71db8e992f67117a1ed9$var$testUserAgent(/Android/);
|
|
818
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
|
+
}
|
|
931
|
+
export function scrollIntoView(scrollView, element) {
|
|
932
|
+
let offsetX = $ee7b96dbd8c9e13f6bb9633061c3e9d$var$relativeOffset(scrollView, element, 'left');
|
|
933
|
+
let offsetY = $ee7b96dbd8c9e13f6bb9633061c3e9d$var$relativeOffset(scrollView, element, 'top');
|
|
934
|
+
let width = element.offsetWidth;
|
|
935
|
+
let height = element.offsetHeight;
|
|
936
|
+
let x = scrollView.scrollLeft;
|
|
937
|
+
let y = scrollView.scrollTop;
|
|
938
|
+
let maxX = x + scrollView.offsetWidth;
|
|
939
|
+
let maxY = y + scrollView.offsetHeight;
|
|
940
|
+
|
|
941
|
+
if (offsetX <= x) {
|
|
942
|
+
x = offsetX;
|
|
943
|
+
} else if (offsetX + width > maxX) {
|
|
944
|
+
x += offsetX + width - maxX;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
if (offsetY <= y) {
|
|
948
|
+
y = offsetY;
|
|
949
|
+
} else if (offsetY + height > maxY) {
|
|
950
|
+
y += offsetY + height - maxY;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
scrollView.scrollLeft = x;
|
|
954
|
+
scrollView.scrollTop = y;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Computes the offset left or top from child to ancestor by accumulating
|
|
958
|
+
* offsetLeft or offsetTop through intervening offsetParents.
|
|
959
|
+
*/
|
|
960
|
+
|
|
961
|
+
function $ee7b96dbd8c9e13f6bb9633061c3e9d$var$relativeOffset(ancestor, child, axis) {
|
|
962
|
+
const prop = axis === 'left' ? 'offsetLeft' : 'offsetTop';
|
|
963
|
+
let sum = 0;
|
|
964
|
+
|
|
965
|
+
while (child.offsetParent) {
|
|
966
|
+
sum += child[prop];
|
|
967
|
+
|
|
968
|
+
if (child.offsetParent === ancestor) {
|
|
969
|
+
// Stop once we have found the ancestor we are interested in.
|
|
970
|
+
break;
|
|
971
|
+
} else if (child.offsetParent.contains(ancestor)) {
|
|
972
|
+
// If the ancestor is not `position:relative`, then we stop at
|
|
973
|
+
// _its_ offset parent, and we subtract off _its_ offset, so that
|
|
974
|
+
// we end up with the proper offset from child to ancestor.
|
|
975
|
+
sum -= ancestor[prop];
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
child = child.offsetParent;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
return sum;
|
|
983
|
+
}
|
|
819
984
|
//# sourceMappingURL=module.js.map
|