@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 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
  }
@@ -213,7 +224,7 @@ function filterDOMProps(props, opts) {
213
224
  let filteredProps = {};
214
225
 
215
226
  for (const prop in props) {
216
- if (Object.prototype.hasOwnProperty.call(props, prop) && ($a736ffc3e05a0bfc1508098ba395b41$var$DOMPropNames.has(prop) || labelable && $a736ffc3e05a0bfc1508098ba395b41$var$labelablePropNames.has(prop) || (propNames == null ? void 0 : propNames.has(prop)) || $a736ffc3e05a0bfc1508098ba395b41$var$propRe.test(prop))) {
227
+ if (Object.prototype.hasOwnProperty.call(props, prop) && ($a736ffc3e05a0bfc1508098ba395b41$var$DOMPropNames.has(prop) || labelable && $a736ffc3e05a0bfc1508098ba395b41$var$labelablePropNames.has(prop) || propNames != null && propNames.has(prop) || $a736ffc3e05a0bfc1508098ba395b41$var$propRe.test(prop))) {
217
228
  filteredProps[prop] = props[prop];
218
229
  }
219
230
  }
@@ -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);
@@ -737,7 +790,7 @@ exports.useResizeObserver = useResizeObserver;
737
790
  // Syncs ref from context with ref passed to hook
738
791
  function useSyncRef(context, ref) {
739
792
  useLayoutEffect(() => {
740
- if (context && context.ref) {
793
+ if (context && context.ref && ref) {
741
794
  context.ref.current = ref.current;
742
795
  return () => {
743
796
  context.ref.current = null;
@@ -771,7 +824,15 @@ function useViewportSize() {
771
824
  useEffect(() => {
772
825
  // Use visualViewport api to track available height even on iOS virtual keyboard opening
773
826
  let onResize = () => {
774
- setSize($f1a92c0e19f2e1ad09851454bf93009$var$getViewportSize());
827
+ setSize(size => {
828
+ let newSize = $f1a92c0e19f2e1ad09851454bf93009$var$getViewportSize();
829
+
830
+ if (newSize.width === size.width && newSize.height === size.height) {
831
+ return size;
832
+ }
833
+
834
+ return newSize;
835
+ });
775
836
  };
776
837
 
777
838
  if (!$f1a92c0e19f2e1ad09851454bf93009$var$visualViewport) {
@@ -805,8 +866,7 @@ const $bd5928122fc632cc7302c36df9f$var$descriptionNodes = new Map();
805
866
 
806
867
  function useDescription(description) {
807
868
  let [id, setId] = useState(null);
808
-
809
- _useLayoutEffect(() => {
869
+ useLayoutEffect(() => {
810
870
  if (!description) {
811
871
  return;
812
872
  }
@@ -838,7 +898,6 @@ function useDescription(description) {
838
898
  }
839
899
  };
840
900
  }, [description]);
841
-
842
901
  return {
843
902
  'aria-describedby': description ? id : undefined
844
903
  };
@@ -902,4 +961,73 @@ function isAndroid() {
902
961
  }
903
962
 
904
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;
905
1033
  //# sourceMappingURL=main.js.map