@react-aria/color 3.0.0-beta.21 → 3.0.0-beta.23

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.
@@ -13,10 +13,10 @@
13
13
  import {AriaColorAreaProps, ColorChannel} from '@react-types/color';
14
14
  import {ColorAreaState} from '@react-stately/color';
15
15
  import {DOMAttributes} from '@react-types/shared';
16
- import {focusWithoutScrolling, isAndroid, isIOS, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils';
16
+ import {focusWithoutScrolling, isAndroid, isIOS, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
17
17
  // @ts-ignore
18
18
  import intlMessages from '../intl/*.json';
19
- import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react';
19
+ import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef, useState} from 'react';
20
20
  import {useColorAreaGradient} from './useColorAreaGradient';
21
21
  import {useFocus, useFocusWithin, useKeyboard, useMove} from '@react-aria/interactions';
22
22
  import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
@@ -54,7 +54,9 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
54
54
  inputXRef,
55
55
  inputYRef,
56
56
  containerRef,
57
- 'aria-label': ariaLabel
57
+ 'aria-label': ariaLabel,
58
+ xName,
59
+ yName
58
60
  } = props;
59
61
  let stringFormatter = useLocalizedStringFormatter(intlMessages);
60
62
 
@@ -62,19 +64,24 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
62
64
 
63
65
  let {direction, locale} = useLocale();
64
66
 
65
- let focusedInputRef = useRef<HTMLInputElement>(null);
66
-
67
+ let [focusedInput, setFocusedInput] = useState<'x' | 'y' | null>(null);
67
68
  let focusInput = useCallback((inputRef:RefObject<HTMLInputElement> = inputXRef) => {
68
69
  if (inputRef.current) {
69
70
  focusWithoutScrolling(inputRef.current);
70
71
  }
71
72
  }, [inputXRef]);
72
73
 
73
- let stateRef = useRef<ColorAreaState>(null);
74
- stateRef.current = state;
75
- let {xChannel, yChannel, zChannel} = stateRef.current.channels;
76
- let xChannelStep = stateRef.current.xChannelStep;
77
- let yChannelStep = stateRef.current.yChannelStep;
74
+ useFormReset(inputXRef, [state.xValue, state.yValue], ([x, y]) => {
75
+ let newColor = state.value
76
+ .withChannelValue(state.channels.xChannel, x)
77
+ .withChannelValue(state.channels.yChannel, y);
78
+ state.setValue(newColor);
79
+ });
80
+
81
+ let [valueChangedViaKeyboard, setValueChangedViaKeyboard] = useState(false);
82
+ let {xChannel, yChannel, zChannel} = state.channels;
83
+ let xChannelStep = state.xChannelStep;
84
+ let yChannelStep = state.yChannelStep;
78
85
 
79
86
  let currentPosition = useRef<{x: number, y: number}>(null);
80
87
 
@@ -88,29 +95,32 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
88
95
  // same handling as useMove, don't need to stop propagation, useKeyboard will do that for us
89
96
  e.preventDefault();
90
97
  // remember to set this and unset it so that onChangeEnd is fired
91
- stateRef.current.setDragging(true);
92
- valueChangedViaKeyboard.current = true;
98
+ state.setDragging(true);
99
+ setValueChangedViaKeyboard(true);
100
+ let dir;
93
101
  switch (e.key) {
94
102
  case 'PageUp':
95
- stateRef.current.incrementY(stateRef.current.yChannelPageStep);
96
- focusedInputRef.current = inputYRef.current;
103
+ state.incrementY(state.yChannelPageStep);
104
+ dir = 'y';
97
105
  break;
98
106
  case 'PageDown':
99
- stateRef.current.decrementY(stateRef.current.yChannelPageStep);
100
- focusedInputRef.current = inputYRef.current;
107
+ state.decrementY(state.yChannelPageStep);
108
+ dir = 'y';
101
109
  break;
102
110
  case 'Home':
103
- direction === 'rtl' ? stateRef.current.incrementX(stateRef.current.xChannelPageStep) : stateRef.current.decrementX(stateRef.current.xChannelPageStep);
104
- focusedInputRef.current = inputXRef.current;
111
+ direction === 'rtl' ? state.incrementX(state.xChannelPageStep) : state.decrementX(state.xChannelPageStep);
112
+ dir = 'x';
105
113
  break;
106
114
  case 'End':
107
- direction === 'rtl' ? stateRef.current.decrementX(stateRef.current.xChannelPageStep) : stateRef.current.incrementX(stateRef.current.xChannelPageStep);
108
- focusedInputRef.current = inputXRef.current;
115
+ direction === 'rtl' ? state.decrementX(state.xChannelPageStep) : state.incrementX(state.xChannelPageStep);
116
+ dir = 'x';
109
117
  break;
110
118
  }
