react-aria-components 1.7.1 → 1.9.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.
Files changed (133) hide show
  1. package/dist/Autocomplete.main.js.map +1 -1
  2. package/dist/Autocomplete.module.js.map +1 -1
  3. package/dist/Calendar.main.js +5 -7
  4. package/dist/Calendar.main.js.map +1 -1
  5. package/dist/Calendar.mjs +6 -8
  6. package/dist/Calendar.module.js +6 -8
  7. package/dist/Calendar.module.js.map +1 -1
  8. package/dist/Collection.main.js +1 -1
  9. package/dist/Collection.main.js.map +1 -1
  10. package/dist/Collection.mjs +1 -1
  11. package/dist/Collection.module.js +1 -1
  12. package/dist/Collection.module.js.map +1 -1
  13. package/dist/ColorPicker.main.js.map +1 -1
  14. package/dist/ColorPicker.module.js.map +1 -1
  15. package/dist/ColorWheel.main.js +3 -1
  16. package/dist/ColorWheel.main.js.map +1 -1
  17. package/dist/ColorWheel.mjs +3 -1
  18. package/dist/ColorWheel.module.js +3 -1
  19. package/dist/ColorWheel.module.js.map +1 -1
  20. package/dist/DateField.main.js +16 -9
  21. package/dist/DateField.main.js.map +1 -1
  22. package/dist/DateField.mjs +16 -9
  23. package/dist/DateField.module.js +16 -9
  24. package/dist/DateField.module.js.map +1 -1
  25. package/dist/Dialog.main.js +16 -2
  26. package/dist/Dialog.main.js.map +1 -1
  27. package/dist/Dialog.mjs +18 -4
  28. package/dist/Dialog.module.js +18 -4
  29. package/dist/Dialog.module.js.map +1 -1
  30. package/dist/DragAndDrop.main.js.map +1 -1
  31. package/dist/DragAndDrop.module.js.map +1 -1
  32. package/dist/GridList.main.js +61 -5
  33. package/dist/GridList.main.js.map +1 -1
  34. package/dist/GridList.mjs +62 -7
  35. package/dist/GridList.module.js +62 -7
  36. package/dist/GridList.module.js.map +1 -1
  37. package/dist/ListBox.main.js +63 -4
  38. package/dist/ListBox.main.js.map +1 -1
  39. package/dist/ListBox.mjs +64 -6
  40. package/dist/ListBox.module.js +64 -6
  41. package/dist/ListBox.module.js.map +1 -1
  42. package/dist/Menu.main.js.map +1 -1
  43. package/dist/Menu.module.js.map +1 -1
  44. package/dist/Modal.main.js.map +1 -1
  45. package/dist/Modal.module.js.map +1 -1
  46. package/dist/OverlayArrow.main.js +4 -1
  47. package/dist/OverlayArrow.main.js.map +1 -1
  48. package/dist/OverlayArrow.mjs +4 -1
  49. package/dist/OverlayArrow.module.js +4 -1
  50. package/dist/OverlayArrow.module.js.map +1 -1
  51. package/dist/Popover.main.js +1 -1
  52. package/dist/Popover.main.js.map +1 -1
  53. package/dist/Popover.mjs +1 -1
  54. package/dist/Popover.module.js +1 -1
  55. package/dist/Popover.module.js.map +1 -1
  56. package/dist/Select.main.js.map +1 -1
  57. package/dist/Select.module.js.map +1 -1
  58. package/dist/Separator.main.js +3 -2
  59. package/dist/Separator.main.js.map +1 -1
  60. package/dist/Separator.mjs +3 -2
  61. package/dist/Separator.module.js +3 -2
  62. package/dist/Separator.module.js.map +1 -1
  63. package/dist/Table.main.js +45 -12
  64. package/dist/Table.main.js.map +1 -1
  65. package/dist/Table.mjs +46 -13
  66. package/dist/Table.module.js +46 -13
  67. package/dist/Table.module.js.map +1 -1
  68. package/dist/TableLayout.main.js.map +1 -1
  69. package/dist/TableLayout.module.js.map +1 -1
  70. package/dist/TagGroup.main.js +2 -2
  71. package/dist/TagGroup.main.js.map +1 -1
  72. package/dist/TagGroup.mjs +2 -2
  73. package/dist/TagGroup.module.js +2 -2
  74. package/dist/TagGroup.module.js.map +1 -1
  75. package/dist/TextArea.main.js.map +1 -1
  76. package/dist/TextArea.module.js.map +1 -1
  77. package/dist/Toast.main.js +45 -16
  78. package/dist/Toast.main.js.map +1 -1
  79. package/dist/Toast.mjs +45 -18
  80. package/dist/Toast.module.js +45 -18
  81. package/dist/Toast.module.js.map +1 -1
  82. package/dist/Tooltip.main.js.map +1 -1
  83. package/dist/Tooltip.module.js.map +1 -1
  84. package/dist/Tree.main.js +2 -2
  85. package/dist/Tree.main.js.map +1 -1
  86. package/dist/Tree.mjs +2 -2
  87. package/dist/Tree.module.js +2 -2
  88. package/dist/Tree.module.js.map +1 -1
  89. package/dist/Virtualizer.main.js +0 -1
  90. package/dist/Virtualizer.main.js.map +1 -1
  91. package/dist/Virtualizer.mjs +0 -1
  92. package/dist/Virtualizer.module.js +0 -1
  93. package/dist/Virtualizer.module.js.map +1 -1
  94. package/dist/import.mjs +6 -6
  95. package/dist/main.js +6 -1
  96. package/dist/main.js.map +1 -1
  97. package/dist/module.js +6 -6
  98. package/dist/module.js.map +1 -1
  99. package/dist/types.d.ts +121 -45
  100. package/dist/types.d.ts.map +1 -1
  101. package/dist/utils.main.js.map +1 -1
  102. package/dist/utils.module.js.map +1 -1
  103. package/i18n/es-ES.js +1 -1
  104. package/i18n/es-ES.mjs +1 -1
  105. package/i18n/index.js +1 -1
  106. package/i18n/index.mjs +1 -1
  107. package/package.json +27 -25
  108. package/src/Autocomplete.tsx +2 -2
  109. package/src/Calendar.tsx +23 -11
  110. package/src/Collection.tsx +5 -2
  111. package/src/ColorPicker.tsx +2 -2
  112. package/src/ColorWheel.tsx +3 -1
  113. package/src/DateField.tsx +13 -9
  114. package/src/Dialog.tsx +19 -5
  115. package/src/DragAndDrop.tsx +4 -2
  116. package/src/GridList.tsx +73 -8
  117. package/src/ListBox.tsx +74 -7
  118. package/src/Menu.tsx +2 -1
  119. package/src/Modal.tsx +1 -0
  120. package/src/OverlayArrow.tsx +6 -3
  121. package/src/Popover.tsx +2 -1
  122. package/src/Select.tsx +7 -1
  123. package/src/Separator.tsx +3 -2
  124. package/src/Table.tsx +52 -19
  125. package/src/TableLayout.ts +1 -1
  126. package/src/TagGroup.tsx +2 -2
  127. package/src/TextArea.tsx +1 -1
  128. package/src/Toast.tsx +51 -13
  129. package/src/Tooltip.tsx +3 -2
  130. package/src/Tree.tsx +2 -2
  131. package/src/Virtualizer.tsx +3 -7
  132. package/src/index.ts +8 -8
  133. package/src/utils.tsx +8 -1
