@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 CHANGED
@@ -8,6 +8,8 @@ exports.clamp = clamp;
8
8
 
9
9
  var _clsx = $parcel$interopDefault(require("clsx"));
10
10
 
11
+ var _babelRuntimeHelpersExtends = $parcel$interopDefault(require("@babel/runtime/helpers/extends"));
12
+
11
13
  var {
12
14
  useSSRSafeId
13
15
  } = require("@react-aria/ssr");
@@ -17,11 +19,10 @@ var _react2 = require("react");
17
19
  var _react = $parcel$interopDefault(_react2);
18
20
 
19
21
  var {
22
+ useCallback,
20
23
  useEffect,
21
24
  useRef,
22
- useState,
23
- useCallback,
24
- useLayoutEffect: _useLayoutEffect
25
+ useState
25
26
  } = _react2;
26
27
 
27
28
  function $parcel$interopDefault(a) {
@@ -43,7 +44,8 @@ function useId(defaultId) {
43
44
  let isRendering = useRef(true);
44
45
  isRendering.current = true;
45
46
  let [value, setValue] = useState(defaultId);
46
- let nextId = useRef(null); // don't memo this, we want it new each render so that the Effects always run
47
+ let nextId = useRef(null);
48
+ let res = useSSRSafeId(value); // don't memo this, we want it new each render so that the Effects always run
47
49
 
48
50
  let updateValue = val => {
49
51
  if (!isRendering.current) {
@@ -53,9 +55,16 @@ function useId(defaultId) {
53
55
  }
54
56
  };
55
57
 
58
+ $f09fcd7f5f367fc80aacfeac62ed2$var$idsUpdaterMap.set(res, updateValue);
56
59
  useLayoutEffect(() => {
57
60
  isRendering.current = false;
58
61
  }, [updateValue]);
62
+ useLayoutEffect(() => {
63
+ let r = res;
64
+ return () => {
65
+ $f09fcd7f5f367fc80aacfeac62ed2$var$idsUpdaterMap.delete(r);
66
+ };
67
+ }, [res]);
59
68
  useEffect(() => {
60
69
  let newId = nextId.current;
61
70
 
@@ -64,8 +73,6 @@ function useId(defaultId) {
64
73
  nextId.current = null;
65
74
  }
66
75
  }, [setValue, updateValue]);
67
- let res = useSSRSafeId(value);
68
- $f09fcd7f5f367fc80aacfeac62ed2$var$idsUpdaterMap.set(res, updateValue);
69
76
  return res;
70
77
  }
71
78
  /**
@@ -100,21 +107,27 @@ function mergeIds(idA, idB) {
100
107
  /**
101
108
  * Used to generate an id, and after render, check if that id is rendered so we know
102
109
  * if we can use it in places such as labelledby.
110
+ * @param depArray - When to recalculate if the id is in the DOM.
103
111
  */
104
112
 
105
113
 
106
114
  exports.mergeIds = mergeIds;
107
115
 
108
- function useSlotId() {
109
- let [id, setId] = useState(useId());
110
- useLayoutEffect(() => {
111
- let setCurr = $f09fcd7f5f367fc80aacfeac62ed2$var$idsUpdaterMap.get(id);
116
+ function useSlotId(depArray) {
117
+ if (depArray === void 0) {
118
+ depArray = [];
119
+ }
112
120
 
113
- if (setCurr && !document.getElementById(id)) {
114
- setId(null);
115
- }
116
- }, [id]);
117
- return id;
121
+ let id = useId();
122
+ let [resolvedId, setResolvedId] = useValueEffect(id);
123
+ let updateId = useCallback(() => {
124
+ setResolvedId(function* () {
125
+ yield id;
126
+ yield document.getElementById(id) ? id : null;
127
+ });
128
+ }, [id, setResolvedId]);
129
+ useLayoutEffect(updateId, [id, updateId, ...depArray]);
130
+ return resolvedId;
118
131
  }
119
132
 
120
133
  exports.useSlotId = useSlotId;
@@ -158,32 +171,30 @@ exports.chain = chain;
158
171
  * @param args - Multiple sets of props to merge together.
159
172
  */
160
173
  function mergeProps() {
161
- let result = {};
162
-
163
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
164
- args[_key] = arguments[_key];
165
- }
166
-
167
- for (let props of args) {
168
- for (let key in result) {
169
- // Chain events
170
- if (/^on[A-Z]/.test(key) && typeof result[key] === 'function' && typeof props[key] === 'function') {
171
- 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
172
- } else if (key === 'className' && typeof result.className === 'string' && typeof props.className === 'string') {
173
- result[key] = _clsx(result.className, props.className);
174
- } else if (key === 'UNSAFE_className' && typeof result.UNSAFE_className === 'string' && typeof props.UNSAFE_className === 'string') {
175
- result[key] = _clsx(result.UNSAFE_className, props.UNSAFE_className);
176
- } else if (key === 'id' && result.id && props.id) {
177
- result.id = mergeIds(result.id, props.id); // Override others
178
- } else {
179
- result[key] = props[key] !== undefined ? props[key] : result[key];
180
- }
181
- } // Add props from b that are not in a
174
+ // Start with a base clone of the first argument. This is a lot faster than starting
175
+ // with an empty object and adding properties as we go.
176
+ let result = _babelRuntimeHelpersExtends({}, arguments.length <= 0 ? undefined : arguments[0]);
182
177
 
178
+ for (let i = 1; i < arguments.length; i++) {
179
+ let props = i < 0 || arguments.length <= i ? undefined : arguments[i];
183
180
 
184
181
  for (let key in props) {
185
- if (result[key] === undefined) {
186
- result[key] = props[key];
182
+ let a = result[key];
183
+ let b = props[key]; // Chain events
184
+
185
+ if (typeof a === 'function' && typeof b === 'function' && // This is a lot faster than a regex.
186
+ key[0] === 'o' && key[1] === 'n' && key.charCodeAt(2) >=
187
+ /* 'A' */
188
+ 65 && key.charCodeAt(2) <=
189
+ /* 'Z' */
190
+ 90) {
191
+ 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
192
+ } else if ((key === 'className' || key === 'UNSAFE_className') && typeof a === 'string' && typeof b === 'string') {
193
+ result[key] = _clsx(a, b);
194
+ } else if (key === 'id' && a && b) {
195
+ result.id = mergeIds(a, b); // Override others
196
+ } else {
197
+ result[key] = b !== undefined ? b : a;
187
198
  }
188
199
  }
189
200
  }
@@ -613,15 +624,24 @@ exports.useDrag1D = useDrag1D;
613
624
  function useGlobalListeners() {
614
625
  let globalListeners = useRef(new Map());
615
626
  let addGlobalListener = useCallback((eventTarget, type, listener, options) => {
627
+ // Make sure we remove the listener after it is called with the `once` option.
628
+ let fn = options != null && options.once ? function () {
629
+ globalListeners.current.delete(listener);
630
+ listener(...arguments);
631
+ } : listener;
616
632
  globalListeners.current.set(listener, {
617
633
  type,
618
634
  eventTarget,
635
+ fn,
619
636
  options
620
637
  });
621
638
  eventTarget.addEventListener(type, listener, options);
622
639
  }, []);
623
640
  let removeGlobalListener = useCallback((eventTarget, type, listener, options) => {
624
- eventTarget.removeEventListener(type, listener, options);
641
+ var _globalListeners$curr;
642
+
643
+ let fn = ((_globalListeners$curr = globalListeners.current.get(listener)) == null ? void 0 : _globalListeners$curr.fn) || listener;
644
+ eventTarget.removeEventListener(type, fn, options);
625
645
  globalListeners.current.delete(listener);
626
646
  }, []);
627
647
  let removeAllGlobalListeners = useCallback(() => {
@@ -678,6 +698,39 @@ function useLabels(props, defaultLabel) {
678
698
 
679
699
  exports.useLabels = useLabels;
680
700
 
701
+ /**
702
+ * Offers an object ref for a given callback ref or an object ref. Especially
703
+ * helfpul when passing forwarded refs (created using `React.forwardRef`) to
704
+ * React Aria Hooks.
705
+ *
706
+ * @param forwardedRef The original ref intended to be used.
707
+ * @returns An object ref that updates the given ref.
708
+ * @see https://reactjs.org/docs/forwarding-refs.html
709
+ */
710
+ function useObjectRef(forwardedRef) {
711
+ const objRef = useRef();
712
+ /**
713
+ * We're using `useLayoutEffect` here instead of `useEffect` because we want
714
+ * to make sure that the `ref` value is up to date before other places in the
715
+ * the execution cycle try to read it.
716
+ */
717
+
718
+ useLayoutEffect(() => {
719
+ if (!forwardedRef) {
720
+ return;
721
+ }
722
+
723
+ if (typeof forwardedRef === 'function') {
724
+ forwardedRef(objRef.current);
725
+ } else {
726
+ forwardedRef.current = objRef.current;
727
+ }
728
+ }, [forwardedRef]);
729
+ return objRef;
730
+ }
731
+
732
+ exports.useObjectRef = useObjectRef;
733
+
681
734
  // Like useEffect, but only called for updates after the initial render.
682
735
  function useUpdateEffect(effect, dependencies) {
683
736
  const isInitialMount = useRef(true);
@@ -813,8 +866,7 @@ const $bd5928122fc632cc7302c36df9f$var$descriptionNodes = new Map();
813
866
 
814
867
  function useDescription(description) {
815
868
  let [id, setId] = useState(null);
816
-
817
- _useLayoutEffect(() => {
869
+ useLayoutEffect(() => {
818
870
  if (!description) {
819
871
  return;
820
872
  }
@@ -846,7 +898,6 @@ function useDescription(description) {
846
898
  }
847
899
  };
848
900
  }, [description]);
849
-
850
901
  return {
851
902
  'aria-describedby': description ? id : undefined
852
903
  };
@@ -910,4 +961,130 @@ function isAndroid() {
910
961
  }
911
962
 
912
963
  exports.isAndroid = isAndroid;
964
+
965
+ function useEvent(ref, event, handler, options) {
966
+ let handlerRef = useRef(handler);
967
+ handlerRef.current = handler;
968
+ let isDisabled = handler == null;
969
+ useEffect(() => {
970
+ if (isDisabled) {
971
+ return;
972
+ }
973
+
974
+ let element = ref.current;
975
+
976
+ let handler = e => handlerRef.current.call(this, e);
977
+
978
+ element.addEventListener(event, handler, options);
979
+ return () => {
980
+ element.removeEventListener(event, handler, options);
981
+ };
982
+ }, [ref, event, options, isDisabled]);
983
+ }
984
+
985
+ exports.useEvent = useEvent;
986
+
987
+ // This hook works like `useState`, but when setting the value, you pass a generator function
988
+ // that can yield multiple values. Each yielded value updates the state and waits for the next
989
+ // layout effect, then continues the generator. This allows sequential updates to state to be
990
+ // written linearly.
991
+ function useValueEffect(defaultValue) {
992
+ let [value, setValue] = useState(defaultValue);
993
+ let valueRef = useRef(value);
994
+ let effect = useRef(null);
995
+ valueRef.current = value; // Store the function in a ref so we can always access the current version
996
+ // which has the proper `value` in scope.
997
+
998
+ let nextRef = useRef(null);
999
+
1000
+ nextRef.current = () => {
1001
+ // Run the generator to the next yield.
1002
+ let newValue = effect.current.next(); // If the generator is done, reset the effect.
1003
+
1004
+ if (newValue.done) {
1005
+ effect.current = null;
1006
+ return;
1007
+ } // If the value is the same as the current value,
1008
+ // then continue to the next yield. Otherwise,
1009
+ // set the value in state and wait for the next layout effect.
1010
+
1011
+
1012
+ if (value === newValue.value) {
1013
+ nextRef.current();
1014
+ } else {
1015
+ setValue(newValue.value);
1016
+ }
1017
+ };
1018
+
1019
+ useLayoutEffect(() => {
1020
+ // If there is an effect currently running, continue to the next yield.
1021
+ if (effect.current) {
1022
+ nextRef.current();
1023
+ }
1024
+ });
1025
+ let queue = useCallback(fn => {
1026
+ effect.current = fn(valueRef.current);
1027
+ nextRef.current();
1028
+ }, [effect, nextRef]);
1029
+ return [value, queue];
1030
+ }
1031
+
1032
+ exports.useValueEffect = useValueEffect;
1033
+
1034
+ function scrollIntoView(scrollView, element) {
1035
+ let offsetX = $cbd55f9ce27bf074ec65b15e6f24531$var$relativeOffset(scrollView, element, 'left');
1036
+ let offsetY = $cbd55f9ce27bf074ec65b15e6f24531$var$relativeOffset(scrollView, element, 'top');
1037
+ let width = element.offsetWidth;
1038
+ let height = element.offsetHeight;
1039
+ let x = scrollView.scrollLeft;
1040
+ let y = scrollView.scrollTop;
1041
+ let maxX = x + scrollView.offsetWidth;
1042
+ let maxY = y + scrollView.offsetHeight;
1043
+
1044
+ if (offsetX <= x) {
1045
+ x = offsetX;
1046
+ } else if (offsetX + width > maxX) {
1047
+ x += offsetX + width - maxX;
1048
+ }
1049
+
1050
+ if (offsetY <= y) {
1051
+ y = offsetY;
1052
+ } else if (offsetY + height > maxY) {
1053
+ y += offsetY + height - maxY;
1054
+ }
1055
+
1056
+ scrollView.scrollLeft = x;
1057
+ scrollView.scrollTop = y;
1058
+ }
1059
+ /**
1060
+ * Computes the offset left or top from child to ancestor by accumulating
1061
+ * offsetLeft or offsetTop through intervening offsetParents.
1062
+ */
1063
+
1064
+
1065
+ exports.scrollIntoView = scrollIntoView;
1066
+
1067
+ function $cbd55f9ce27bf074ec65b15e6f24531$var$relativeOffset(ancestor, child, axis) {
1068
+ const prop = axis === 'left' ? 'offsetLeft' : 'offsetTop';
1069
+ let sum = 0;
1070
+
1071
+ while (child.offsetParent) {
1072
+ sum += child[prop];
1073
+
1074
+ if (child.offsetParent === ancestor) {
1075
+ // Stop once we have found the ancestor we are interested in.
1076
+ break;
1077
+ } else if (child.offsetParent.contains(ancestor)) {
1078
+ // If the ancestor is not `position:relative`, then we stop at
1079
+ // _its_ offset parent, and we subtract off _its_ offset, so that
1080
+ // we end up with the proper offset from child to ancestor.
1081
+ sum -= ancestor[prop];
1082
+ break;
1083
+ }
1084
+
1085
+ child = child.offsetParent;
1086
+ }
1087
+
1088
+ return sum;
1089
+ }
913
1090
  //# sourceMappingURL=main.js.map