111
- stateRef.current.setDragging(false);
112
- if (focusedInputRef.current) {
113
- focusInput(focusedInputRef.current ? focusedInputRef : inputXRef);
119
+ state.setDragging(false);
120
+ if (dir) {
121
+ let input = dir === 'x' ? inputXRef : inputYRef;
122
+ focusInput(input);
123
+ setFocusedInput(dir);
114
124
  }
115
125
  }
116
126
  });
@@ -118,7 +128,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
118
128
  let moveHandler = {
119
129
  onMoveStart() {
120
130
  currentPosition.current = null;
121
- stateRef.current.setDragging(true);
131
+ state.setDragging(true);
122
132
  },
123
133
  onMove({deltaX, deltaY, pointerType, shiftKey}) {
124
134
  let {
@@ -132,7 +142,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
132
142
  yChannelStep,
133
143
  getThumbPosition,
134
144
  setColorFromPoint
135
- } = stateRef.current;
145
+ } = state;
136
146
  if (currentPosition.current == null) {
137
147
  currentPosition.current = getThumbPosition();
138
148
  }
@@ -150,9 +160,10 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
150
160
  } else if (deltaY < 0) {
151
161
  incrementY(deltaYValue);
152
162
  }
153
- valueChangedViaKeyboard.current = valueChanged;
163
+ setValueChangedViaKeyboard(valueChanged);
154
164
  // set the focused input based on which axis has the greater delta
155
- focusedInputRef.current = valueChanged && Math.abs(deltaY) > Math.abs(deltaX) ? inputYRef.current : inputXRef.current;
165
+ focusedInput = valueChanged && Math.abs(deltaY) > Math.abs(deltaX) ? 'y' : 'x';
166
+ setFocusedInput(focusedInput);
156
167
  } else {
157
168
  currentPosition.current.x += (direction === 'rtl' ? -1 : 1) * deltaX / width ;
158
169
  currentPosition.current.y += deltaY / height;
@@ -161,18 +172,17 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
161
172
  },
162
173
  onMoveEnd() {
163
174
  isOnColorArea.current = undefined;
164
- stateRef.current.setDragging(false);
165
- focusInput(focusedInputRef.current ? focusedInputRef : inputXRef);
175
+ state.setDragging(false);
176
+ let input = focusedInput === 'x' ? inputXRef : inputYRef;
177
+ focusInput(input);
166
178
  }
167
179
  };
168
180
  let {moveProps: movePropsThumb} = useMove(moveHandler);
169
181
 
170
- let valueChangedViaKeyboard = useRef<boolean>(false);
171
182
  let {focusWithinProps} = useFocusWithin({
172
183
  onFocusWithinChange: (focusWithin:boolean) => {
173
184
  if (!focusWithin) {
174
- valueChangedViaKeyboard.current = false;
175
- focusedInputRef.current === undefined;
185
+ setValueChangedViaKeyboard(false);
176
186
  }
177
187
  }
178
188
  });