package/i18n/index.mjs CHANGED
@@ -83,7 +83,7 @@ function getLocalizationScript(locale, dict = dictionary) {
83
83
  return getPackageLocalizationScript(locale, strings);
84
84
  }
85
85
 
86
- let deps = {"@react-aria/autocomplete":["@react-aria/combobox","@react-aria/searchfield"],"@react-aria/color":["@react-aria/numberfield","@react-aria/spinbutton","@react-stately/color"],"@react-aria/combobox":["@react-aria/menu","@react-aria/overlays"],"@react-aria/datepicker":["@react-aria/spinbutton","@react-stately/datepicker"],"@react-aria/dnd":["@react-aria/overlays"],"@react-aria/gridlist":["@react-aria/grid"],"@react-aria/menu":["@react-aria/overlays"],"@react-aria/numberfield":["@react-aria/spinbutton"],"@react-aria/table":["@react-aria/grid"],"@react-aria/tag":["@react-aria/gridlist"],"@react-aria/tree":["@react-aria/gridlist"],"react-aria-components":["@react-aria/autocomplete","@react-aria/dnd"]};
86
+ let deps = {"@react-aria/autocomplete":["@react-aria/combobox","@react-aria/searchfield"],"@react-aria/color":["@react-aria/numberfield","@react-aria/spinbutton","@react-stately/color"],"@react-aria/combobox":["@react-aria/menu","@react-aria/overlays"],"@react-aria/datepicker":["@react-aria/spinbutton","@react-stately/datepicker"],"@react-aria/dnd":["@react-aria/overlays"],"@react-aria/gridlist":["@react-aria/grid"],"@react-aria/menu":["@react-aria/overlays"],"@react-aria/numberfield":["@react-aria/spinbutton"],"@react-aria/table":["@react-aria/grid"],"@react-aria/tag":["@react-aria/gridlist"],"@react-aria/tree":["@react-aria/gridlist"],"react-aria-components":["@react-aria/autocomplete","@react-aria/dnd","@react-aria/overlays"]};
87
87
 
88
88
  function createLocalizedStringDictionary(packages) {
89
89
  let strings = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-aria-components",
3
- "version": "1.7.1",
3
+ "version": "1.9.0",
4
4
  "description": "A library of styleable components built using React Aria",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -37,31 +37,33 @@
37
37
  "url": "https://github.com/adobe/react-spectrum"
38
38
  },
