react-aria-components 1.0.0-beta.0 → 1.0.0-beta.2

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/src/DateField.tsx CHANGED
@@ -10,12 +10,14 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  import {AriaDateFieldProps, AriaTimeFieldProps, DateValue, mergeProps, TimeValue, useDateField, useDateSegment, useFocusRing, useHover, useLocale, useTimeField} from 'react-aria';
13
- import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps, useSlot} from './utils';
13
+ import {ContextValue, forwardRefType, Provider, removeDataAttributes, RenderProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps, useSlot} from './utils';
14
14
  import {createCalendar} from '@internationalized/date';
15
- import {DateFieldState, DateSegmentType, DateSegment as IDateSegment, useDateFieldState, useTimeFieldState} from 'react-stately';
15
+ import {DateFieldState, DateSegmentType, DateSegment as IDateSegment, TimeFieldState, useDateFieldState, useTimeFieldState} from 'react-stately';
16
16
  import {filterDOMProps, useObjectRef} from '@react-aria/utils';
17
+ import {Group, GroupContext} from './Group';
18
+ import {Input, InputContext} from './Input';
17
19
  import {LabelContext} from './Label';
18
- import React, {cloneElement, createContext, ForwardedRef, forwardRef, HTMLAttributes, InputHTMLAttributes, ReactElement, RefObject, useContext, useRef} from 'react';
20
+ import React, {cloneElement, createContext, ForwardedRef, forwardRef, ReactElement, useContext, useRef} from 'react';
19
21
  import {TextContext} from './Text';
20
22
 