@@ -200,7 +210,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
200
210
  let onThumbDown = (id: number | null) => {
201
211
  if (!state.isDragging) {
202
212
  currentPointer.current = id;
203
- valueChangedViaKeyboard.current = false;
213
+ setValueChangedViaKeyboard(false);
204
214
  focusInput();
205
215
  state.setDragging(true);
206
216
  if (typeof PointerEvent !== 'undefined') {
@@ -215,7 +225,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
215
225
  let onThumbUp = (e) => {
216
226
  let id = e.pointerId ?? e.changedTouches?.[0].identifier;
217
227
  if (id === currentPointer.current) {
218
- valueChangedViaKeyboard.current = false;
228
+ setValueChangedViaKeyboard(false);
219
229
  focusInput();
220
230
  state.setDragging(false);
221
231
  currentPointer.current = undefined;
@@ -240,7 +250,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
240
250
  }
241
251
  if (x >= 0 && x <= 1 && y >= 0 && y <= 1 && !state.isDragging && currentPointer.current === undefined) {
242
252
  isOnColorArea.current = true;
243
- valueChangedViaKeyboard.current = false;
253
+ setValueChangedViaKeyboard(false);
244
254
  currentPointer.current = id;
245
255
  state.setColorFromPoint(x, y);
246
256
 
@@ -260,7 +270,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
260
270
  let id = e.pointerId ?? e.changedTouches?.[0].identifier;
261
271
  if (isOnColorArea.current && id === currentPointer.current) {
262
272
  isOnColorArea.current = false;
263
- valueChangedViaKeyboard.current = false;
273
+ setValueChangedViaKeyboard(false);
264
274
  currentPointer.current = undefined;
265
275
  state.setDragging(false);
266
276
  focusInput();
@@ -316,13 +326,13 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
316
326
 
317
327
  let {focusProps: xInputFocusProps} = useFocus({
318
328
  onFocus: () => {
319
- focusedInputRef.current = inputXRef.current;
329
+ setFocusedInput('x');
320
330
  }
321
331
  });
322
332
 
323
333
  let {focusProps: yInputFocusProps} = useFocus({
324
334
  onFocus: () => {
325
- focusedInputRef.current = inputYRef.current;
335
+ setFocusedInput('y');
326
336
  }
327
337
  });
328
338
 
@@ -330,7 +340,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
330
340
 
331
341
  function getAriaValueTextForChannel(channel:ColorChannel) {
332
342
  return (
333
- valueChangedViaKeyboard.current ?
343
+ valueChangedViaKeyboard ?
334
344
  stringFormatter.format('colorNameAndValue', {name: state.value.getChannelName(channel, locale), value: state.value.formatChannelValue(channel, locale)})
335
345
  :
336
346
  [
@@ -409,13 +419,14 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
409
419
  'aria-valuetext': getAriaValueTextForChannel(xChannel),
410
420
  disabled: isDisabled,
411
421
  value: state.value.getChannelValue(xChannel),
412
- tabIndex: (isMobile || !focusedInputRef.current || focusedInputRef.current === inputXRef.current ? undefined : -1),
422
+ name: xName,
423
+ tabIndex: (isMobile || !focusedInput || focusedInput === 'x' ? undefined : -1),
413
424
  /*
414
425
  So that only a single "2d slider" control shows up when listing form elements for screen readers,
415
426
  add aria-hidden="true" to the unfocused control when the value has not changed via the keyboard,
416
427
  but remove aria-hidden to reveal the input for each channel when the value has changed with the keyboard.
417
428
  */
418
- 'aria-hidden': (isMobile || !focusedInputRef.current || focusedInputRef.current === inputXRef.current || valueChangedViaKeyboard.current ? undefined : 'true'),
429
+ 'aria-hidden': (isMobile || !focusedInput || focusedInput === 'x' || valueChangedViaKeyboard ? undefined : 'true'),
419
430
  onChange: (e: ChangeEvent<HTMLInputElement>) => {
420
431
  state.setXValue(parseFloat(e.target.value));
421
432
  }
@@ -433,13 +444,14 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
433
444
  'aria-orientation': 'vertical',
434
445
  disabled: isDisabled,
435
446
  value: state.value.getChannelValue(yChannel),
436
- tabIndex: (isMobile || (focusedInputRef.current && focusedInputRef.current === inputYRef.current) ? undefined : -1),
447
+ name: yName,
448
+ tabIndex: (isMobile || focusedInput === 'y' ? undefined : -1),
437
449
  /*
438
450
  So that only a single "2d slider" control shows up when listing form elements for screen readers,
439
451
  add aria-hidden="true" to the unfocused input when the value has not changed via the keyboard,
440
452
  but remove aria-hidden to reveal the input for each channel when the value has changed with the keyboard.
441
453
  */
442
- 'aria-hidden': (isMobile || (focusedInputRef.current && focusedInputRef.current === inputYRef.current) || valueChangedViaKeyboard.current ? undefined : 'true'),
454
+ 'aria-hidden': (isMobile || focusedInput === 'y' || valueChangedViaKeyboard ? undefined : 'true'),
443
455
  onChange: (e: ChangeEvent<HTMLInputElement>) => {
444
456
  state.setYValue(parseFloat(e.target.value));
445
457
  }
@@ -43,7 +43,7 @@ export interface ColorSliderAria {
43
43
  * Color sliders allow users to adjust an individual channel of a color value.
44
44
  */
45
45
  export function useColorSlider(props: AriaColorSliderOptions, state: ColorSliderState): ColorSliderAria {
46
- let {trackRef, inputRef, orientation, channel, 'aria-label': ariaLabel} = props;
46
+ let {trackRef, inputRef, orientation, channel, 'aria-label': ariaLabel, name} = props;
47
47
 
48
48
  let {locale, direction} = useLocale();
49
49
 
@@ -58,6 +58,7 @@ export function useColorSlider(props: AriaColorSliderOptions, state: ColorSlider
58
58
  index: 0,
59
59
  orientation,
60
60
  isDisabled: props.isDisabled,
61
+ name,
61
62
  trackRef,
62
63
  inputRef
63
64
  }, state);
@@ -13,7 +13,7 @@
13
13
  import {AriaColorWheelProps} from '@react-types/color';
14
14
  import {ColorWheelState} from '@react-stately/color';
15
15
  import {DOMAttributes} from '@react-types/shared';
16
- import {focusWithoutScrolling, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils';
16
+ import {focusWithoutScrolling, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
17
17
  import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react';
18
18
  import {useKeyboard, useMove} from '@react-aria/interactions';
19
19
  import {useLocale} from '@react-aria/i18n';
@@ -43,7 +43,8 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
43
43
  isDisabled,
44
44
  innerRadius,
45
45
  outerRadius,
46
- 'aria-label': ariaLabel
46
+ 'aria-label': ariaLabel,
47
+ name
47
48
  } = props;
48
49
 
49
50
  let {addGlobalListener, removeGlobalListener} = useGlobalListeners();
@@ -56,8 +57,7 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
56
57
  }
57
58
  }, [inputRef]);
58
59
 
59
- let stateRef = useRef<ColorWheelState>(null);
60
- stateRef.current = state;
60
+ useFormReset(inputRef, state.hue, state.setHue);
61
61
 
62
62
  let currentPosition = useRef<{x: number, y: number}>(null);
63
63
 
@@ -71,18 +71,18 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
71
71
  // same handling as useMove, don't need to stop propagation, useKeyboard will do that for us
72
72
  e.preventDefault();
73
73
  // remember to set this and unset it so that onChangeEnd is fired
74
- stateRef.current.setDragging(true);
74
+ state.setDragging(true);
75
75
  switch (e.key) {
76
76
  case 'PageUp':
77
77
  e.preventDefault();
78
- state.increment(stateRef.current.pageStep);
78
+ state.increment(state.pageStep);
79
79
  break;
80
80
  case 'PageDown':
81
81
  e.preventDefault();
82
- state.decrement(stateRef.current.pageStep);
82
+ state.decrement(state.pageStep);
83
83
  break;
84
84
  }
85
- stateRef.current.setDragging(false);
85
+ state.setDragging(false);
86
86
  }
87
87
  });
88
88
 
@@ -93,18 +93,18 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
93
93
  },
94
94
  onMove({deltaX, deltaY, pointerType, shiftKey}) {
95
95
  if (currentPosition.current == null) {
96
- currentPosition.current = stateRef.current.getThumbPosition(thumbRadius);
96
+ currentPosition.current = state.getThumbPosition(thumbRadius);
97
97
  }
98
98
  currentPosition.current.x += deltaX;
99
99
  currentPosition.current.y += deltaY;
100
100
  if (pointerType === 'keyboard') {
101
101
  if (deltaX > 0 || deltaY < 0) {
102
- state.increment(shiftKey ? stateRef.current.pageStep : stateRef.current.step);
102
+ state.increment(shiftKey ? state.pageStep : state.step);
103
103
  } else if (deltaX < 0 || deltaY > 0) {
104
- state.decrement(shiftKey ? stateRef.current.pageStep : stateRef.current.step);
104
+ state.decrement(shiftKey ? state.pageStep : state.step);
105
105
  }
106
106
  } else {
107
- stateRef.current.setHueFromPoint(currentPosition.current.x, currentPosition.current.y, thumbRadius);
107
+ state.setHueFromPoint(currentPosition.current.x, currentPosition.current.y, thumbRadius);
108
108
  }
109
109
  },
110
110
  onMoveEnd() {
@@ -175,7 +175,7 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
175
175
  if (innerRadius < radius && radius < outerRadius && !state.isDragging && currentPointer.current === undefined) {
176
176
  isOnTrack.current = true;
177
177
  currentPointer.current = id;
178
- stateRef.current.setHueFromPoint(x, y, radius);
178
+ state.setHueFromPoint(x, y, radius);
179
179
 
180
180
  focusInput();
181
181
  state.setDragging(true);
@@ -314,6 +314,7 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
314
314
  'aria-valuetext': state.value.formatChannelValue('hue', locale),
315
315
  disabled: isDisabled,
316
316
  value: `${state.value.getChannelValue('hue')}`,
317
+ name,
317
318
  onChange: (e: ChangeEvent<HTMLInputElement>) => {
318
319
  state.setHue(parseFloat(e.target.value));
319
320
  }