39
39
  "dependencies": {
40
- "@internationalized/date": "^3.7.0",
41
- "@internationalized/string": "^3.2.5",
42
- "@react-aria/autocomplete": "3.0.0-beta.1",
43
- "@react-aria/collections": "3.0.0-beta.1",
44
- "@react-aria/dnd": "^3.9.1",
45
- "@react-aria/focus": "^3.20.1",
46
- "@react-aria/interactions": "^3.24.1",
47
- "@react-aria/live-announcer": "^3.4.1",
48
- "@react-aria/toolbar": "3.0.0-beta.14",
49
- "@react-aria/utils": "^3.28.1",
50
- "@react-aria/virtualizer": "^4.1.3",
51
- "@react-stately/autocomplete": "3.0.0-beta.0",
52
- "@react-stately/layout": "^4.2.1",
53
- "@react-stately/selection": "^3.20.0",
54
- "@react-stately/table": "^3.14.0",
55
- "@react-stately/utils": "^3.10.5",
56
- "@react-stately/virtualizer": "^4.3.1",
57
- "@react-types/form": "^3.7.10",
58
- "@react-types/grid": "^3.3.0",
59
- "@react-types/shared": "^3.28.0",
60
- "@react-types/table": "^3.11.0",
40
+ "@internationalized/date": "^3.8.1",
41
+ "@internationalized/string": "^3.2.6",
42
+ "@react-aria/autocomplete": "3.0.0-beta.3",
43
+ "@react-aria/collections": "3.0.0-rc.1",
44
+ "@react-aria/dnd": "^3.9.3",
45
+ "@react-aria/focus": "^3.20.3",
46
+ "@react-aria/interactions": "^3.25.1",
47
+ "@react-aria/live-announcer": "^3.4.2",
48
+ "@react-aria/overlays": "^3.27.1",
49
+ "@react-aria/ssr": "^3.9.8",
50
+ "@react-aria/toolbar": "3.0.0-beta.16",
51
+ "@react-aria/utils": "^3.29.0",
52
+ "@react-aria/virtualizer": "^4.1.5",
53
+ "@react-stately/autocomplete": "3.0.0-beta.1",
54
+ "@react-stately/layout": "^4.3.0",
55
+ "@react-stately/selection": "^3.20.2",
56
+ "@react-stately/table": "^3.14.2",
57
+ "@react-stately/utils": "^3.10.6",
58
+ "@react-stately/virtualizer": "^4.4.0",
59
+ "@react-types/form": "^3.7.12",
60
+ "@react-types/grid": "^3.3.2",
61
+ "@react-types/shared": "^3.29.1",
62
+ "@react-types/table": "^3.13.0",
61
63
  "@swc/helpers": "^0.5.0",
62
64
  "client-only": "^0.0.1",
63
- "react-aria": "^3.38.1",
64
- "react-stately": "^3.36.1",
65
+ "react-aria": "^3.40.0",
66
+ "react-stately": "^3.38.0",
65
67
  "use-sync-external-store": "^1.4.0"
66
68
  },
67
69
  "peerDependencies": {
@@ -74,5 +76,5 @@
74
76
  "devDependencies": {
75
77
  "@tailwindcss/postcss": "^4.0.0"
76
78
  },
77
- "gitHead": "9c4ebbc0c1972cc880febc29de995ca58caa3ba4"
79
+ "gitHead": "9c77d4e8267ed39469c65f65da94ece7be509874"
78
80
  }
@@ -15,7 +15,7 @@ import {AutocompleteState, useAutocompleteState} from '@react-stately/autocomple
15
15
  import {InputContext} from './Input';
16
16
  import {mergeProps} from '@react-aria/utils';
17
17
  import {Provider, removeDataAttributes, SlotProps, SlottedContextValue, useSlottedContext} from './utils';
18
- import React, {createContext, RefObject, useRef} from 'react';
18
+ import React, {createContext, JSX, RefObject, useRef} from 'react';
19
19
  import {SearchFieldContext} from './SearchField';
20
20
  import {TextFieldContext} from './TextField';
21
21
 
@@ -36,7 +36,7 @@ export const UNSTABLE_InternalAutocompleteContext = createContext<InternalAutoco
36
36
  /**
37
37
  * An autocomplete combines a TextField or SearchField with a Menu or ListBox, allowing users to search or filter a list of suggestions.
38
38
  */
39
- export function Autocomplete(props: AutocompleteProps) {
39
+ export function Autocomplete(props: AutocompleteProps): JSX.Element {
40
40
  let ctx = useSlottedContext(AutocompleteContext, props.slot);
41
41
  props = mergeProps(ctx, props);
42
42
  let {filter, disableAutoFocusFirst} = props;
package/src/Calendar.tsx CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  VisuallyHidden
25
25
  } from 'react-aria';
26
26
  import {ButtonContext} from './Button';
27
- import {CalendarDate, createCalendar, DateDuration, endOfMonth, getWeeksInMonth, isSameDay, isSameMonth} from '@internationalized/date';
27
+ import {CalendarDate, CalendarIdentifier, createCalendar, DateDuration, endOfMonth, Calendar as ICalendar, isSameDay, isSameMonth} from '@internationalized/date';
28
28
  import {CalendarState, RangeCalendarState, useCalendarState, useRangeCalendarState} from 'react-stately';
29
29
  import {ContextValue, DOMProps, Provider, RenderProps, SlotProps, StyleProps, useContextProps, useRenderProps, useSlottedContext} from './utils';
30
30
  import {DOMAttributes, FocusableElement, forwardRefType, HoverEvents} from '@react-types/shared';
@@ -62,7 +62,14 @@ export interface CalendarProps<T extends DateValue> extends Omit<AriaCalendarPro
62
62
  * The amount of days that will be displayed at once. This affects how pagination works.
63
63
  * @default {months: 1}
64
64
  */
65
- visibleDuration?: DateDuration
65
+ visibleDuration?: DateDuration,
66
+
67
+ /**
68
+ * A function to create a new [Calendar](https://react-spectrum.adobe.com/internationalized/date/Calendar.html)
69
+ * object for a given calendar identifier. If not provided, the `createCalendar` function
70
+ * from `@internationalized/date` will be used.
71
+ */
72
+ createCalendar?: (identifier: CalendarIdentifier) => ICalendar
66
73
  }