21
23
  export interface DateFieldRenderProps {
@@ -37,16 +39,10 @@ export interface DateFieldRenderProps {
37
39
  export interface DateFieldProps<T extends DateValue> extends Omit<AriaDateFieldProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState'>, RenderProps<DateFieldRenderProps>, SlotProps {}
38
40
  export interface TimeFieldProps<T extends TimeValue> extends Omit<AriaTimeFieldProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState'>, RenderProps<DateFieldRenderProps>, SlotProps {}
39
41
 
40
- interface DateInputContextValue extends SlotProps {
41
- state: DateFieldState,
42
- fieldProps: HTMLAttributes<HTMLElement>,
43
- inputProps: InputHTMLAttributes<HTMLInputElement>,
44
- inputRef: RefObject<HTMLInputElement>
45
- }
46
-
47
42
  export const DateFieldContext = createContext<ContextValue<DateFieldProps<any>, HTMLDivElement>>(null);
48
43
  export const TimeFieldContext = createContext<ContextValue<TimeFieldProps<any>, HTMLDivElement>>(null);
49
- export const DateInputContext = createContext<ContextValue<DateInputContextValue, HTMLDivElement>>(null);
44
+ export const DateFieldStateContext = createContext<DateFieldState | null>(null);
45
+ export const TimeFieldStateContext = createContext<TimeFieldState | null>(null);
50
46
 
51
47
  function DateField<T extends DateValue>(props: DateFieldProps<T>, ref: ForwardedRef<HTMLDivElement>) {
52
48
  [props, ref] = useContextProps(props, ref, DateFieldContext);
@@ -60,10 +56,10 @@ function DateField<T extends DateValue>(props: DateFieldProps<T>, ref: Forwarded
60
56
  let fieldRef = useRef<HTMLDivElement>(null);
61
57
  let [labelRef, label] = useSlot();
62
58
  let inputRef = useRef<HTMLInputElement>(null);
63
- let {labelProps, fieldProps, inputProps, descriptionProps, errorMessageProps} = useDateField({...props, label, inputRef}, state, fieldRef);
59
+ let {labelProps, fieldProps, inputProps, descriptionProps, errorMessageProps} = useDateField({...removeDataAttributes(props), label, inputRef}, state, fieldRef);
64
60
 
65
61
  let renderProps = useRenderProps({
66
- ...props,
62
+ ...removeDataAttributes(props),
67
63
  values: {
68
64
  state,
69
65
  isInvalid: state.isInvalid,
@@ -78,7 +74,9 @@ function DateField<T extends DateValue>(props: DateFieldProps<T>, ref: Forwarded
78
74
  return (
79
75
  <Provider
80
76
  values={[
81
- [DateInputContext, {state, fieldProps, ref: fieldRef, inputRef, inputProps}],
77
+ [DateFieldStateContext, state],
78
+ [GroupContext, {...fieldProps, ref: fieldRef}],
79
+ [InputContext, {...inputProps, ref: inputRef}],
82
80
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
83
81
  [TextContext, {
84
82
  slots: {
@@ -91,7 +89,7 @@ function DateField<T extends DateValue>(props: DateFieldProps<T>, ref: Forwarded
91
89
  {...DOMProps}
92
90
  {...renderProps}
93
91
  ref={ref}
94
- slot={props.slot}
92
+ slot={props.slot || undefined}
95
93
  data-invalid={state.isInvalid || undefined} />
96
94
  </Provider>
97
95
  );
@@ -115,7 +113,7 @@ function TimeField<T extends TimeValue>(props: TimeFieldProps<T>, ref: Forwarded
115
113
  let fieldRef = useRef<HTMLDivElement>(null);
116
114
  let [labelRef, label] = useSlot();
117
115
  let inputRef = useRef<HTMLInputElement>(null);
118
- let {labelProps, fieldProps, inputProps, descriptionProps, errorMessageProps} = useTimeField({...props, label, inputRef}, state, fieldRef);
116
+ let {labelProps, fieldProps, inputProps, descriptionProps, errorMessageProps} = useTimeField({...removeDataAttributes(props), label, inputRef}, state, fieldRef);
119
117
 
120
118
  let renderProps = useRenderProps({
121
119
  ...props,
@@ -133,7 +131,9 @@ function TimeField<T extends TimeValue>(props: TimeFieldProps<T>, ref: Forwarded
133
131
  return (
134
132
  <Provider
135
133
  values={[
136
- [DateInputContext, {state, fieldProps, ref: fieldRef, inputRef, inputProps}],
134
+ [TimeFieldStateContext, state],
135
+ [GroupContext, {...fieldProps, ref: fieldRef}],
136
+ [InputContext, {...inputProps, ref: inputRef}],
137
137
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
138
138
  [TextContext, {
139
139
  slots: {
@@ -146,7 +146,7 @@ function TimeField<T extends TimeValue>(props: TimeFieldProps<T>, ref: Forwarded
146
146
  {...DOMProps}
147
147
  {...renderProps}
148
148
  ref={ref}
149
- slot={props.slot}
149
+ slot={props.slot || undefined}
150
150
  data-invalid={state.isInvalid || undefined} />
151
151
  </Provider>
152
152
  );
@@ -159,8 +159,6 @@ function TimeField<T extends TimeValue>(props: TimeFieldProps<T>, ref: Forwarded
159
159
  const _TimeField = /*#__PURE__*/ (forwardRef as forwardRefType)(TimeField);
160
160
  export {_TimeField as TimeField};
161
161
 
162
- const InternalDateInputContext = createContext<DateFieldState| null>(null);
163
-
164
162
  export interface DateInputRenderProps {
165
163
  /**
166
164
  * Whether the date input is currently hovered with a mouse.
@@ -188,38 +186,60 @@ export interface DateInputProps extends SlotProps, StyleRenderProps<DateInputRen
188
186
  children: (segment: IDateSegment) => ReactElement
189
187
  }
190
188
 
191
- function DateInput({children, slot, ...otherProps}: DateInputProps, ref: ForwardedRef<HTMLDivElement>) {
192
- let [{state, fieldProps, inputProps, inputRef}, fieldRef] = useContextProps({slot} as DateInputProps & DateInputContextValue, ref, DateInputContext);
189
+ function DateInput(props: DateInputProps, ref: ForwardedRef<HTMLDivElement>): JSX.Element {
190
+ // If state is provided by DateField/TimeField, just render.
191
+ // Otherwise (e.g. in DatePicker), we need to call hooks and create state ourselves.
192
+ let dateFieldState = useContext(DateFieldStateContext);
193
+ let timeFieldState = useContext(TimeFieldStateContext);
194
+ return dateFieldState || timeFieldState
195
+ ? <DateInputInner {...props} ref={ref} />
196
+ : <DateInputStandalone {...props} ref={ref} />;
197
+ }
193
198
 
194
- let {hoverProps, isHovered} = useHover({});
195
- let {isFocused, isFocusVisible, focusProps} = useFocusRing({
196
- within: true,
197
- isTextInput: true
199
+ const DateInputStandalone = forwardRef((props: DateInputProps, ref: ForwardedRef<HTMLDivElement>) => {
200
+ let [dateFieldProps, fieldRef] = useContextProps({slot: props.slot} as DateFieldProps<any>, ref, DateFieldContext);
201
+ let {locale} = useLocale();
202
+ let state = useDateFieldState({
203
+ ...dateFieldProps,
204
+ locale,
205
+ createCalendar
198
206
  });
199
207
 
200
- let renderProps = useRenderProps({
201
- ...otherProps,
202
- values: {isHovered, isFocusWithin: isFocused, isFocusVisible, isDisabled: state.isDisabled},
203
- defaultClassName: 'react-aria-DateInput'
204
- });
208
+ let inputRef = useRef<HTMLInputElement>(null);
209
+ let {fieldProps, inputProps} = useDateField({...dateFieldProps, inputRef}, state, fieldRef);
205
210
 
206
211
  return (
207
- <InternalDateInputContext.Provider value={state}>
208
- <div
209
- {...mergeProps(filterDOMProps(otherProps as any), fieldProps, focusProps, hoverProps)}
210
- {...renderProps}
211
- ref={fieldRef}
212
- slot={slot}
213
- data-focus-within={isFocused || undefined}
214
- data-hovered={isHovered || undefined}
215
- data-focus-visible={isFocusVisible || undefined}
216
- data-disabled={state.isDisabled || undefined}>
212
+ <Provider
213
+ values={[
214
+ [DateFieldStateContext, state],
215
+ [InputContext, {...inputProps, ref: inputRef}],
216
+ [GroupContext, {...fieldProps, ref: fieldRef}]
217
+ ]}>
218
+ <DateInputInner {...props} />
219
+ </Provider>
220
+ );
221
+ });
222
+
223
+ const DateInputInner = forwardRef((props: DateInputProps, ref: ForwardedRef<HTMLDivElement>) => {
224
+ let {className, children} = props;
225
+ let dateFieldState = useContext(DateFieldStateContext);
226
+ let timeFieldState = useContext(TimeFieldStateContext);
227
+ let state = dateFieldState ?? timeFieldState!;
228
+
229
+ return (
230
+ <>
231
+ <Group
232
+ {...props}
233
+ ref={ref}
234
+ slot={props.slot || undefined}
235
+ className={className ?? 'react-aria-DateInput'}
236
+ isInvalid={state.isInvalid}>
217
237
  {state.segments.map((segment, i) => cloneElement(children(segment), {key: i}))}
218
- </div>
219
- <input {...inputProps} ref={inputRef} />
220
- </InternalDateInputContext.Provider>
238
+ </Group>
239
+ <Input />
240
+ </>
221
241
  );
222
- }
242
+ });
223
243
 
224
244
  /**
225
245
  * A date input groups the editable date segments within a date field.
@@ -228,6 +248,21 @@ const _DateInput = /*#__PURE__*/ (forwardRef as forwardRefType)(DateInput);
228
248
  export {_DateInput as DateInput};
229
249
 
230
250
  export interface DateSegmentRenderProps extends Omit<IDateSegment, 'isEditable'> {
251
+ /**
252
+ * Whether the segment is currently hovered with a mouse.
253
+ * @selector [data-hovered]
254
+ */
255
+ isHovered: boolean,
256
+ /**
257
+ * Whether the segment is focused, either via a mouse or keyboard.
258
+ * @selector [data-focused]
259
+ */
260
+ isFocused: boolean,
261
+ /**
262
+ * Whether the segment is keyboard focused.
263
+ * @selector [data-focus-visible]
264
+ */
265
+ isFocusVisible: boolean,
231
266
  /**
232
267
  * Whether the value is a placeholder.
233
268
  * @selector [data-placeholder]
@@ -255,29 +290,40 @@ export interface DateSegmentProps extends RenderProps<DateSegmentRenderProps> {
255
290
  }
256
291
 
257
292
  function DateSegment({segment, ...otherProps}: DateSegmentProps, ref: ForwardedRef<HTMLDivElement>) {
258
- let state = useContext(InternalDateInputContext)!;
293
+ let dateFieldState = useContext(DateFieldStateContext);
294
+ let timeFieldState = useContext(TimeFieldStateContext);
295
+ let state = dateFieldState ?? timeFieldState!;
259
296
  let domRef = useObjectRef(ref);
260
297
  let {segmentProps} = useDateSegment(segment, state, domRef);
298
+ let {focusProps, isFocused, isFocusVisible} = useFocusRing();
299
+ let {hoverProps, isHovered} = useHover({isDisabled: state.isDisabled || segment.type === 'literal'});
261
300
  let renderProps = useRenderProps({
262
301
  ...otherProps,
263
302
  values: {
264
303
  ...segment,
265
304
  isReadOnly: !segment.isEditable,
266
- isInvalid: state.isInvalid
305
+ isInvalid: state.isInvalid,
306
+ isHovered,
307
+ isFocused,
308
+ isFocusVisible
267
309
  },
268
310
  defaultChildren: segment.text,
269
311
  defaultClassName: 'react-aria-DateSegment'
270
312
  });
271
313
 
314
+
272
315
  return (
273
316
  <div
274
- {...mergeProps(filterDOMProps(otherProps as any), segmentProps)}
317
+ {...mergeProps(filterDOMProps(otherProps as any), segmentProps, focusProps, hoverProps)}
275
318
  {...renderProps}
276
319
  ref={domRef}
277
320
  data-placeholder={segment.isPlaceholder || undefined}
278
321
  data-invalid={state.isInvalid || undefined}
279
322
  data-readonly={!segment.isEditable || undefined}
280
- data-type={segment.type} />
323
+ data-type={segment.type}
324
+ data-hovered={isHovered || undefined}
325
+ data-focused={isFocused || undefined}
326
+ data-focus-visible={isFocusVisible || undefined} />
281
327
  );
282
328
  }
283
329
 
@@ -9,14 +9,13 @@
9
9
  * OF ANY KIND, either express or implied. See the License for the specific language
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
- import {AriaDatePickerProps, AriaDateRangePickerProps, DateValue, useDateField, useDatePicker, useDateRangePicker, useFocusRing, useLocale} from 'react-aria';
12
+ import {AriaDatePickerProps, AriaDateRangePickerProps, DateValue, useDatePicker, useDateRangePicker, useFocusRing} from 'react-aria';
13
13
  import {ButtonContext} from './Button';
14
14
  import {CalendarContext, RangeCalendarContext} from './Calendar';
15
- import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
16
- import {createCalendar} from '@internationalized/date';
17
- import {DateInputContext} from './DateField';
18
- import {DatePickerState, DateRangePickerState, useDateFieldState, useDatePickerState, useDateRangePickerState} from 'react-stately';
19
- import {DialogContext} from './Dialog';
15
+ import {ContextValue, forwardRefType, Provider, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
16
+ import {DateFieldContext} from './DateField';
17
+ import {DatePickerState, DateRangePickerState, useDatePickerState, useDateRangePickerState} from 'react-stately';
18
+ import {DialogContext, OverlayTriggerStateContext} from './Dialog';
20
19
  import {filterDOMProps} from '@react-aria/utils';
21
20
  import {GroupContext} from './Group';
22
21
  import {LabelContext} from './Label';
@@ -45,6 +44,11 @@ export interface DatePickerRenderProps {
45
44
  * @selector [data-disabled]
46
45
  */
47
46
  isInvalid: boolean,
47
+ /**
48
+ * Whether the date picker's popover is currently open.
49
+ * @selector [data-open]
50
+ */
51
+ isOpen: boolean,
48
52
  /**
49
53
  * State of the date picker.
50
54
  */
@@ -62,6 +66,8 @@ export interface DateRangePickerProps<T extends DateValue> extends Omit<AriaDate
62
66
 
63
67
  export const DatePickerContext = createContext<ContextValue<DatePickerProps<any>, HTMLDivElement>>(null);
64
68
  export const DateRangePickerContext = createContext<ContextValue<DateRangePickerProps<any>, HTMLDivElement>>(null);
69
+ export const DatePickerStateContext = createContext<DatePickerState | null>(null);
70
+ export const DateRangePickerStateContext = createContext<DateRangePickerState | null>(null);
65
71
 
66
72
  function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: ForwardedRef<HTMLDivElement>) {
67
73
  [props, ref] = useContextProps(props, ref, DatePickerContext);
@@ -77,20 +83,9 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
77
83
  calendarProps,
78
84
  descriptionProps,
79
85
  errorMessageProps
80
- } = useDatePicker({...props, label}, state, groupRef);
81
-
82
- let {locale} = useLocale();
83
- let fieldState = useDateFieldState({
84
- ...fieldProps,
85
- locale,
86
- createCalendar
87
- });
86
+ } = useDatePicker({...removeDataAttributes(props), label}, state, groupRef);
88
87
 
89
- let fieldRef = useRef<HTMLDivElement>(null);
90
- let inputRef = useRef<HTMLInputElement>(null);
91
88
  let {focusProps, isFocused, isFocusVisible} = useFocusRing({within: true});
92
- let {fieldProps: dateFieldProps, inputProps} = useDateField({...fieldProps, label, inputRef}, fieldState, fieldRef);
93
-
94
89
  let renderProps = useRenderProps({
95
90
  ...props,
96
91
  values: {
@@ -98,7 +93,8 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
98
93
  isFocusWithin: isFocused,
99
94
  isFocusVisible,
100
95
  isDisabled: props.isDisabled || false,
101
- isInvalid: state.isInvalid
96
+ isInvalid: state.isInvalid,
97
+ isOpen: state.isOpen
102
98
  },
103
99
  defaultClassName: 'react-aria-DatePicker'
104
100
  });
@@ -109,12 +105,14 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
109
105
  return (
110
106
  <Provider
111
107
  values={[
108
+ [DatePickerStateContext, state],
112
109
  [GroupContext, {...groupProps, ref: groupRef}],
113
- [DateInputContext, {state: fieldState, fieldProps: dateFieldProps, ref: fieldRef, inputProps, inputRef}],
110
+ [DateFieldContext, fieldProps],
114
111
  [ButtonContext, {...buttonProps, isPressed: state.isOpen}],
115
112
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
116
113
  [CalendarContext, calendarProps],
117
- [PopoverContext, {state, triggerRef: groupRef, placement: 'bottom start'}],
114
+ [OverlayTriggerStateContext, state],
115
+ [PopoverContext, {triggerRef: groupRef, placement: 'bottom start'}],
118
116
  [DialogContext, dialogProps],
119
117
  [TextContext, {
120
118
  slots: {
@@ -128,11 +126,12 @@ function DatePicker<T extends DateValue>(props: DatePickerProps<T>, ref: Forward
128
126
  {...DOMProps}
129
127
  {...renderProps}
130
128
  ref={ref}
131
- slot={props.slot}
129
+ slot={props.slot || undefined}
132
130
  data-focus-within={isFocused || undefined}
133
131
  data-invalid={state.isInvalid || undefined}
134
132
  data-focus-visible={isFocusVisible || undefined}
135
- data-disabled={props.isDisabled || undefined} />
133
+ data-disabled={props.isDisabled || undefined}
134
+ data-open={state.isOpen || undefined} />
136
135
  </Provider>
137
136
  );
138
137
  }
@@ -158,30 +157,9 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
158
157
  calendarProps,
159
158
  descriptionProps,
160
159
  errorMessageProps
161
- } = useDateRangePicker({...props, label}, state, groupRef);
160
+ } = useDateRangePicker({...removeDataAttributes(props), label}, state, groupRef);
162
161
 
163
- let {locale} = useLocale();
164
- let startFieldState = useDateFieldState({
165
- ...startFieldProps,
166
- locale,
167
- createCalendar
168
- });
169
-
170
- let startFieldRef = useRef<HTMLDivElement>(null);
171
- let startInputRef = useRef<HTMLInputElement>(null);
172
162
  let {focusProps, isFocused, isFocusVisible} = useFocusRing({within: true});
173
- let {fieldProps: startDateFieldProps, inputProps: startInputProps} = useDateField({...startFieldProps, label, inputRef: startInputRef}, startFieldState, startFieldRef);
174
-
175
- let endFieldState = useDateFieldState({
176
- ...endFieldProps,
177
- locale,
178
- createCalendar
179
- });
180
-
181
- let endFieldRef = useRef<HTMLDivElement>(null);
182
- let endInputRef = useRef<HTMLInputElement>(null);
183
- let {fieldProps: endDateFieldProps, inputProps: endInputProps} = useDateField({...endFieldProps, label, inputRef: endInputRef}, endFieldState, endFieldRef);
184
-
185
163
  let renderProps = useRenderProps({
186
164
  ...props,
187
165
  values: {
@@ -189,7 +167,8 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
189
167
  isFocusWithin: isFocused,
190
168
  isFocusVisible,
191
169
  isDisabled: props.isDisabled || false,
192
- isInvalid: state.isInvalid
170
+ isInvalid: state.isInvalid,
171
+ isOpen: state.isOpen
193
172
  },
194
173
  defaultClassName: 'react-aria-DateRangePicker'
195
174
  });
@@ -200,28 +179,18 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
200
179
  return (
201
180
  <Provider
202
181
  values={[
182
+ [DateRangePickerStateContext, state],
203
183
  [GroupContext, {...groupProps, ref: groupRef}],
204
184
  [ButtonContext, {...buttonProps, isPressed: state.isOpen}],
205
185
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
206
186
  [RangeCalendarContext, calendarProps],
207
- [PopoverContext, {state, triggerRef: groupRef, placement: 'bottom start'}],
187
+ [OverlayTriggerStateContext, state],
188
+ [PopoverContext, {triggerRef: groupRef, placement: 'bottom start'}],
208
189
  [DialogContext, dialogProps],
209
- [DateInputContext, {
190
+ [DateFieldContext, {
210
191
  slots: {
211
- start: {
212
- state: startFieldState,
213
- fieldProps: startDateFieldProps,
214
- ref: startFieldRef,
215
- inputRef: startInputRef,
216
- inputProps: startInputProps
217
- },
218
- end: {
219
- state: endFieldState,
220
- fieldProps: endDateFieldProps,
221
- ref: endFieldRef,
222
- inputRef: endInputRef,
223
- inputProps: endInputProps
224
- }
192
+ start: startFieldProps,
193
+ end: endFieldProps
225
194
  }
226
195
  }],
227
196
  [TextContext, {
@@ -236,11 +205,12 @@ function DateRangePicker<T extends DateValue>(props: DateRangePickerProps<T>, re
236
205
  {...DOMProps}
237
206
  {...renderProps}
238
207
  ref={ref}
239
- slot={props.slot}
208
+ slot={props.slot || undefined}
240
209
  data-focus-within={isFocused || undefined}
241
210
  data-invalid={state.isInvalid || undefined}
242
211
  data-focus-visible={isFocusVisible || undefined}
243
- data-disabled={props.isDisabled || undefined} />
212
+ data-disabled={props.isDisabled || undefined}
213
+ data-open={state.isOpen || undefined} />
244
214
  </Provider>
245
215
  );
246
216
  }
package/src/Dialog.tsx CHANGED
@@ -10,14 +10,13 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  import {AriaDialogProps, useDialog, useOverlayTrigger} from 'react-aria';
13
- import {ButtonContext} from './Button';
14
13
  import {ContextValue, forwardRefType, Provider, SlotProps, StyleProps, useContextProps} from './utils';
15
14
  import {filterDOMProps} from '@react-aria/utils';
16
15
  import {HeadingContext} from './Heading';
17
- import {ModalContext} from './Modal';
18
- import {OverlayTriggerProps, useOverlayTriggerState} from 'react-stately';
16
+ import {OverlayTriggerProps, OverlayTriggerState, useOverlayTriggerState} from 'react-stately';
19
17
  import {PopoverContext} from './Popover';
20
- import React, {createContext, ForwardedRef, forwardRef, ReactNode, useRef} from 'react';
18
+ import {PressResponder} from '@react-aria/interactions';
19
+ import React, {createContext, ForwardedRef, forwardRef, ReactNode, useContext, useRef} from 'react';
21
20
 
22
21
  export interface DialogTriggerProps extends OverlayTriggerProps {
23
22
  children: ReactNode
@@ -28,11 +27,12 @@ interface DialogRenderProps {
28
27
  }
29
28
 
30
29
  export interface DialogProps extends AriaDialogProps, StyleProps, SlotProps {
31
- children?: ReactNode | ((opts: DialogRenderProps) => ReactNode),
32
- onClose?: () => void
30
+ /** Children of the dialog. A function may be provided to access a function to close the dialog. */
31
+ children?: ReactNode | ((opts: DialogRenderProps) => ReactNode)
33
32
  }
34
33
 
35
34
  export const DialogContext = createContext<ContextValue<DialogProps, HTMLElement>>(null);
35
+ export const OverlayTriggerStateContext = createContext<OverlayTriggerState | null>(null);
36
36
 
37
37
  /**
38
38
  * A DialogTrigger opens a dialog when a trigger element is pressed.
@@ -46,25 +46,26 @@ export function DialogTrigger(props: DialogTriggerProps) {
46
46
  return (
47
47
  <Provider
48
48
  values={[
49
- [ModalContext, {state}],
50
- [DialogContext, {...overlayProps, onClose: state.close}],
51
- [ButtonContext, {...triggerProps, isPressed: state.isOpen, ref: buttonRef}],
52
- [PopoverContext, {state, triggerRef: buttonRef}]
49
+ [OverlayTriggerStateContext, state],
50
+ [DialogContext, overlayProps],
51
+ [PopoverContext, {triggerRef: buttonRef}]
53
52
  ]}>
54
- {props.children}
53
+ <PressResponder {...triggerProps} ref={buttonRef} isPressed={state.isOpen}>
54
+ {props.children}
55
+ </PressResponder>
55
56
  </Provider>
56
57
  );
57
58
  }
58
59
 
59
-
60
60
  function Dialog(props: DialogProps, ref: ForwardedRef<HTMLElement>) {
61
61
  [props, ref] = useContextProps(props, ref, DialogContext);
62
62
  let {dialogProps, titleProps} = useDialog(props, ref);
63
+ let state = useContext(OverlayTriggerStateContext);
63
64
 
64
65
  let children = props.children;
65
66
  if (typeof children === 'function') {
66
67
  children = children({
67
- close: props.onClose || (() => {})
68
+ close: state?.close || (() => {})
68
69
  });
69
70
  }
70
71
 
@@ -73,12 +74,11 @@ function Dialog(props: DialogProps, ref: ForwardedRef<HTMLElement>) {
73
74
  {...filterDOMProps(props)}
74
75
  {...dialogProps}
75
76
  ref={ref}
76
- slot={props.slot}
77
+ slot={props.slot || undefined}
77
78
  style={props.style}
78
79
  className={props.className ?? 'react-aria-Dialog'}>
79
80
  <Provider
80
81
  values={[
81
- [ButtonContext, undefined],
82
82
  // TODO: clear context within dialog content?
83
83
  [HeadingContext, {...titleProps, level: 2}]
84
84
  ]}>
package/src/DropZone.tsx CHANGED
@@ -12,8 +12,10 @@
12
12
 
13
13
  import {AriaLabelingProps} from '@react-types/shared';
14
14
  import {ContextValue, Provider, RenderProps, SlotProps, useContextProps, useRenderProps} from './utils';
15
- import {DropOptions, mergeProps, useClipboard, useDrop, useFocusRing, useHover, VisuallyHidden} from 'react-aria';
15
+ import {DropOptions, mergeProps, useClipboard, useDrop, useFocusRing, useHover, useLocalizedStringFormatter, VisuallyHidden} from 'react-aria';
16
16
  import {filterDOMProps, useLabels, useSlotId} from '@react-aria/utils';
17
+ // @ts-ignore
18
+ import intlMessages from '../intl/*.json';
17
19
  import React, {createContext, ForwardedRef, forwardRef, useRef} from 'react';
18
20
  import {TextContext} from './Text';
19
21
 
@@ -50,9 +52,10 @@ function DropZone(props: DropZoneProps, ref: ForwardedRef<HTMLDivElement>) {
50
52
  let {dropProps, dropButtonProps, isDropTarget} = useDrop({...props, ref: buttonRef, hasDropButton: true});
51
53
  let {hoverProps, isHovered} = useHover({});
52
54
  let {focusProps, isFocused, isFocusVisible} = useFocusRing();
55
+ let stringFormatter = useLocalizedStringFormatter(intlMessages);
53
56
 
54
57
  let textId = useSlotId();
55
- let ariaLabel = props['aria-label'] || 'DropZone';
58
+ let ariaLabel = props['aria-label'] || stringFormatter.format('dropzoneLabel');
56
59
  let messageId = (isDropTarget && props['aria-labelledby']) ? props['aria-labelledby'] : null;
57
60
  let ariaLabelledby = [textId, messageId].filter(Boolean).join(' ');
58
61
  let labelProps = useLabels({'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby});
@@ -84,7 +87,7 @@ function DropZone(props: DropZoneProps, ref: ForwardedRef<HTMLDivElement>) {
84
87
  <div
85
88
  {...mergeProps(dropProps, hoverProps, DOMProps)}
86
89
  {...renderProps}
87
- slot={props.slot}
90
+ slot={props.slot || undefined}
88
91
  ref={ref}
89
92
  onClick={() => buttonRef.current?.focus()}
90
93
  data-hovered={isHovered || undefined}
@@ -31,11 +31,7 @@ export interface FileTriggerProps {
31
31
  /**
32
32
  * Handler when a user selects a file.
33
33
  */
34
- onChange?: (files: FileList | null) => void,
35
- /**
36
- * The name of the input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname).
37
- */
38
- name?: string,
34
+ onSelect?: (files: FileList | null) => void,
39
35
  /**
40
36
  * The children of the component.
41
37
  */
@@ -43,13 +39,19 @@ export interface FileTriggerProps {
43
39
  }
44
40
 
45
41
  function FileTrigger(props: FileTriggerProps, ref: ForwardedRef<HTMLInputElement>) {
46
- let {onChange, acceptedFileTypes, allowsMultiple, defaultCamera, name, children, ...rest} = props;
42
+ let {onSelect, acceptedFileTypes, allowsMultiple, defaultCamera, children, ...rest} = props;
47
43
  let inputRef = useObjectRef(ref);
48
44
  let domProps = filterDOMProps(rest);
49
45
 
50
46
  return (
51
47
  <>
52
- <PressResponder onPress={() => inputRef.current?.click()}>
48
+ <PressResponder
49
+ onPress={() => {
50
+ if (inputRef.current.value) {
51
+ inputRef.current.value = '';
52
+ }
53
+ inputRef.current?.click();
54
+ }}>
53
55
  {children}
54
56
  </PressResponder>
55
57
  <Input
@@ -58,10 +60,9 @@ function FileTrigger(props: FileTriggerProps, ref: ForwardedRef<HTMLInputElement
58
60
  ref={inputRef}
59
61
  style={{display: 'none'}}
60
62
  accept={acceptedFileTypes?.toString()}
61
- onChange={(e) => onChange?.(e.target.files)}
63
+ onChange={(e) => onSelect?.(e.target.files)}
62
64
  capture={defaultCamera}
63
- multiple={allowsMultiple}
64
- name={name} />
65
+ multiple={allowsMultiple} />
65
66
  </>
66
67
  );
67
68
  }