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/Popover.tsx CHANGED
@@ -15,6 +15,7 @@ import {ContextValue, forwardRefType, HiddenContext, RenderProps, SlotProps, use
15
15
  import {filterDOMProps, mergeProps} from '@react-aria/utils';
16
16
  import {OverlayArrowContext} from './OverlayArrow';
17
17
  import {OverlayTriggerProps, OverlayTriggerState, useOverlayTriggerState} from 'react-stately';
18
+ import {OverlayTriggerStateContext} from './Dialog';
18
19
  import React, {createContext, ForwardedRef, forwardRef, RefObject, useContext} from 'react';
19
20
 
20
21
  export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPopoverProps, 'popoverRef' | 'triggerRef'>, OverlayTriggerProps, RenderProps<PopoverRenderProps>, SlotProps {
@@ -45,18 +46,13 @@ export interface PopoverRenderProps {
45
46
  isExiting: boolean
46
47
  }
47
48
 
48
- interface PopoverContextValue extends PopoverProps {
49
- state?: OverlayTriggerState,
50
- triggerRef?: RefObject<Element>
51
- }
52
-
53
- export const PopoverContext = createContext<ContextValue<PopoverContextValue, HTMLElement>>(null);
49
+ export const PopoverContext = createContext<ContextValue<PopoverProps, HTMLElement>>(null);
54
50
 
55
51
  function Popover(props: PopoverProps, ref: ForwardedRef<HTMLElement>) {
56
52
  [props, ref] = useContextProps(props, ref, PopoverContext);
57
- let ctx = props as PopoverContextValue;
53
+ let contextState = useContext(OverlayTriggerStateContext);
58
54
  let localState = useOverlayTriggerState(props);
59
- let state = props.isOpen != null || props.defaultOpen != null || !ctx?.state ? localState : ctx.state;
55
+ let state = props.isOpen != null || props.defaultOpen != null || !contextState ? localState : contextState;
60
56
  let isExiting = useExitAnimation(ref, state.isOpen);
61
57
  let isHidden = useContext(HiddenContext);
62
58
 
@@ -81,7 +77,7 @@ function Popover(props: PopoverProps, ref: ForwardedRef<HTMLElement>) {
81
77
  return (
82
78
  <PopoverInner
83
79
  {...props}
84
- triggerRef={ctx.triggerRef!}
80
+ triggerRef={props.triggerRef!}
85
81
  state={state}
86
82
  popoverRef={ref}
87
83
  isExiting={isExiting} />
@@ -126,13 +122,13 @@ function PopoverInner({state, isExiting, ...props}: PopoverInnerProps) {
126
122
  {...mergeProps(filterDOMProps(props as any), popoverProps)}
127
123
  {...renderProps}
128
124
  ref={ref}
129
- slot={props.slot}
125
+ slot={props.slot || undefined}
130
126
  style={style}
131
127
  data-placement={placement}
132
128
  data-entering={isEntering || undefined}
133
129
  data-exiting={isExiting || undefined}>
134
130
  {!props.isNonModal && <DismissButton onDismiss={state.close} />}
135
- <OverlayArrowContext.Provider value={{arrowProps, placement}}>
131
+ <OverlayArrowContext.Provider value={{...arrowProps, placement}}>
136
132
  {renderProps.children}
137
133
  </OverlayArrowContext.Provider>
138
134
  <DismissButton onDismiss={state.close} />
@@ -65,7 +65,7 @@ function ProgressBar(props: ProgressBarProps, ref: ForwardedRef<HTMLDivElement>)
65
65
  });
66
66
 
67
67
  return (
68
- <div {...progressBarProps} {...renderProps} ref={ref} slot={props.slot}>
68
+ <div {...progressBarProps} {...renderProps} ref={ref} slot={props.slot || undefined}>
69
69
  <LabelContext.Provider value={{...labelProps, ref: labelRef, elementType: 'span'}}>
70
70
  {renderProps.children}
71
71
  </LabelContext.Provider>
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import {AriaRadioGroupProps, AriaRadioProps, Orientation, useFocusRing, useHover, usePress, useRadio, useRadioGroup, VisuallyHidden} from 'react-aria';
14
- import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
14
+ import {ContextValue, forwardRefType, Provider, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
15
15
  import {filterDOMProps, mergeProps, useObjectRef} from '@react-aria/utils';
16
16
  import {LabelContext} from './Label';
17
17
  import {RadioGroupState, useRadioGroupState} from 'react-stately';
@@ -103,7 +103,7 @@ export interface RadioRenderProps {
103
103
 
104
104
  export const RadioGroupContext = createContext<ContextValue<RadioGroupProps, HTMLDivElement>>(null);
105
105
  export const RadioContext = createContext<ContextValue<Partial<RadioProps>, HTMLInputElement>>(null);
106
- let InternalRadioContext = createContext<RadioGroupState | null>(null);
106
+ export const RadioGroupStateContext = createContext<RadioGroupState | null>(null);
107
107
 
108
108
  function RadioGroup(props: RadioGroupProps, ref: ForwardedRef<HTMLDivElement>) {
109
109
  [props, ref] = useContextProps(props, ref, RadioGroupContext);
@@ -132,7 +132,7 @@ function RadioGroup(props: RadioGroupProps, ref: ForwardedRef<HTMLDivElement>) {
132
132
  {...radioGroupProps}
133
133
  {...renderProps}
134
134
  ref={ref}
135
- slot={props.slot}
135
+ slot={props.slot || undefined}
136
136
  data-orientation={props.orientation || 'vertical'}
137
137
  data-invalid={state.isInvalid || undefined}
138
138
  data-disabled={state.isDisabled || undefined}
@@ -140,7 +140,7 @@ function RadioGroup(props: RadioGroupProps, ref: ForwardedRef<HTMLDivElement>) {
140
140
  data-required={state.isRequired || undefined}>
141
141
  <Provider
142
142
  values={[
143
- [InternalRadioContext, state],
143
+ [RadioGroupStateContext, state],
144
144
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
145
145
  [TextContext, {
146
146
  slots: {
@@ -157,10 +157,10 @@ function RadioGroup(props: RadioGroupProps, ref: ForwardedRef<HTMLDivElement>) {
157
157
 
158
158
  function Radio(props: RadioProps, ref: ForwardedRef<HTMLInputElement>) {
159
159
  [props, ref] = useContextProps(props, ref, RadioContext);
160
- let state = React.useContext(InternalRadioContext)!;
160
+ let state = React.useContext(RadioGroupStateContext)!;
161
161
  let domRef = useObjectRef(ref);
162
162
  let {inputProps, isSelected, isDisabled, isPressed: isPressedKeyboard} = useRadio({
163
- ...props,
163
+ ...removeDataAttributes<RadioProps>(props),
164
164
  // ReactNode type doesn't allow function children.
165
165
  children: typeof props.children === 'function' ? true : props.children
166
166
  }, state, domRef);
@@ -12,7 +12,7 @@
12
12
 
13
13
  import {AriaSearchFieldProps, useSearchField} from 'react-aria';
14
14
  import {ButtonContext} from './Button';
15
- import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
15
+ import {ContextValue, forwardRefType, Provider, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
16
16
  import {filterDOMProps} from '@react-aria/utils';
17
17
  import {InputContext} from './Input';
18
18
  import {LabelContext} from './Label';
@@ -52,7 +52,7 @@ function SearchField(props: SearchFieldProps, ref: ForwardedRef<HTMLDivElement>)
52
52
  let [labelRef, label] = useSlot();
53
53
  let state = useSearchFieldState(props);
54
54
  let {labelProps, inputProps, clearButtonProps, descriptionProps, errorMessageProps} = useSearchField({
55
- ...props,
55
+ ...removeDataAttributes(props),
56
56
  label
57
57
  }, state, inputRef);
58
58
 
@@ -75,7 +75,7 @@ function SearchField(props: SearchFieldProps, ref: ForwardedRef<HTMLDivElement>)
75
75
  {...DOMProps}
76
76
  {...renderProps}
77
77
  ref={ref}
78
- slot={props.slot}
78
+ slot={props.slot || undefined}
79
79
  data-empty={state.value === '' || undefined}
80
80
  data-disabled={props.isDisabled || undefined}
81
81
  data-invalid={props.isInvalid || undefined}>
package/src/Select.tsx CHANGED
@@ -10,13 +10,16 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {AriaSelectProps, HiddenSelect, useFocusRing, useSelect} from 'react-aria';
13
+ import {AriaSelectProps, HiddenSelect, useFocusRing, useLocalizedStringFormatter, useSelect} from 'react-aria';
14
14
  import {ButtonContext} from './Button';
15
- import {ContextValue, forwardRefType, Hidden, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
15
+ import {CollectionDocumentContext, ItemRenderProps, useCollectionDocument} from './Collection';
16
+ import {ContextValue, forwardRefType, Hidden, Provider, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
16
17
  import {filterDOMProps, useResizeObserver} from '@react-aria/utils';
17
- import {ItemRenderProps, useCollectionDocument} from './Collection';
18
+ // @ts-ignore
19
+ import intlMessages from '../intl/*.json';
18
20
  import {LabelContext} from './Label';
19
- import {ListBoxContext} from './ListBox';
21
+ import {ListBoxContext, ListStateContext} from './ListBox';
22
+ import {OverlayTriggerStateContext} from './Dialog';
20
23
  import {PopoverContext} from './Popover';
21
24
  import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, ReactNode, useCallback, useContext, useMemo, useRef, useState} from 'react';
22
25
  import {SelectState, useSelectState} from 'react-stately';
@@ -57,14 +60,8 @@ export interface SelectRenderProps {
57
60
 
58
61
  export interface SelectProps<T extends object> extends Omit<AriaSelectProps<T>, 'children' | 'label' | 'description' | 'errorMessage' | 'validationState' | 'items'>, RenderProps<SelectRenderProps>, SlotProps {}
59
62
 
60
- interface SelectValueContext {
61
- state: SelectState<unknown>,
62
- valueProps: HTMLAttributes<HTMLElement>,
63
- placeholder?: string
64
- }
65
-
66
63
  export const SelectContext = createContext<ContextValue<SelectProps<any>, HTMLDivElement>>(null);
67
- const InternalSelectContext = createContext<SelectValueContext | null>(null);
64
+ export const SelectStateContext = createContext<SelectState<unknown> | null>(null);
68
65
 
69
66
  function Select<T extends object>(props: SelectProps<T>, ref: ForwardedRef<HTMLDivElement>) {
70
67
  [props, ref] = useContextProps(props, ref, SelectContext);
@@ -97,7 +94,7 @@ function Select<T extends object>(props: SelectProps<T>, ref: ForwardedRef<HTMLD
97
94
  menuProps,
98
95
  descriptionProps,
99
96
  errorMessageProps
100
- } = useSelect({...props, label}, state, buttonRef);
97
+ } = useSelect({...removeDataAttributes(props), label}, state, buttonRef);
101
98
 
102
99
  // Make menu width match input + button
103
100
  let [buttonWidth, setButtonWidth] = useState<string | null>(null);
@@ -128,24 +125,28 @@ function Select<T extends object>(props: SelectProps<T>, ref: ForwardedRef<HTMLD
128
125
  <Hidden>
129
126
  <Provider
130
127
  values={[
131
- [InternalSelectContext, {state, valueProps, placeholder: props.placeholder}],
132
- [ListBoxContext, {document}]
128
+ [SelectContext, props],
129
+ [SelectStateContext, state],
130
+ [CollectionDocumentContext, document]
133
131
  ]}>
134
132
  {renderProps.children}
135
133
  </Provider>
136
134
  </Hidden>
137
135
  <Provider
138
136
  values={[
139
- [InternalSelectContext, {state, valueProps, placeholder: props.placeholder}],
137
+ [SelectContext, props],
138
+ [SelectStateContext, state],
139
+ [SelectValueContext, valueProps],
140
140
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
141
141
  [ButtonContext, {...triggerProps, ref: buttonRef, isPressed: state.isOpen}],
142
+ [OverlayTriggerStateContext, state],
142
143
  [PopoverContext, {
143
- state,
144
144
  triggerRef: buttonRef,
145
145
  placement: 'bottom start',
146
146
  style: {'--trigger-width': buttonWidth} as React.CSSProperties
147
147
  }],
148
- [ListBoxContext, {state, ...menuProps}],
148
+ [ListBoxContext, menuProps],
149
+ [ListStateContext, state],
149
150
  [TextContext, {
150
151
  slots: {
151
152
  description: descriptionProps,
@@ -158,7 +159,7 @@ function Select<T extends object>(props: SelectProps<T>, ref: ForwardedRef<HTMLD
158
159
  {...renderProps}
159
160
  {...focusProps}
160
161
  ref={ref}
161
- slot={props.slot}
162
+ slot={props.slot || undefined}
162
163
  data-focused={state.isFocused || undefined}
163
164
  data-focus-visible={isFocusVisible || undefined}
164
165
  data-open={state.isOpen || undefined}
@@ -196,8 +197,12 @@ export interface SelectValueRenderProps<T> {
196
197
 
197
198
  export interface SelectValueProps<T extends object> extends Omit<HTMLAttributes<HTMLElement>, keyof RenderProps<unknown>>, RenderProps<SelectValueRenderProps<T>> {}
198
199
 
200
+ export const SelectValueContext = createContext<ContextValue<SelectValueProps<any>, HTMLSpanElement>>(null);
201
+
199
202
  function SelectValue<T extends object>(props: SelectValueProps<T>, ref: ForwardedRef<HTMLSpanElement>) {
200
- let {state, valueProps, placeholder} = useContext(InternalSelectContext)!;
203
+ [props, ref] = useContextProps(props, ref, SelectValueContext);
204
+ let state = useContext(SelectStateContext)!;
205
+ let {placeholder} = useSlottedContext(SelectContext)!;
201
206
  let selectedItem = state.selectedKey != null
202
207
  ? state.collection.getItem(state.selectedKey)
203
208
  : null;
@@ -217,10 +222,11 @@ function SelectValue<T extends object>(props: SelectValueProps<T>, ref: Forwarde
217
222
  });
218
223
  }
219
224
 
225
+ let stringFormatter = useLocalizedStringFormatter(intlMessages);
226
+
220
227
  let renderProps = useRenderProps({
221
228
  ...props,
222
- // TODO: localize this.
223
- defaultChildren: rendered || placeholder || 'Select an item',
229
+ defaultChildren: rendered || placeholder || stringFormatter.format('selectPlaceholder'),
224
230
  defaultClassName: 'react-aria-SelectValue',
225
231
  values: {
226
232
  selectedItem: state.selectedItem?.value as T ?? null,
@@ -230,10 +236,9 @@ function SelectValue<T extends object>(props: SelectValueProps<T>, ref: Forwarde
230
236
  });
231
237
 
232
238
  let DOMProps = filterDOMProps(props);
233
- delete DOMProps.id;
234
239
 
235
240
  return (
236
- <span ref={ref} {...DOMProps} {...valueProps} {...renderProps} data-placeholder={!selectedItem || undefined}>
241
+ <span ref={ref} {...DOMProps} {...renderProps} data-placeholder={!selectedItem || undefined}>
237
242
  {/* clear description and error message slots */}
238
243
  <TextContext.Provider value={undefined}>
239
244
  {renderProps.children}
package/src/Separator.tsx CHANGED
@@ -45,7 +45,7 @@ function Separator(originalProps: SeparatorProps, originalRef: ForwardedRef<HTML
45
45
  style={style}
46
46
  className={className ?? 'react-aria-Separator'}
47
47
  ref={ref}
48
- slot={props.slot} />
48
+ slot={props.slot || undefined} />
49
49
  );
50
50
  }
51
51
 
package/src/Slider.tsx CHANGED
@@ -11,11 +11,10 @@
11
11
  */
12
12
 
13
13
  import {AriaSliderProps, AriaSliderThumbProps, mergeProps, Orientation, useFocusRing, useHover, useNumberFormatter, useSlider, useSliderThumb, VisuallyHidden} from 'react-aria';
14
- import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot} from './utils';
15
- import {DOMAttributes} from '@react-types/shared';
16
- import {filterDOMProps, mergeRefs} from '@react-aria/utils';
14
+ import {ContextValue, forwardRefType, Provider, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
15
+ import {filterDOMProps} from '@react-aria/utils';
17
16
  import {LabelContext} from './Label';
18
- import React, {createContext, ForwardedRef, forwardRef, OutputHTMLAttributes, RefObject, useContext, useRef} from 'react';
17
+ import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, OutputHTMLAttributes, RefObject, useContext, useRef} from 'react';
19
18
  import {SliderState, useSliderState} from 'react-stately';
20
19
 
21
20
  export interface SliderProps<T = number | number[]> extends Omit<AriaSliderProps<T>, 'label'>, RenderProps<SliderRenderProps>, SlotProps {
@@ -25,15 +24,10 @@ export interface SliderProps<T = number | number[]> extends Omit<AriaSliderProps
25
24
  formatOptions?: Intl.NumberFormatOptions
26
25
  }
27
26
 
28
- interface SliderContextValue {
29
- state: SliderState,
30
- trackProps: DOMAttributes,
31
- outputProps: OutputHTMLAttributes<HTMLOutputElement>,
32
- trackRef: RefObject<HTMLDivElement>
33
- }
34
-
35
27
  export const SliderContext = createContext<ContextValue<SliderProps, HTMLDivElement>>(null);
36
- const InternalSliderContext = createContext<SliderContextValue | null>(null);
28
+ export const SliderStateContext = createContext<SliderState | null>(null);
29
+ export const SliderTrackContext = createContext<ContextValue<SliderTrackContextValue, HTMLDivElement>>(null);
30
+ export const SliderOutputContext = createContext<ContextValue<SliderOutputContextValue, HTMLOutputElement>>(null);
37
31
 
38
32
  export interface SliderRenderProps {
39
33
  /**
@@ -81,7 +75,9 @@ function Slider<T extends number | number[]>(props: SliderProps<T>, ref: Forward
81
75
  return (
82
76
  <Provider
83
77
  values={[
84
- [InternalSliderContext, {state, trackProps, trackRef, outputProps}],
78
+ [SliderStateContext, state],
79
+ [SliderTrackContext, {...trackProps, ref: trackRef}],
80
+ [SliderOutputContext, outputProps],
85
81
  [LabelContext, {...labelProps, ref: labelRef}]
86
82
  ]}>
87
83
  <div
@@ -89,7 +85,7 @@ function Slider<T extends number | number[]>(props: SliderProps<T>, ref: Forward
89
85
  {...groupProps}
90
86
  {...renderProps}
91
87
  ref={ref}
92
- slot={props.slot}
88
+ slot={props.slot || undefined}
93
89
  data-orientation={state.orientation}
94
90
  data-disabled={state.isDisabled || undefined} />
95
91
  </Provider>
@@ -103,9 +99,12 @@ const _Slider = /*#__PURE__*/ (forwardRef as forwardRefType)(Slider);
103
99
  export {_Slider as Slider};
104
100
 
105
101
  export interface SliderOutputProps extends RenderProps<SliderRenderProps> {}
102
+ interface SliderOutputContextValue extends Omit<OutputHTMLAttributes<HTMLOutputElement>, 'children' | 'className' | 'style'>, SliderOutputProps {}
106
103
 
107
- function SliderOutput({children, style, className, ...otherProps}: SliderOutputProps, ref: ForwardedRef<HTMLOutputElement>) {
108
- let {state, outputProps} = useContext(InternalSliderContext)!;
104
+ function SliderOutput(props: SliderOutputProps, ref: ForwardedRef<HTMLOutputElement>) {
105
+ [props, ref] = useContextProps(props, ref, SliderOutputContext);
106
+ let {children, style, className, ...otherProps} = props;
107
+ let state = useContext(SliderStateContext)!;
109
108
  let renderProps = useRenderProps({
110
109
  className,
111
110
  style,
@@ -121,7 +120,7 @@ function SliderOutput({children, style, className, ...otherProps}: SliderOutputP
121
120
 
122
121
  return (
123
122
  <output
124
- {...mergeProps(filterDOMProps(otherProps as any), outputProps)}
123
+ {...otherProps}
125
124
  {...renderProps}
126
125
  ref={ref}
127
126
  data-orientation={state.orientation || undefined}
@@ -144,11 +143,12 @@ export interface SliderTrackRenderProps extends SliderRenderProps {
144
143
  }
145
144
 
146
145
  export interface SliderTrackProps extends RenderProps<SliderTrackRenderProps> {}
146
+ interface SliderTrackContextValue extends Omit<HTMLAttributes<HTMLElement>, 'children' | 'className' | 'style'>, SliderTrackProps {}
147
147
 
148
148
  function SliderTrack(props: SliderTrackProps, ref: ForwardedRef<HTMLDivElement>) {
149
- let {state, trackProps, trackRef} = useContext(InternalSliderContext)!;
149
+ [props, ref] = useContextProps(props, ref, SliderTrackContext);
150
+ let state = useContext(SliderStateContext)!;
150
151
  let {hoverProps, isHovered} = useHover({});
151
- let domRef = mergeRefs(ref, trackRef);
152
152
  let renderProps = useRenderProps({
153
153
  ...props,
154
154
  defaultClassName: 'react-aria-SliderTrack',
@@ -162,9 +162,9 @@ function SliderTrack(props: SliderTrackProps, ref: ForwardedRef<HTMLDivElement>)
162
162
 
163
163
  return (
164
164
  <div
165
- {...mergeProps(filterDOMProps(props as any), hoverProps, trackProps)}
165
+ {...mergeProps(props, hoverProps)}
166
166
  {...renderProps}
167
- ref={domRef}
167
+ ref={ref}
168
168
  data-hovered={isHovered || undefined}
169
169
  data-orientation={state.orientation || undefined}
170
170
  data-disabled={state.isDisabled || undefined} />
@@ -212,14 +212,15 @@ export interface SliderThumbRenderProps {
212
212
  export interface SliderThumbProps extends Omit<AriaSliderThumbProps, 'validationState'>, RenderProps<SliderThumbRenderProps> {}
213
213
 
214
214
  function SliderThumb(props: SliderThumbProps, ref: ForwardedRef<HTMLDivElement>) {
215
- let {state, trackRef} = useContext(InternalSliderContext)!;
215
+ let state = useContext(SliderStateContext)!;
216
+ let {ref: trackRef} = useSlottedContext(SliderTrackContext)!;
216
217
  let {index = 0} = props;
217
218
  let inputRef = useRef<HTMLInputElement>(null);
218
219
  let [labelRef, label] = useSlot();
219
220
  let {thumbProps, inputProps, labelProps, isDragging, isFocused, isDisabled} = useSliderThumb({
220
221
  ...props,
221
222
  index,
222
- trackRef,
223
+ trackRef: trackRef as RefObject<HTMLDivElement>,
223
224
  inputRef,
224
225
  label
225
226
  }, state);
package/src/Switch.tsx CHANGED
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import {AriaSwitchProps, mergeProps, useFocusRing, useHover, usePress, useSwitch, VisuallyHidden} from 'react-aria';
14
- import {ContextValue, forwardRefType, RenderProps, SlotProps, useContextProps, useRenderProps} from './utils';
14
+ import {ContextValue, forwardRefType, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps} from './utils';
15
15
  import {filterDOMProps} from '@react-aria/utils';
16
16
  import React, {createContext, ForwardedRef, forwardRef, useState} from 'react';
17
17
  import {ToggleState, useToggleState} from 'react-stately';
@@ -66,7 +66,7 @@ function Switch(props: SwitchProps, ref: ForwardedRef<HTMLInputElement>) {
66
66
  [props, ref] = useContextProps(props, ref, SwitchContext);
67
67
  let state = useToggleState(props);
68
68
  let {inputProps, isSelected, isDisabled, isReadOnly, isPressed: isPressedKeyboard} = useSwitch({
69
- ...props,
69
+ ...removeDataAttributes(props),
70
70
  // ReactNode type doesn't allow function children.
71
71
  children: typeof props.children === 'function' ? true : props.children
72
72
  }, state, ref);
@@ -117,7 +117,7 @@ function Switch(props: SwitchProps, ref: ForwardedRef<HTMLInputElement>) {
117
117
  return (
118
118
  <label
119
119
  {...mergeProps(DOMProps, pressProps, hoverProps, renderProps)}
120
- slot={props.slot}
120
+ slot={props.slot || undefined}
121
121
  data-selected={isSelected || undefined}
122
122
  data-pressed={pressed || undefined}
123
123
  data-hovered={isHovered || undefined}
package/src/Table.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import {AriaLabelingProps} from '@react-types/shared';
1
+ import {AriaLabelingProps, LinkDOMProps} from '@react-types/shared';
2
2
  import {BaseCollection, CollectionContext, CollectionProps, CollectionRendererContext, ItemRenderProps, NodeValue, useCachedChildren, useCollection, useCollectionChildren, useSSRCollectionNode} from './Collection';
3
3
  import {buildHeaderRows, TableColumnResizeState} from '@react-stately/table';
4
4
  import {ButtonContext} from './Button';
@@ -6,10 +6,12 @@ import {CheckboxContext} from './Checkbox';
6
6
  import {ColumnSize, ColumnStaticSize, TableCollection as ITableCollection, TableProps as SharedTableProps} from '@react-types/table';
7
7
  import {ContextValue, defaultSlot, DOMProps, forwardRefType, Provider, RenderProps, SlotProps, StyleProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
8
8
  import {DisabledBehavior, DraggableCollectionState, DroppableCollectionState, Node, SelectionBehavior, SelectionMode, SortDirection, TableState, useTableColumnResizeState, useTableState} from 'react-stately';
9
- import {DragAndDropHooks, DropIndicator, DropIndicatorContext, DropIndicatorProps} from './useDragAndDrop';
10
- import {DraggableItemResult, DragPreviewRenderer, DropIndicatorAria, DroppableCollectionResult, FocusScope, ListKeyboardDelegate, mergeProps, useFocusRing, useHover, useLocale, useTable, useTableCell, useTableColumnHeader, useTableColumnResize, useTableHeaderRow, useTableRow, useTableRowGroup, useTableSelectAllCheckbox, useTableSelectionCheckbox, useVisuallyHidden} from 'react-aria';
9
+ import {DragAndDropContext, DragAndDropHooks, DropIndicator, DropIndicatorContext, DropIndicatorProps} from './useDragAndDrop';
10
+ import {DraggableItemResult, DragPreviewRenderer, DropIndicatorAria, DroppableCollectionResult, FocusScope, ListKeyboardDelegate, mergeProps, useFocusRing, useHover, useLocale, useLocalizedStringFormatter, useTable, useTableCell, useTableColumnHeader, useTableColumnResize, useTableHeaderRow, useTableRow, useTableRowGroup, useTableSelectAllCheckbox, useTableSelectionCheckbox, useVisuallyHidden} from 'react-aria';
11
11
  import {filterDOMProps, useLayoutEffect, useObjectRef, useResizeObserver} from '@react-aria/utils';
12
12
  import {GridNode} from '@react-types/grid';
13
+ // @ts-ignore
14
+ import intlMessages from '../intl/*.json';
13
15
  import React, {createContext, ForwardedRef, forwardRef, Key, ReactElement, ReactNode, RefObject, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
14
16
  import ReactDOM from 'react-dom';
15
17
 
@@ -35,11 +37,11 @@ class TableCollection<T> extends BaseCollection<T> implements ITableCollection<T
35
37
  }
36
38
 
37
39
  commit(firstKey: Key, lastKey: Key, isSSR = false) {
38
- this.updateColumns();
40
+ this.updateColumns(isSSR);
39
41
  super.commit(firstKey, lastKey, isSSR);
40
42
  }
41
43
 
42
- private updateColumns() {
44
+ private updateColumns(isSSR: boolean) {
43
45
  if (!this.columnsDirty) {
44
46
  return;
45
47
  }
@@ -73,7 +75,7 @@ class TableCollection<T> extends BaseCollection<T> implements ITableCollection<T
73
75
 
74
76
  this.headerRows = buildHeaderRows(columnKeyMap, this.columns);
75
77
  this.columnsDirty = false;
76
- if (this.rowHeaderColumnKeys.size === 0 && this.columns.length > 0) {
78
+ if (this.rowHeaderColumnKeys.size === 0 && this.columns.length > 0 && !isSSR) {
77
79
  throw new Error('A table must have at least one Column with the isRowHeader prop set to true');
78
80
  }
79
81
  }
@@ -244,16 +246,9 @@ function ResizableTableContainer(props: ResizableTableContainerProps, ref: Forwa
244
246
  const _ResizableTableContainer = forwardRef(ResizableTableContainer);
245
247
  export {_ResizableTableContainer as ResizableTableContainer};
246
248
 
247
- interface InternalTableContextValue {
248
- state: TableState<unknown>,
249
- dragAndDropHooks?: DragAndDropHooks,
250
- dragState?: DraggableCollectionState,
251
- dropState?: DroppableCollectionState,
252
- layoutState?: TableColumnResizeState<unknown>
253
- }
254
-
255
249
  export const TableContext = createContext<ContextValue<TableProps, HTMLTableElement>>(null);
256
- const InternalTableContext = createContext<InternalTableContextValue | null>(null);
250
+ export const TableStateContext = createContext<TableState<unknown> | null>(null);
251
+ export const TableColumnResizeStateContext = createContext<TableColumnResizeState<unknown> | null>(null);
257
252
 
258
253
  export interface TableRenderProps {
259
254
  /**
@@ -389,7 +384,7 @@ function Table(props: TableProps, ref: ForwardedRef<HTMLTableElement>) {
389
384
 
390
385
  let style = renderProps.style;
391
386
  let tableContainerContext = useContext(ResizableTableContainerContext);
392
- let layoutState: TableColumnResizeState<unknown> | undefined = undefined;
387
+ let layoutState: TableColumnResizeState<unknown> | null = null;
393
388
  if (tableContainerContext) {
394
389
  layoutState = tableContainerContext.useTableColumnResizeState({
395
390
  tableWidth: tableContainerContext.tableWidth
@@ -408,7 +403,9 @@ function Table(props: TableProps, ref: ForwardedRef<HTMLTableElement>) {
408
403
  </TableOptionsContext.Provider>
409
404
  <Provider
410
405
  values={[
411
- [InternalTableContext, {state, dragAndDropHooks, dragState, dropState, layoutState}],
406
+ [TableStateContext, state],
407
+ [TableColumnResizeStateContext, layoutState],
408
+ [DragAndDropContext, {dragAndDropHooks, dragState, dropState}],
412
409
  [DropIndicatorContext, {render: TableDropIndicatorWrapper}]
413
410
  ]}>
414
411
  <FocusScope>
@@ -418,7 +415,7 @@ function Table(props: TableProps, ref: ForwardedRef<HTMLTableElement>) {
418
415
  {...mergeProps(gridProps, focusProps, droppableCollection?.collectionProps)}
419
416
  style={style}
420
417
  ref={ref}
421
- slot={props.slot}
418
+ slot={props.slot || undefined}
422
419
  data-allows-dragging={isListDraggable || undefined}
423
420
  data-drop-target={isRootDropTarget || undefined}
424
421
  data-focused={isFocused || undefined}
@@ -599,7 +596,7 @@ export {_TableBody as TableBody};
599
596
 
600
597
  export interface RowRenderProps extends ItemRenderProps {}
601
598
 
602
- export interface RowProps<T> extends StyleRenderProps<RowRenderProps> {
599
+ export interface RowProps<T> extends StyleRenderProps<RowRenderProps>, LinkDOMProps {
603
600
  id?: Key,
604
601
  /** A list of columns used when dynamically rendering cells. */
605
602
  columns?: Iterable<T>,
@@ -747,7 +744,7 @@ function TableBodyRowGroup<T>({collection, isDroppable}: {collection: TableColle
747
744
 
748
745
  function TableHeaderRow<T>({item}: {item: GridNode<T>}) {
749
746
  let ref = useRef<HTMLTableRowElement>(null);
750
- let {state} = useContext(InternalTableContext)!;
747
+ let state = useContext(TableStateContext)!;
751
748
  let {rowProps} = useTableHeaderRow({node: item}, state, ref);
752
749
  let {checkboxProps} = useTableSelectAllCheckbox(state);
753
750
 
@@ -788,7 +785,7 @@ const ColumnResizerContext = createContext<ColumnResizerContextValue | null>(nul
788
785
 
789
786
  function TableColumnHeader<T>({column}: {column: GridNode<T>}) {
790
787
  let ref = useObjectRef<HTMLTableHeaderCellElement>(column.props.ref);
791
- let {state} = useContext(InternalTableContext)!;
788
+ let state = useContext(TableStateContext)!;
792
789
  let {columnHeaderProps} = useTableColumnHeader(
793
790
  {node: column},
794
791
  state,
@@ -797,7 +794,7 @@ function TableColumnHeader<T>({column}: {column: GridNode<T>}) {
797
794
  let {isFocused, isFocusVisible, focusProps} = useFocusRing();
798
795
  let {hoverProps, isHovered} = useHover({});
799
796
 
800
- let {layoutState} = useContext(InternalTableContext)!;
797
+ let layoutState = useContext(TableColumnResizeStateContext);
801
798
  let isResizing = false;
802
799
  if (layoutState) {
803
800
  isResizing = layoutState.resizingColumn === column.key;
@@ -896,10 +893,11 @@ export interface ColumnResizerProps extends RenderProps<ColumnResizerRenderProps
896
893
  }
897
894
 
898
895
  function ColumnResizer(props: ColumnResizerProps, ref: ForwardedRef<HTMLDivElement>) {
899
- let {layoutState} = useContext(InternalTableContext)!;
896
+ let layoutState = useContext(TableColumnResizeStateContext);
900
897
  if (!layoutState) {
901
898
  throw new Error('Wrap your <Table> in a <ResizableTableContainer> to enable column resizing');
902
899
  }
900
+ let stringFormatter = useLocalizedStringFormatter(intlMessages);
903
901
 
904
902
  let {onResizeStart, onResize, onResizeEnd} = useContext(ResizableTableContainerContext)!;
905
903
  let {column, triggerRef} = useContext(ColumnResizerContext)!;
@@ -907,8 +905,7 @@ function ColumnResizer(props: ColumnResizerProps, ref: ForwardedRef<HTMLDivEleme
907
905
  let {resizerProps, inputProps, isResizing} = useTableColumnResize(
908
906
  {
909
907
  column,
910
- // TODO: translate
911
- 'aria-label': props['aria-label'] || 'Resizer',
908
+ 'aria-label': props['aria-label'] || stringFormatter.format('tableResizer'),
912
909
  onResizeStart,
913
910
  onResize,
914
911
  onResizeEnd,
@@ -986,7 +983,8 @@ export {_ColumnResizer as ColumnResizer};
986
983
 
987
984
  function TableRow<T>({item}: {item: GridNode<T>}) {
988
985
  let ref = useObjectRef<HTMLTableRowElement>(item.props.ref);
989
- let {state, dragAndDropHooks, dragState, dropState} = useContext(InternalTableContext)!;
986
+ let state = useContext(TableStateContext)!;
987
+ let {dragAndDropHooks, dragState, dropState} = useContext(DragAndDropContext);
990
988
  let {rowProps, ...states} = useTableRow(
991
989
  {
992
990
  node: item,
@@ -1115,7 +1113,8 @@ function TableRow<T>({item}: {item: GridNode<T>}) {
1115
1113
 
1116
1114
  function TableCell<T>({cell}: {cell: GridNode<T>}) {
1117
1115
  let ref = useObjectRef<HTMLTableCellElement>(cell.props.ref);
1118
- let {state, dragState} = useContext(InternalTableContext)!;
1116
+ let state = useContext(TableStateContext)!;
1117
+ let {dragState} = useContext(DragAndDropContext);
1119
1118
 
1120
1119
  // @ts-ignore
1121
1120
  cell.column = state.collection.columns[cell.index];
@@ -1155,7 +1154,7 @@ function TableCell<T>({cell}: {cell: GridNode<T>}) {
1155
1154
 
1156
1155
  function TableDropIndicatorWrapper(props: DropIndicatorProps, ref: ForwardedRef<HTMLElement>) {
1157
1156
  ref = useObjectRef(ref);
1158
- let {dragAndDropHooks, dropState} = useContext(InternalTableContext)!;
1157
+ let {dragAndDropHooks, dropState} = useContext(DragAndDropContext)!;
1159
1158
  let buttonRef = useRef<HTMLDivElement>(null);
1160
1159
  let {dropIndicatorProps, isHidden, isDropTarget} = dragAndDropHooks!.useDropIndicator!(
1161
1160
  props,
@@ -1186,7 +1185,7 @@ function TableDropIndicator(props: TableDropIndicatorProps, ref: ForwardedRef<HT
1186
1185
  ...otherProps
1187
1186
  } = props;
1188
1187
 
1189
- let {state} = useContext(InternalTableContext)!;
1188
+ let state = useContext(TableStateContext)!;
1190
1189
  let {visuallyHiddenProps} = useVisuallyHidden();
1191
1190
  let renderProps = useRenderProps({
1192
1191
  ...otherProps,
@@ -1216,7 +1215,8 @@ function TableDropIndicator(props: TableDropIndicatorProps, ref: ForwardedRef<HT
1216
1215
  const TableDropIndicatorForwardRef = forwardRef(TableDropIndicator);
1217
1216
 
1218
1217
  function RootDropIndicator() {
1219
- let {state, dragAndDropHooks, dropState} = useContext(InternalTableContext)!;
1218
+ let state = useContext(TableStateContext)!;
1219
+ let {dragAndDropHooks, dropState} = useContext(DragAndDropContext);
1220
1220
  let ref = useRef<HTMLDivElement>(null);
1221
1221
  let {dropIndicatorProps} = dragAndDropHooks!.useDropIndicator!({
1222
1222
  target: {type: 'root'}