67
74
 
68
75
  export interface RangeCalendarProps<T extends DateValue> extends Omit<AriaRangeCalendarProps<T>, 'errorMessage' | 'validationState'>, RenderProps<RangeCalendarRenderProps>, SlotProps {
@@ -70,7 +77,14 @@ export interface RangeCalendarProps<T extends DateValue> extends Omit<AriaRangeC
70
77
  * The amount of days that will be displayed at once. This affects how pagination works.
71
78
  * @default {months: 1}
72
79
  */
73
- visibleDuration?: DateDuration
80
+ visibleDuration?: DateDuration,
81
+
82
+ /**
83
+ * A function to create a new [Calendar](https://react-spectrum.adobe.com/internationalized/date/Calendar.html)
84
+ * object for a given calendar identifier. If not provided, the `createCalendar` function
85
+ * from `@internationalized/date` will be used.
86
+ */
87
+ createCalendar?: (identifier: CalendarIdentifier) => ICalendar
74
88
  }
75
89
 
76
90
  export const CalendarContext = createContext<ContextValue<CalendarProps<any>, HTMLDivElement>>(null);
@@ -87,7 +101,7 @@ export const Calendar = /*#__PURE__*/ (forwardRef as forwardRefType)(function Ca
87
101
  let state = useCalendarState({
88
102
  ...props,
89
103
  locale,
90
- createCalendar
104
+ createCalendar: props.createCalendar || createCalendar
91
105
  });
92
106
 
93
107
  let {calendarProps, prevButtonProps, nextButtonProps, errorMessageProps, title} = useCalendar(props, state);
@@ -160,7 +174,7 @@ export const RangeCalendar = /*#__PURE__*/ (forwardRef as forwardRefType)(functi
160
174
  let state = useRangeCalendarState({
161
175
  ...props,
162
176
  locale,
163
- createCalendar
177
+ createCalendar: props.createCalendar || createCalendar
164
178
  });
165
179
 
166
180
  let {calendarProps, prevButtonProps, nextButtonProps, errorMessageProps, title} = useRangeCalendar(
@@ -329,7 +343,7 @@ interface InternalCalendarGridContextValue {
329
343
  headerProps: DOMAttributes<FocusableElement>,
330
344
  weekDays: string[],
331
345
  startDate: CalendarDate,
332
- firstDayOfWeek: 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | undefined
346
+ weeksInMonth: number
333
347
  }
334
348
 
335
349
  const InternalCalendarGridContext = createContext<InternalCalendarGridContextValue | null>(null);
@@ -351,7 +365,7 @@ export const CalendarGrid = /*#__PURE__*/ (forwardRef as forwardRefType)(functio
351
365
 
352
366
  let firstDayOfWeek = calenderProps?.firstDayOfWeek ?? rangeCalenderProps?.firstDayOfWeek;
353
367
 
354
- let {gridProps, headerProps, weekDays} = useCalendarGrid({
368
+ let {gridProps, headerProps, weekDays, weeksInMonth} = useCalendarGrid({
355
369
  startDate,
356
370
  endDate: endOfMonth(startDate),
357
371
  weekdayStyle: props.weekdayStyle,
@@ -359,7 +373,7 @@ export const CalendarGrid = /*#__PURE__*/ (forwardRef as forwardRefType)(functio
359
373
  }, state);
360
374
 
361
375
  return (
362
- <InternalCalendarGridContext.Provider value={{headerProps, weekDays, startDate, firstDayOfWeek}}>
376
+ <InternalCalendarGridContext.Provider value={{headerProps, weekDays, startDate, weeksInMonth}}>
363
377
  <table
364
378
  {...filterDOMProps(props as any)}
365
379
  {...gridProps}
@@ -442,9 +456,7 @@ function CalendarGridBody(props: CalendarGridBodyProps, ref: ForwardedRef<HTMLTa
442
456
  let calendarState = useContext(CalendarStateContext);
443
457
  let rangeCalendarState = useContext(RangeCalendarStateContext);
444
458
  let state = calendarState ?? rangeCalendarState!;
445
- let {startDate, firstDayOfWeek} = useContext(InternalCalendarGridContext)!;
446
- let {locale} = useLocale();
447
- let weeksInMonth = getWeeksInMonth(startDate, locale, firstDayOfWeek);
459
+ let {startDate, weeksInMonth} = useContext(InternalCalendarGridContext)!;
448
460
 
449
461
  return (
450
462
  <tbody
@@ -102,7 +102,9 @@ export const SectionContext = createContext<SectionContextValue | null>(null);
102
102
  /** @deprecated */
103
103
  export const Section = /*#__PURE__*/ createBranchComponent('section', <T extends object>(props: SectionProps<T>, ref: ForwardedRef<HTMLElement>, section: Node<T>): JSX.Element => {
104
104
  let {name, render} = useContext(SectionContext)!;
105
- console.warn(`<Section> is deprecated. Please use <${name}> instead.`);
105
+ if (process.env.NODE_ENV !== 'production') {
106
+ console.warn(`<Section> is deprecated. Please use <${name}> instead.`);
107
+ }
106
108
  return render(props, ref, section, 'react-aria-Section');
107
109
  });
108
110
 
@@ -177,6 +179,7 @@ function useCollectionRender(
177
179
 
178
180
  export const CollectionRendererContext = createContext<CollectionRenderer>(DefaultCollectionRenderer);
179
181
 
180
- export function usePersistedKeys(focusedKey: Key | null) {
182
+ type PersistedKeysReturnValue = Set<Key> | null;
183
+ export function usePersistedKeys(focusedKey: Key | null): PersistedKeysReturnValue {
181
184
  return useMemo(() => focusedKey != null ? new Set([focusedKey]) : null, [focusedKey]);
182
185
  }
@@ -16,7 +16,7 @@ import {ColorSwatchContext} from './ColorSwatch';
16
16
  import {ColorSwatchPickerContext} from './ColorSwatchPicker';
17
17
  import {mergeProps} from 'react-aria';
18
18
  import {Provider, RenderProps, SlotProps, SlottedContextValue, useRenderProps, useSlottedContext} from './utils';
19
- import React, {createContext} from 'react';
19
+ import React, {createContext, JSX} from 'react';
20
20
 
21
21
  export interface ColorPickerRenderProps {
22
22
  /** The currently selected color. */
@@ -32,7 +32,7 @@ export const ColorPickerStateContext = createContext<ColorPickerState | null>(nu
32
32
  * A ColorPicker synchronizes a color value between multiple React Aria color components.
33
33
  * It simplifies building color pickers with customizable layouts via composition.
34
34
  */
35
- export function ColorPicker(props: ColorPickerProps) {
35
+ export function ColorPicker(props: ColorPickerProps): JSX.Element {
36
36
  let ctx = useSlottedContext(ColorPickerContext, props.slot);
37
37
  props = mergeProps(ctx, props);
38
38
  let state = useColorPickerState(props);
@@ -77,6 +77,8 @@ export const ColorWheelTrackContext = createContext<ContextValue<ColorWheelTrack
77
77
  export const ColorWheelTrack = forwardRef(function ColorWheelTrack(props: ColorWheelTrackProps, ref: ForwardedRef<HTMLDivElement>) {
78
78
  [props, ref] = useContextProps(props, ref, ColorWheelTrackContext);
79
79
  let state = useContext(ColorWheelStateContext)!;
80
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
81
+ let {className, style, ...rest} = props;
80
82
 
81
83
  let renderProps = useRenderProps({
82
84
  ...props,
@@ -89,7 +91,7 @@ export const ColorWheelTrack = forwardRef(function ColorWheelTrack(props: ColorW
89
91
 
90
92
  return (
91
93
  <div
92
- {...props}
94
+ {...rest}
93
95
  {...renderProps}
94
96
  ref={ref}
95
97
  data-disabled={state.isDisabled || undefined} />
package/src/DateField.tsx CHANGED
@@ -92,7 +92,7 @@ export const DateField = /*#__PURE__*/ (forwardRef as forwardRefType)(function D
92
92
  <Provider
93
93
  values={[
94
94
  [DateFieldStateContext, state],
95
- [GroupContext, {...fieldProps, ref: fieldRef, isInvalid: state.isInvalid}],
95
+ [GroupContext, {...fieldProps, ref: fieldRef, isInvalid: state.isInvalid, isDisabled: state.isDisabled}],
96
96
  [InputContext, {...inputProps, ref: inputRef}],
97
97
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
98
98
  [TextContext, {
@@ -108,7 +108,8 @@ export const DateField = /*#__PURE__*/ (forwardRef as forwardRefType)(function D
108
108
  {...renderProps}
109
109
  ref={ref}
110
110
  slot={props.slot || undefined}
111
- data-invalid={state.isInvalid || undefined} />
111
+ data-invalid={state.isInvalid || undefined}
112
+ data-disabled={state.isDisabled || undefined} />
112
113
  </Provider>
113
114
  );
114
115
  });
@@ -157,7 +158,7 @@ export const TimeField = /*#__PURE__*/ (forwardRef as forwardRefType)(function T
157
158
  <Provider
158
159
  values={[
159
160
  [TimeFieldStateContext, state],
160
- [GroupContext, {...fieldProps, ref: fieldRef, isInvalid: state.isInvalid}],
161
+ [GroupContext, {...fieldProps, ref: fieldRef, isInvalid: state.isInvalid, isDisabled: state.isDisabled}],
161
162
  [InputContext, {...inputProps, ref: inputRef}],
162
163
  [LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
163
164
  [TextContext, {
@@ -173,7 +174,8 @@ export const TimeField = /*#__PURE__*/ (forwardRef as forwardRefType)(function T
173
174
  {...renderProps}
174
175
  ref={ref}
175
176
  slot={props.slot || undefined}
176
- data-invalid={state.isInvalid || undefined} />
177
+ data-invalid={state.isInvalid || undefined}
178
+ data-disabled={state.isDisabled || undefined} />
177
179
  </Provider>
178
180
  );
179
181
  });
@@ -241,7 +243,7 @@ const DateInputStandalone = forwardRef((props: DateInputProps, ref: ForwardedRef
241
243
  values={[
242
244
  [DateFieldStateContext, state],
243
245
  [InputContext, {...inputProps, ref: inputRef}],
244
- [GroupContext, {...fieldProps, ref: fieldRef, isInvalid: state.isInvalid}]
246
+ [GroupContext, {...fieldProps, ref: fieldRef, isInvalid: state.isInvalid, isDisabled: state.isDisabled}]
245
247
  ]}>
246
248
  <DateInputInner {...props} />
247
249
  </Provider>
@@ -261,7 +263,8 @@ const DateInputInner = forwardRef((props: DateInputProps, ref: ForwardedRef<HTML
261
263
  ref={ref}
262
264
  slot={props.slot || undefined}
263
265
  className={className ?? 'react-aria-DateInput'}
264
- isInvalid={state.isInvalid}>
266
+ isInvalid={state.isInvalid}
267
+ isDisabled={state.isDisabled}>
265
268
  {state.segments.map((segment, i) => cloneElement(children(segment), {key: i}))}
266
269
  </Group>
267
270
  <Input />
@@ -328,11 +331,12 @@ export const DateSegment = /*#__PURE__*/ (forwardRef as forwardRefType)(function
328
331
  let {segmentProps} = useDateSegment(segment, state, domRef);
329
332
  let {focusProps, isFocused, isFocusVisible} = useFocusRing();
330
333
  let {hoverProps, isHovered} = useHover({...otherProps, isDisabled: state.isDisabled || segment.type === 'literal'});
334
+ let {isEditable, ...segmentRest} = segment;
331
335
  let renderProps = useRenderProps({
332
336
  ...otherProps,
333
337
  values: {
334
- ...segment,
335
- isReadOnly: !segment.isEditable,
338
+ ...segmentRest,
339
+ isReadOnly: !isEditable,
336
340
  isInvalid: state.isInvalid,
337
341
  isDisabled: state.isDisabled,
338
342
  isHovered,
@@ -352,7 +356,7 @@ export const DateSegment = /*#__PURE__*/ (forwardRef as forwardRefType)(function
352
356
  ref={domRef}
353
357
  data-placeholder={segment.isPlaceholder || undefined}
354
358
  data-invalid={state.isInvalid || undefined}
355
- data-readonly={!segment.isEditable || undefined}
359
+ data-readonly={!isEditable || undefined}
356
360
  data-disabled={state.isDisabled || undefined}
357
361
  data-type={segment.type}
358
362
  data-hovered={isHovered || undefined}
package/src/Dialog.tsx CHANGED
@@ -12,13 +12,13 @@
12
12
  import {AriaDialogProps, useDialog, useId, useOverlayTrigger} from 'react-aria';
13
13
  import {ButtonContext} from './Button';
14
14
  import {ContextValue, DEFAULT_SLOT, Provider, SlotProps, StyleProps, useContextProps, useRenderProps} from './utils';
15
- import {filterDOMProps} from '@react-aria/utils';
15
+ import {filterDOMProps, useResizeObserver} from '@react-aria/utils';
16
16
  import {forwardRefType} from '@react-types/shared';
17
17
  import {HeadingContext} from './RSPContexts';
18
18
  import {OverlayTriggerProps, OverlayTriggerState, useMenuTriggerState} from 'react-stately';
19
19
  import {PopoverContext} from './Popover';
20
20
  import {PressResponder} from '@react-aria/interactions';
21
- import React, {createContext, ForwardedRef, forwardRef, ReactNode, useContext, useRef} from 'react';
21
+ import React, {createContext, ForwardedRef, forwardRef, JSX, ReactNode, useCallback, useContext, useRef, useState} from 'react';
22
22
  import {RootMenuTriggerStateContext} from './Menu';
23
23
 
24
24
  export interface DialogTriggerProps extends OverlayTriggerProps {
@@ -40,7 +40,7 @@ export const OverlayTriggerStateContext = createContext<OverlayTriggerState | nu
40
40
  /**
41
41
  * A DialogTrigger opens a dialog when a trigger element is pressed.
42
42
  */
43
- export function DialogTrigger(props: DialogTriggerProps) {
43
+ export function DialogTrigger(props: DialogTriggerProps): JSX.Element {
44
44
  // Use useMenuTriggerState instead of useOverlayTriggerState in case a menu is embedded in the dialog.
45
45
  // This is needed to handle submenus.
46
46
  let state = useMenuTriggerState(props);
@@ -48,6 +48,19 @@ export function DialogTrigger(props: DialogTriggerProps) {
48
48
  let buttonRef = useRef<HTMLButtonElement>(null);
49
49
  let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state, buttonRef);
50
50
 
51
+ // Allows popover width to match trigger element
52
+ let [buttonWidth, setButtonWidth] = useState<string | null>(null);
53
+ let onResize = useCallback(() => {
54
+ if (buttonRef.current) {
55
+ setButtonWidth(buttonRef.current.offsetWidth + 'px');
56
+ }
57
+ }, [buttonRef]);
58
+
59
+ useResizeObserver({
60
+ ref: buttonRef,
61
+ onResize: onResize
62
+ });
63
+
51
64
  // Label dialog by the trigger as a fallback if there is no title slot.
52
65
  // This is done in RAC instead of hooks because otherwise we cannot distinguish
53
66
  // between context and props. Normally aria-labelledby overrides the title
@@ -64,7 +77,8 @@ export function DialogTrigger(props: DialogTriggerProps) {
64
77
  [PopoverContext, {
65
78
  trigger: 'DialogTrigger',
66
79
  triggerRef: buttonRef,
67
- 'aria-labelledby': overlayProps['aria-labelledby']
80
+ 'aria-labelledby': overlayProps['aria-labelledby'],
81
+ style: {'--trigger-width': buttonWidth} as React.CSSProperties
68
82
  }]
69
83
  ]}>
70
84
  <PressResponder {...triggerProps} ref={buttonRef} isPressed={state.isOpen}>
@@ -93,7 +107,7 @@ export const Dialog = /*#__PURE__*/ (forwardRef as forwardRefType)(function Dial
93
107
  // Use that as a fallback in case there is no title slot.
94
108
  if (props['aria-labelledby']) {
95
109
  dialogProps['aria-labelledby'] = props['aria-labelledby'];
96
- } else {
110
+ } else if (process.env.NODE_ENV !== 'production') {
97
111
  console.warn('If a Dialog does not contain a <Heading slot="title">, it must have an aria-label or aria-labelledby attribute for accessibility.');
98
112
  }
99
113
  }
@@ -45,7 +45,9 @@ export const DropIndicator = forwardRef(function DropIndicator(props: DropIndica
45
45
  return <>{render(props, ref)}</>;
46
46
  });
47
47
 
48
- export function useRenderDropIndicator(dragAndDropHooks?: DragAndDropHooks, dropState?: DroppableCollectionState) {
48
+ type RenderDropIndicatorRetValue = ((target: ItemDropTarget) => ReactNode | undefined) | undefined
49
+
50
+ export function useRenderDropIndicator(dragAndDropHooks?: DragAndDropHooks, dropState?: DroppableCollectionState): RenderDropIndicatorRetValue {
49
51
  let renderDropIndicator = dragAndDropHooks?.renderDropIndicator;
50
52
  let isVirtualDragging = dragAndDropHooks?.isVirtualDragging?.();
51
53
  let fn = useCallback((target: ItemDropTarget) => {
@@ -59,7 +61,7 @@ export function useRenderDropIndicator(dragAndDropHooks?: DragAndDropHooks, drop
59
61
  return dragAndDropHooks?.useDropIndicator ? fn : undefined;
60
62
  }
61
63
 
62
- export function useDndPersistedKeys(selectionManager: MultipleSelectionManager, dragAndDropHooks?: DragAndDropHooks, dropState?: DroppableCollectionState) {
64
+ export function useDndPersistedKeys(selectionManager: MultipleSelectionManager, dragAndDropHooks?: DragAndDropHooks, dropState?: DroppableCollectionState): Set<Key> {
63
65
  // Persist the focused key and the drop target key.
64
66
  let focusedKey = selectionManager.focusedKey;
65
67
  let dropTargetKey: Key | null | undefined = null;
package/src/GridList.tsx CHANGED
@@ -14,11 +14,11 @@ import {ButtonContext} from './Button';
14
14
  import {CheckboxContext} from './RSPContexts';
15
15
  import {Collection, CollectionBuilder, createLeafComponent} from '@react-aria/collections';
16
16
  import {CollectionProps, CollectionRendererContext, DefaultCollectionRenderer, ItemRenderProps} from './Collection';
17
- import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, ScrollableProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
17
+ import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, ScrollableProps, SlotProps, StyleProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
18
18
  import {DragAndDropContext, DropIndicatorContext, DropIndicatorProps, useDndPersistedKeys, useRenderDropIndicator} from './DragAndDrop';
19
19
  import {DragAndDropHooks} from './useDragAndDrop';
20
20
  import {DraggableCollectionState, DroppableCollectionState, Collection as ICollection, ListState, Node, SelectionBehavior, useListState} from 'react-stately';
21
- import {filterDOMProps, useObjectRef} from '@react-aria/utils';
21
+ import {filterDOMProps, inertValue, LoadMoreSentinelProps, UNSTABLE_useLoadMoreSentinel, useObjectRef} from '@react-aria/utils';
22
22
  import {forwardRefType, HoverEvents, Key, LinkDOMProps, RefObject} from '@react-types/shared';
23
23
  import {ListStateContext} from './ListBox';
24
24
  import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
@@ -130,7 +130,8 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
130
130
  keyboardDelegate,
131
131
  // Only tab navigation is supported in grid layout.
132
132
  keyboardNavigationBehavior: layout === 'grid' ? 'tab' : keyboardNavigationBehavior,
133
- isVirtualized
133
+ isVirtualized,
134
+ shouldSelectOnPressUp: props.shouldSelectOnPressUp
134
135
  }, state, ref);
135
136
 
136
137
  let selectionManager = state.selectionManager;
@@ -139,6 +140,9 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
139
140
  let dragHooksProvided = useRef(isListDraggable);
140
141
  let dropHooksProvided = useRef(isListDroppable);
141
142
  useEffect(() => {
143
+ if (process.env.NODE_ENV === 'production') {
144
+ return;
145
+ }
142
146
  if (dragHooksProvided.current !== isListDraggable) {
143
147
  console.warn('Drag hooks were provided during one render, but not another. This should be avoided as it may produce unexpected behavior.');
144
148
  }
@@ -190,9 +194,11 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
190
194
  }
191
195
 
192
196
  let {focusProps, isFocused, isFocusVisible} = useFocusRing();
197
+ // TODO: What do we think about this check? Ideally we could just query the collection and see if ALL node are loaders and thus have it return that it is empty
198
+ let isEmpty = state.collection.size === 0 || (state.collection.size === 1 && state.collection.getItem(state.collection.getFirstKey()!)?.type === 'loader');
193
199
  let renderValues = {
194
200
  isDropTarget: isRootDropTarget,
195
- isEmpty: state.collection.size === 0,
201
+ isEmpty,
196
202
  isFocused,
197
203
  isFocusVisible,
198
204
  layout,
@@ -207,10 +213,11 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
207
213
 
208
214
  let emptyState: ReactNode = null;
209
215
  let emptyStatePropOverrides: HTMLAttributes<HTMLElement> | null = null;
210
- if (state.collection.size === 0 && props.renderEmptyState) {
216
+
217
+ if (isEmpty && props.renderEmptyState) {
211
218
  let content = props.renderEmptyState(renderValues);
212
219
  emptyState = (
213
- <div role="row" style={{display: 'contents'}}>
220
+ <div role="row" aria-rowindex={1} style={{display: 'contents'}}>
214
221
  <div role="gridcell" style={{display: 'contents'}}>
215
222
  {content}
216
223
  </div>
@@ -228,7 +235,7 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
228
235
  slot={props.slot || undefined}
229
236
  onScroll={props.onScroll}
230
237
  data-drop-target={isRootDropTarget || undefined}
231
- data-empty={state.collection.size === 0 || undefined}
238
+ data-empty={isEmpty || undefined}
232
239
  data-focused={isFocused || undefined}
233
240
  data-focus-visible={isFocusVisible || undefined}
234
241
  data-layout={layout}>
@@ -344,7 +351,7 @@ export const GridListItem = /*#__PURE__*/ createLeafComponent('item', function G
344
351
  }, []);
345
352
 
346
353
  useEffect(() => {
347
- if (!item.textValue) {
354
+ if (!item.textValue && process.env.NODE_ENV !== 'production') {
348
355
  console.warn('A `textValue` prop is required for <GridListItem> elements with non-plain text children in order to support accessibility features such as type to select.');
349
356
  }
350
357
  }, [item.textValue]);
@@ -489,3 +496,61 @@ function RootDropIndicator() {
489
496
  </div>
490
497
  );
491
498
  }
499
+
500
+ export interface GridListLoadingSentinelProps extends Omit<LoadMoreSentinelProps, 'collection'>, StyleProps {
501
+ /**
502
+ * The load more spinner to render when loading additional items.
503
+ */
504
+ children?: ReactNode,
505
+ /**
506
+ * Whether or not the loading spinner should be rendered or not.
507
+ */
508
+ isLoading?: boolean
509
+ }
510
+
511
+ export const UNSTABLE_GridListLoadingSentinel = createLeafComponent('loader', function GridListLoadingIndicator<T extends object>(props: GridListLoadingSentinelProps, ref: ForwardedRef<HTMLDivElement>, item: Node<T>) {
512
+ let state = useContext(ListStateContext)!;
513
+ let {isVirtualized} = useContext(CollectionRendererContext);
514
+ let {isLoading, onLoadMore, scrollOffset, ...otherProps} = props;
515
+
516
+ let sentinelRef = useRef(null);
517
+ let memoedLoadMoreProps = useMemo(() => ({
518
+ onLoadMore,
519
+ collection: state?.collection,
520
+ sentinelRef,
521
+ scrollOffset
522
+ }), [onLoadMore, scrollOffset, state?.collection]);
523
+ UNSTABLE_useLoadMoreSentinel(memoedLoadMoreProps, sentinelRef);
524
+
525
+ let renderProps = useRenderProps({
526
+ ...otherProps,
527
+ id: undefined,
528
+ children: item.rendered,
529
+ defaultClassName: 'react-aria-GridListLoadingIndicator',
530
+ values: null
531
+ });
532
+
533
+ return (
534
+ <>
535
+ {/* Alway render the sentinel. For now onus is on the user for styling when using flex + gap (this would introduce a gap even though it doesn't take room) */}
536
+ {/* @ts-ignore - compatibility with React < 19 */}
537
+ <div style={{position: 'relative', width: 0, height: 0}} inert={inertValue(true)} >
538
+ <div data-testid="loadMoreSentinel" ref={sentinelRef} style={{position: 'absolute', height: 1, width: 1}} />
539
+ </div>
540
+ {isLoading && renderProps.children && (
541
+ <div
542
+ {...renderProps}
543
+ {...mergeProps(filterDOMProps(props as any))}
544
+ role="row"
545
+ aria-rowindex={isVirtualized ? item.index + 1 : undefined}
546
+ ref={ref}>
547
+ <div
548
+ aria-colindex={isVirtualized ? 1 : undefined}
549
+ role="gridcell">
550
+ {renderProps.children}
551
+ </div>
552
+ </div>
553
+ )}
554
+ </>
555
+ );
556
+ });