@octanejs/aria 0.0.4 → 0.0.5
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/package.json +7 -5
- package/src/autocomplete/useAutocomplete.ts +656 -0
- package/src/collections/BaseCollection.ts +374 -0
- package/src/collections/CollectionBuilder.ts +343 -0
- package/src/collections/Document.ts +451 -0
- package/src/collections/Hidden.ts +84 -0
- package/src/collections/useCachedChildren.ts +103 -0
- package/src/components/Autocomplete.ts +124 -0
- package/src/components/Breadcrumbs.ts +170 -0
- package/src/components/Button.ts +209 -0
- package/src/components/Checkbox.ts +593 -0
- package/src/components/Collection.ts +301 -0
- package/src/components/ComboBox.ts +477 -0
- package/src/components/Dialog.ts +195 -0
- package/src/components/Disclosure.ts +322 -0
- package/src/components/DragAndDrop.ts +177 -0
- package/src/components/FieldError.ts +79 -0
- package/src/components/Form.ts +65 -0
- package/src/components/GridList.ts +1099 -0
- package/src/components/Group.ts +146 -0
- package/src/components/Header.ts +32 -0
- package/src/components/Heading.ts +45 -0
- package/src/components/Input.ts +139 -0
- package/src/components/Keyboard.ts +23 -0
- package/src/components/Label.ts +30 -0
- package/src/components/Link.ts +137 -0
- package/src/components/ListBox.ts +886 -0
- package/src/components/Menu.ts +685 -0
- package/src/components/Meter.ts +100 -0
- package/src/components/Modal.ts +352 -0
- package/src/components/NumberField.ts +221 -0
- package/src/components/OverlayArrow.ts +117 -0
- package/src/components/Popover.ts +385 -0
- package/src/components/ProgressBar.ts +112 -0
- package/src/components/RadioGroup.ts +542 -0
- package/src/components/SearchField.ts +208 -0
- package/src/components/Select.ts +505 -0
- package/src/components/SelectionIndicator.ts +52 -0
- package/src/components/Separator.ts +97 -0
- package/src/components/SharedElementTransition.ts +209 -0
- package/src/components/Slider.ts +472 -0
- package/src/components/Switch.ts +449 -0
- package/src/components/Tabs.ts +675 -0
- package/src/components/TagGroup.ts +443 -0
- package/src/components/Text.ts +27 -0
- package/src/components/TextArea.ts +92 -0
- package/src/components/TextField.ts +200 -0
- package/src/components/ToggleButton.ts +147 -0
- package/src/components/ToggleButtonGroup.ts +106 -0
- package/src/components/Toolbar.ts +74 -0
- package/src/components/Tooltip.ts +249 -0
- package/src/components/index.ts +302 -0
- package/src/components/useDragAndDrop.ts +164 -0
- package/src/components/utils.ts +521 -0
- package/src/intl/autocomplete/ar-AE.json +3 -0
- package/src/intl/autocomplete/bg-BG.json +3 -0
- package/src/intl/autocomplete/cs-CZ.json +3 -0
- package/src/intl/autocomplete/da-DK.json +3 -0
- package/src/intl/autocomplete/de-DE.json +3 -0
- package/src/intl/autocomplete/el-GR.json +3 -0
- package/src/intl/autocomplete/en-US.json +3 -0
- package/src/intl/autocomplete/es-ES.json +3 -0
- package/src/intl/autocomplete/et-EE.json +3 -0
- package/src/intl/autocomplete/fi-FI.json +3 -0
- package/src/intl/autocomplete/fr-FR.json +3 -0
- package/src/intl/autocomplete/he-IL.json +3 -0
- package/src/intl/autocomplete/hr-HR.json +3 -0
- package/src/intl/autocomplete/hu-HU.json +3 -0
- package/src/intl/autocomplete/index.ts +75 -0
- package/src/intl/autocomplete/it-IT.json +3 -0
- package/src/intl/autocomplete/ja-JP.json +3 -0
- package/src/intl/autocomplete/ko-KR.json +3 -0
- package/src/intl/autocomplete/lt-LT.json +3 -0
- package/src/intl/autocomplete/lv-LV.json +3 -0
- package/src/intl/autocomplete/nb-NO.json +3 -0
- package/src/intl/autocomplete/nl-NL.json +3 -0
- package/src/intl/autocomplete/pl-PL.json +3 -0
- package/src/intl/autocomplete/pt-BR.json +3 -0
- package/src/intl/autocomplete/pt-PT.json +3 -0
- package/src/intl/autocomplete/ro-RO.json +3 -0
- package/src/intl/autocomplete/ru-RU.json +3 -0
- package/src/intl/autocomplete/sk-SK.json +3 -0
- package/src/intl/autocomplete/sl-SI.json +3 -0
- package/src/intl/autocomplete/sr-SP.json +3 -0
- package/src/intl/autocomplete/sv-SE.json +3 -0
- package/src/intl/autocomplete/tr-TR.json +3 -0
- package/src/intl/autocomplete/uk-UA.json +3 -0
- package/src/intl/autocomplete/zh-CN.json +3 -0
- package/src/intl/autocomplete/zh-TW.json +3 -0
- package/src/stately/autocomplete/useAutocompleteState.ts +85 -0
- package/src/utils/animation.ts +149 -0
- package/src/utils/useLoadMoreSentinel.ts +78 -0
- package/src/utils/useViewportSize.ts +123 -0
|
@@ -0,0 +1,1099 @@
|
|
|
1
|
+
// Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/GridList.tsx).
|
|
2
|
+
// octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded
|
|
3
|
+
// ref is `props.ref` (GridList passes it into `useContextProps` explicitly; GridListItem,
|
|
4
|
+
// GridListSection and the drop-indicator wrapper adapt theirs with `useObjectRef` exactly
|
|
5
|
+
// like upstream's forwarded refs); the plain-`.ts` components use the S()/subSlot
|
|
6
|
+
// component-slot convention. The collection composes the Phase-4 engine:
|
|
7
|
+
// `CollectionBuilder`/`createLeafComponent`/`createBranchComponent` from
|
|
8
|
+
// `../collections/CollectionBuilder` and the renderer's `CollectionRoot`/`CollectionBranch`
|
|
9
|
+
// via `CollectionRendererContext`. Upstream's RAC-local `CollectionProps` import is our
|
|
10
|
+
// `ItemCollectionProps` (see ./Collection.ts). Upstream's `inertValue` (React <19 string
|
|
11
|
+
// compat) collapses to the plain boolean — octane follows React 19 `inert` semantics. The
|
|
12
|
+
// dnd branches use the PHASE-7 structural aliases from ./useDragAndDrop and stay inert
|
|
13
|
+
// until a consumer can construct `dragAndDropHooks`. react-aria's private
|
|
14
|
+
// `useLoadMoreSentinel` comes from ../utils/useLoadMoreSentinel (shared with ListBox).
|
|
15
|
+
// Explicit dep arrays are preserved verbatim.
|
|
16
|
+
import type {
|
|
17
|
+
HoverEvents,
|
|
18
|
+
Collection as ICollection,
|
|
19
|
+
Key,
|
|
20
|
+
LinkDOMProps,
|
|
21
|
+
Node,
|
|
22
|
+
Orientation,
|
|
23
|
+
PressEvents,
|
|
24
|
+
SelectionBehavior,
|
|
25
|
+
} from '@react-types/shared';
|
|
26
|
+
import {
|
|
27
|
+
Fragment,
|
|
28
|
+
createContext,
|
|
29
|
+
createElement,
|
|
30
|
+
useContext,
|
|
31
|
+
useEffect,
|
|
32
|
+
useMemo,
|
|
33
|
+
useRef,
|
|
34
|
+
} from 'octane';
|
|
35
|
+
|
|
36
|
+
import { HeaderNode, ItemNode, LoaderNode, SectionNode } from '../collections/BaseCollection';
|
|
37
|
+
import {
|
|
38
|
+
CollectionBuilder,
|
|
39
|
+
createBranchComponent,
|
|
40
|
+
createLeafComponent,
|
|
41
|
+
} from '../collections/CollectionBuilder';
|
|
42
|
+
import { FocusScope } from '../focus/FocusScope';
|
|
43
|
+
import { useFocusRing } from '../focus/useFocusRing';
|
|
44
|
+
import { type AriaGridListProps, useGridList } from '../gridlist/useGridList';
|
|
45
|
+
import { useGridListItem } from '../gridlist/useGridListItem';
|
|
46
|
+
import { useGridListSection } from '../gridlist/useGridListSection';
|
|
47
|
+
import { useGridListSelectionCheckbox } from '../gridlist/useGridListSelectionCheckbox';
|
|
48
|
+
import { useCollator } from '../i18n/useCollator';
|
|
49
|
+
import { useLocale } from '../i18n/I18nProvider';
|
|
50
|
+
import { useHover } from '../interactions/useHover';
|
|
51
|
+
import { S, subSlot } from '../internal';
|
|
52
|
+
import { ListKeyboardDelegate } from '../selection/ListKeyboardDelegate';
|
|
53
|
+
import {
|
|
54
|
+
type ListState,
|
|
55
|
+
UNSTABLE_useFilteredListState,
|
|
56
|
+
useListState,
|
|
57
|
+
} from '../stately/list/useListState';
|
|
58
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
59
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
60
|
+
import { type LoadMoreSentinelProps, useLoadMoreSentinel } from '../utils/useLoadMoreSentinel';
|
|
61
|
+
import { useObjectRef } from '../utils/useObjectRef';
|
|
62
|
+
import { useVisuallyHidden } from '../visually-hidden/VisuallyHidden';
|
|
63
|
+
import {
|
|
64
|
+
FieldInputContext,
|
|
65
|
+
SelectableCollectionContext,
|
|
66
|
+
type SelectableCollectionContextValue,
|
|
67
|
+
} from './Autocomplete';
|
|
68
|
+
import { ButtonContext } from './Button';
|
|
69
|
+
import { CheckboxContext, CheckboxFieldContext } from './Checkbox';
|
|
70
|
+
import {
|
|
71
|
+
Collection,
|
|
72
|
+
CollectionRendererContext,
|
|
73
|
+
DefaultCollectionRenderer,
|
|
74
|
+
type ItemCollectionProps,
|
|
75
|
+
type ItemRenderProps,
|
|
76
|
+
type SectionProps,
|
|
77
|
+
} from './Collection';
|
|
78
|
+
import {
|
|
79
|
+
DragAndDropContext,
|
|
80
|
+
DropIndicatorContext,
|
|
81
|
+
type DropIndicatorProps,
|
|
82
|
+
useDndPersistedKeys,
|
|
83
|
+
useRenderDropIndicator,
|
|
84
|
+
} from './DragAndDrop';
|
|
85
|
+
import type {
|
|
86
|
+
DragAndDropHooks,
|
|
87
|
+
DraggableCollectionState,
|
|
88
|
+
DraggableItemResult,
|
|
89
|
+
DropIndicatorAria,
|
|
90
|
+
DroppableCollectionResult,
|
|
91
|
+
DroppableCollectionState,
|
|
92
|
+
} from './useDragAndDrop';
|
|
93
|
+
import { ListStateContext } from './ListBox';
|
|
94
|
+
import { SelectionIndicatorContext } from './SelectionIndicator';
|
|
95
|
+
import { SharedElementTransition } from './SharedElementTransition';
|
|
96
|
+
import { TextContext } from './Text';
|
|
97
|
+
import {
|
|
98
|
+
type ClassNameOrFunction,
|
|
99
|
+
type ContextValue,
|
|
100
|
+
DEFAULT_SLOT,
|
|
101
|
+
dom,
|
|
102
|
+
type DOMProps,
|
|
103
|
+
type DOMRenderProps,
|
|
104
|
+
Provider,
|
|
105
|
+
type RenderProps,
|
|
106
|
+
type SlotProps,
|
|
107
|
+
type StyleProps,
|
|
108
|
+
type StyleRenderProps,
|
|
109
|
+
useContextProps,
|
|
110
|
+
useRenderProps,
|
|
111
|
+
} from './utils';
|
|
112
|
+
|
|
113
|
+
// octane adaptation: structural bag (upstream's `GlobalDOMAttributes` drags React handler types).
|
|
114
|
+
type GlobalDOMAttributes = Record<string, any>;
|
|
115
|
+
type RefObject<T> = { current: T };
|
|
116
|
+
type ReactNode = any;
|
|
117
|
+
type HTMLAttributes = Record<string, any>;
|
|
118
|
+
|
|
119
|
+
export interface GridListRenderProps {
|
|
120
|
+
/**
|
|
121
|
+
* Whether the list has no items and should display its empty state.
|
|
122
|
+
*
|
|
123
|
+
* @selector [data-empty]
|
|
124
|
+
*/
|
|
125
|
+
isEmpty: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Whether the grid list is currently focused.
|
|
128
|
+
*
|
|
129
|
+
* @selector [data-focused]
|
|
130
|
+
*/
|
|
131
|
+
isFocused: boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the grid list is currently keyboard focused.
|
|
134
|
+
*
|
|
135
|
+
* @selector [data-focus-visible]
|
|
136
|
+
*/
|
|
137
|
+
isFocusVisible: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Whether the grid list is currently the active drop target.
|
|
140
|
+
*
|
|
141
|
+
* @selector [data-drop-target]
|
|
142
|
+
*/
|
|
143
|
+
isDropTarget: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Whether the items are arranged in a stack or grid.
|
|
146
|
+
*
|
|
147
|
+
* @selector [data-layout="stack | grid"]
|
|
148
|
+
*/
|
|
149
|
+
layout: 'stack' | 'grid';
|
|
150
|
+
/**
|
|
151
|
+
* The primary orientation of the items.
|
|
152
|
+
*
|
|
153
|
+
* @selector [data-orientation="vertical | horizontal"]
|
|
154
|
+
*/
|
|
155
|
+
orientation: Orientation;
|
|
156
|
+
/**
|
|
157
|
+
* State of the grid list.
|
|
158
|
+
*/
|
|
159
|
+
state: ListState<unknown>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface GridListProps<T>
|
|
163
|
+
extends
|
|
164
|
+
Omit<AriaGridListProps<T>, 'children'>,
|
|
165
|
+
ItemCollectionProps<T>,
|
|
166
|
+
StyleRenderProps<GridListRenderProps>,
|
|
167
|
+
SlotProps,
|
|
168
|
+
GlobalDOMAttributes {
|
|
169
|
+
/**
|
|
170
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
171
|
+
* element. A function may be provided to compute the class based on component state.
|
|
172
|
+
*
|
|
173
|
+
* @default 'react-aria-GridList'
|
|
174
|
+
*/
|
|
175
|
+
className?: ClassNameOrFunction<GridListRenderProps>;
|
|
176
|
+
/**
|
|
177
|
+
* Whether typeahead navigation is disabled.
|
|
178
|
+
*
|
|
179
|
+
* @default false
|
|
180
|
+
*/
|
|
181
|
+
disallowTypeAhead?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* How multiple selection should behave in the collection.
|
|
184
|
+
*
|
|
185
|
+
* @default 'toggle'
|
|
186
|
+
*/
|
|
187
|
+
selectionBehavior?: SelectionBehavior;
|
|
188
|
+
/**
|
|
189
|
+
* The drag and drop hooks returned by `useDragAndDrop` used to enable drag and drop behavior for
|
|
190
|
+
* the GridList.
|
|
191
|
+
*/
|
|
192
|
+
dragAndDropHooks?: DragAndDropHooks<NoInfer<T>>;
|
|
193
|
+
/** Provides content to display when there are no items in the list. */
|
|
194
|
+
renderEmptyState?: (props: GridListRenderProps) => ReactNode;
|
|
195
|
+
/**
|
|
196
|
+
* Whether the items are arranged in a stack or grid.
|
|
197
|
+
*
|
|
198
|
+
* @default 'stack'
|
|
199
|
+
*/
|
|
200
|
+
layout?: 'stack' | 'grid';
|
|
201
|
+
/**
|
|
202
|
+
* The primary orientation of the items. Usually this is the direction that the collection
|
|
203
|
+
* scrolls.
|
|
204
|
+
*
|
|
205
|
+
* @default 'vertical'
|
|
206
|
+
*/
|
|
207
|
+
orientation?: Orientation;
|
|
208
|
+
/**
|
|
209
|
+
* Which item in the collection to focus when tabbing into the collection. Overrides default
|
|
210
|
+
* roving tab index like behavior.
|
|
211
|
+
*
|
|
212
|
+
* @private
|
|
213
|
+
*/
|
|
214
|
+
UNSTABLE_focusOnEntry?: 'first' | 'last';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export const GridListContext =
|
|
218
|
+
createContext<ContextValue<GridListProps<any>, HTMLDivElement>>(null);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* A grid list displays a list of interactive items, with support for keyboard navigation,
|
|
222
|
+
* single or multiple selection, and row actions.
|
|
223
|
+
*/
|
|
224
|
+
export function GridList<T extends object>(props: GridListProps<T>): any {
|
|
225
|
+
const slot = S('GridList');
|
|
226
|
+
// Render the portal first so that we have the collection by the time we render the DOM in SSR.
|
|
227
|
+
let ref: any;
|
|
228
|
+
[props, ref] = useContextProps(props, (props as any).ref, GridListContext, subSlot(slot, 'ctx'));
|
|
229
|
+
|
|
230
|
+
return createElement(CollectionBuilder, {
|
|
231
|
+
content: createElement(Collection, props as any),
|
|
232
|
+
children: (collection: ICollection<Node<any>>) =>
|
|
233
|
+
createElement(GridListInner, { props, collection, gridListRef: ref }),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
interface GridListInnerProps<T> {
|
|
238
|
+
props: GridListProps<T> & SelectableCollectionContextValue<T>;
|
|
239
|
+
collection: ICollection<Node<any>>;
|
|
240
|
+
gridListRef: RefObject<HTMLElement | null>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function GridListInner<T>({ props, collection, gridListRef: ref }: GridListInnerProps<T>): any {
|
|
244
|
+
const slot = S('GridListInner');
|
|
245
|
+
[props, ref] = useContextProps(
|
|
246
|
+
props,
|
|
247
|
+
ref as any,
|
|
248
|
+
SelectableCollectionContext,
|
|
249
|
+
subSlot(slot, 'ctx'),
|
|
250
|
+
) as any;
|
|
251
|
+
let {
|
|
252
|
+
shouldUseVirtualFocus: _shouldUseVirtualFocus,
|
|
253
|
+
filter,
|
|
254
|
+
disallowTypeAhead,
|
|
255
|
+
UNSTABLE_focusOnEntry,
|
|
256
|
+
...DOMCollectionProps
|
|
257
|
+
} = props;
|
|
258
|
+
let {
|
|
259
|
+
dragAndDropHooks,
|
|
260
|
+
keyboardNavigationBehavior = 'arrow',
|
|
261
|
+
layout = 'stack',
|
|
262
|
+
orientation = 'vertical',
|
|
263
|
+
} = props;
|
|
264
|
+
let {
|
|
265
|
+
CollectionRoot,
|
|
266
|
+
isVirtualized,
|
|
267
|
+
layoutDelegate,
|
|
268
|
+
dropTargetDelegate: ctxDropTargetDelegate,
|
|
269
|
+
} = useContext(CollectionRendererContext);
|
|
270
|
+
let gridlistState = useListState(
|
|
271
|
+
{
|
|
272
|
+
...DOMCollectionProps,
|
|
273
|
+
collection,
|
|
274
|
+
children: undefined,
|
|
275
|
+
layoutDelegate,
|
|
276
|
+
} as any,
|
|
277
|
+
subSlot(slot, 'state'),
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
let filteredState = UNSTABLE_useFilteredListState(
|
|
281
|
+
gridlistState as ListState<T>,
|
|
282
|
+
filter,
|
|
283
|
+
subSlot(slot, 'filtered'),
|
|
284
|
+
);
|
|
285
|
+
let collator = useCollator({ usage: 'search', sensitivity: 'base' }, subSlot(slot, 'collator'));
|
|
286
|
+
let { disabledBehavior, disabledKeys } = filteredState.selectionManager;
|
|
287
|
+
let { direction } = useLocale(subSlot(slot, 'locale'));
|
|
288
|
+
let keyboardDelegate = useMemo(
|
|
289
|
+
() =>
|
|
290
|
+
new ListKeyboardDelegate({
|
|
291
|
+
collection: filteredState.collection,
|
|
292
|
+
collator,
|
|
293
|
+
ref,
|
|
294
|
+
disabledKeys,
|
|
295
|
+
disabledBehavior,
|
|
296
|
+
layoutDelegate,
|
|
297
|
+
layout,
|
|
298
|
+
orientation,
|
|
299
|
+
direction,
|
|
300
|
+
}),
|
|
301
|
+
[
|
|
302
|
+
filteredState.collection,
|
|
303
|
+
ref,
|
|
304
|
+
layout,
|
|
305
|
+
orientation,
|
|
306
|
+
disabledKeys,
|
|
307
|
+
disabledBehavior,
|
|
308
|
+
layoutDelegate,
|
|
309
|
+
collator,
|
|
310
|
+
direction,
|
|
311
|
+
],
|
|
312
|
+
subSlot(slot, 'keyboardDelegate'),
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
let { gridProps } = useGridList(
|
|
316
|
+
{
|
|
317
|
+
...DOMCollectionProps,
|
|
318
|
+
keyboardDelegate,
|
|
319
|
+
// Only tab navigation is supported in grid layout.
|
|
320
|
+
keyboardNavigationBehavior: layout === 'grid' ? 'tab' : keyboardNavigationBehavior,
|
|
321
|
+
isVirtualized,
|
|
322
|
+
shouldSelectOnPressUp: props.shouldSelectOnPressUp,
|
|
323
|
+
disallowTypeAhead,
|
|
324
|
+
UNSTABLE_focusOnEntry,
|
|
325
|
+
} as any,
|
|
326
|
+
filteredState,
|
|
327
|
+
ref,
|
|
328
|
+
subSlot(slot, 'gridList'),
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
let selectionManager = filteredState.selectionManager;
|
|
332
|
+
let isListDraggable = !!dragAndDropHooks?.useDraggableCollectionState;
|
|
333
|
+
let isListDroppable = !!dragAndDropHooks?.useDroppableCollectionState;
|
|
334
|
+
let dragHooksProvided = useRef(isListDraggable, subSlot(slot, 'dragProvided'));
|
|
335
|
+
let dropHooksProvided = useRef(isListDroppable, subSlot(slot, 'dropProvided'));
|
|
336
|
+
useEffect(
|
|
337
|
+
() => {
|
|
338
|
+
if (process.env.NODE_ENV === 'production') {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (dragHooksProvided.current !== isListDraggable) {
|
|
342
|
+
console.warn(
|
|
343
|
+
'Drag hooks were provided during one render, but not another. This should be avoided as it may produce unexpected behavior.',
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (dropHooksProvided.current !== isListDroppable) {
|
|
347
|
+
console.warn(
|
|
348
|
+
'Drop hooks were provided during one render, but not another. This should be avoided as it may produce unexpected behavior.',
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
[isListDraggable, isListDroppable],
|
|
353
|
+
subSlot(slot, 'dndWarn'),
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
let dragState: DraggableCollectionState | undefined = undefined;
|
|
357
|
+
let dropState: DroppableCollectionState | undefined = undefined;
|
|
358
|
+
let droppableCollection: DroppableCollectionResult | undefined = undefined;
|
|
359
|
+
let isRootDropTarget = false;
|
|
360
|
+
let dragPreview: any = null;
|
|
361
|
+
let preview = useRef<any>(null, subSlot(slot, 'preview'));
|
|
362
|
+
|
|
363
|
+
// PHASE-7: these branches call consumer-provided dnd hooks (upstream contract); they are
|
|
364
|
+
// unreachable until `useDragAndDrop` is ported and a consumer can construct hooks.
|
|
365
|
+
if (isListDraggable && dragAndDropHooks) {
|
|
366
|
+
dragState = dragAndDropHooks.useDraggableCollectionState!({
|
|
367
|
+
collection: filteredState.collection,
|
|
368
|
+
selectionManager,
|
|
369
|
+
preview: dragAndDropHooks.renderDragPreview ? preview : undefined,
|
|
370
|
+
});
|
|
371
|
+
dragAndDropHooks.useDraggableCollection!({}, dragState, ref);
|
|
372
|
+
|
|
373
|
+
let DragPreview = dragAndDropHooks.DragPreview!;
|
|
374
|
+
dragPreview = dragAndDropHooks.renderDragPreview
|
|
375
|
+
? createElement(DragPreview, { ref: preview, children: dragAndDropHooks.renderDragPreview })
|
|
376
|
+
: null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (isListDroppable && dragAndDropHooks) {
|
|
380
|
+
dropState = dragAndDropHooks.useDroppableCollectionState!({
|
|
381
|
+
collection: filteredState.collection,
|
|
382
|
+
selectionManager,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
let dropTargetDelegate =
|
|
386
|
+
dragAndDropHooks.dropTargetDelegate ||
|
|
387
|
+
ctxDropTargetDelegate ||
|
|
388
|
+
new dragAndDropHooks.ListDropTargetDelegate(collection, ref, {
|
|
389
|
+
layout,
|
|
390
|
+
direction,
|
|
391
|
+
orientation,
|
|
392
|
+
});
|
|
393
|
+
droppableCollection = dragAndDropHooks.useDroppableCollection!(
|
|
394
|
+
{
|
|
395
|
+
keyboardDelegate,
|
|
396
|
+
dropTargetDelegate,
|
|
397
|
+
},
|
|
398
|
+
dropState,
|
|
399
|
+
ref,
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
isRootDropTarget = dropState.isDropTarget({ type: 'root' });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
let { focusProps, isFocused, isFocusVisible } = useFocusRing(
|
|
406
|
+
undefined,
|
|
407
|
+
subSlot(slot, 'focusRing'),
|
|
408
|
+
);
|
|
409
|
+
let isEmpty = filteredState.collection.size === 0;
|
|
410
|
+
let renderValues = {
|
|
411
|
+
isDropTarget: isRootDropTarget,
|
|
412
|
+
orientation,
|
|
413
|
+
isEmpty,
|
|
414
|
+
isFocused,
|
|
415
|
+
isFocusVisible,
|
|
416
|
+
layout,
|
|
417
|
+
state: filteredState,
|
|
418
|
+
};
|
|
419
|
+
let renderProps = useRenderProps(
|
|
420
|
+
{
|
|
421
|
+
...props,
|
|
422
|
+
children: undefined,
|
|
423
|
+
defaultClassName: 'react-aria-GridList',
|
|
424
|
+
values: renderValues,
|
|
425
|
+
} as any,
|
|
426
|
+
subSlot(slot, 'render'),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
let emptyState: any = null;
|
|
430
|
+
let emptyStatePropOverrides: HTMLAttributes | null = null;
|
|
431
|
+
|
|
432
|
+
if (isEmpty && props.renderEmptyState) {
|
|
433
|
+
let content = props.renderEmptyState(renderValues as any);
|
|
434
|
+
emptyState = createElement('div', {
|
|
435
|
+
role: 'row',
|
|
436
|
+
'aria-rowindex': 1,
|
|
437
|
+
style: { display: 'contents' },
|
|
438
|
+
children: createElement('div', {
|
|
439
|
+
role: 'gridcell',
|
|
440
|
+
style: { display: 'contents' },
|
|
441
|
+
children: content,
|
|
442
|
+
}),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
let DOMProps = filterDOMProps(props, { global: true });
|
|
447
|
+
|
|
448
|
+
// octane adaptation: hooks hoisted out of the createElement argument list (upstream calls
|
|
449
|
+
// them inline in JSX attribute position).
|
|
450
|
+
let persistedKeys = useDndPersistedKeys(
|
|
451
|
+
selectionManager,
|
|
452
|
+
dragAndDropHooks,
|
|
453
|
+
dropState,
|
|
454
|
+
subSlot(slot, 'persisted'),
|
|
455
|
+
);
|
|
456
|
+
let renderDropIndicator = useRenderDropIndicator(
|
|
457
|
+
dragAndDropHooks,
|
|
458
|
+
dropState,
|
|
459
|
+
subSlot(slot, 'dropIndicator'),
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
return createElement(FocusScope, {
|
|
463
|
+
children: createElement(dom.div, {
|
|
464
|
+
...mergeProps(
|
|
465
|
+
DOMProps,
|
|
466
|
+
renderProps,
|
|
467
|
+
gridProps,
|
|
468
|
+
focusProps,
|
|
469
|
+
droppableCollection?.collectionProps,
|
|
470
|
+
emptyStatePropOverrides,
|
|
471
|
+
),
|
|
472
|
+
ref,
|
|
473
|
+
slot: props.slot || undefined,
|
|
474
|
+
onScroll: props.onScroll,
|
|
475
|
+
'data-drop-target': isRootDropTarget || undefined,
|
|
476
|
+
'data-empty': isEmpty || undefined,
|
|
477
|
+
'data-focused': isFocused || undefined,
|
|
478
|
+
'data-focus-visible': isFocusVisible || undefined,
|
|
479
|
+
'data-layout': layout,
|
|
480
|
+
'data-orientation': orientation,
|
|
481
|
+
// Positional (non-array) children: the trailing empty/drag-preview slots are
|
|
482
|
+
// unkeyed siblings of the collection Provider, exactly like upstream's JSX.
|
|
483
|
+
children: createElement(
|
|
484
|
+
Fragment,
|
|
485
|
+
null,
|
|
486
|
+
createElement(
|
|
487
|
+
Provider,
|
|
488
|
+
{
|
|
489
|
+
values: [
|
|
490
|
+
[ListStateContext, filteredState],
|
|
491
|
+
[DragAndDropContext, { dragAndDropHooks, dragState, dropState }],
|
|
492
|
+
[DropIndicatorContext, { render: GridListDropIndicatorWrapper }],
|
|
493
|
+
] as any,
|
|
494
|
+
} as any,
|
|
495
|
+
isListDroppable ? createElement(RootDropIndicator, {}) : null,
|
|
496
|
+
createElement(SharedElementTransition, {
|
|
497
|
+
children: createElement(CollectionRoot, {
|
|
498
|
+
collection: filteredState.collection,
|
|
499
|
+
scrollRef: ref,
|
|
500
|
+
persistedKeys,
|
|
501
|
+
renderDropIndicator,
|
|
502
|
+
}),
|
|
503
|
+
}),
|
|
504
|
+
),
|
|
505
|
+
emptyState,
|
|
506
|
+
dragPreview,
|
|
507
|
+
),
|
|
508
|
+
}),
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export interface GridListItemRenderProps extends ItemRenderProps {
|
|
513
|
+
/** The unique id of the item. */
|
|
514
|
+
id?: Key;
|
|
515
|
+
/**
|
|
516
|
+
* Whether the item's children have keyboard focus.
|
|
517
|
+
*
|
|
518
|
+
* @selector [data-focus-visible-within]
|
|
519
|
+
*/
|
|
520
|
+
isFocusVisibleWithin: boolean;
|
|
521
|
+
/**
|
|
522
|
+
* State of the grid list.
|
|
523
|
+
*/
|
|
524
|
+
state: ListState<unknown>;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export interface GridListItemProps<T = object>
|
|
528
|
+
extends
|
|
529
|
+
RenderProps<GridListItemRenderProps>,
|
|
530
|
+
LinkDOMProps,
|
|
531
|
+
HoverEvents,
|
|
532
|
+
PressEvents,
|
|
533
|
+
Omit<GlobalDOMAttributes, 'onClick'> {
|
|
534
|
+
/**
|
|
535
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
536
|
+
* element. A function may be provided to compute the class based on component state.
|
|
537
|
+
*
|
|
538
|
+
* @default 'react-aria-GridListItem'
|
|
539
|
+
*/
|
|
540
|
+
className?: ClassNameOrFunction<GridListItemRenderProps>;
|
|
541
|
+
/** The unique id of the item. */
|
|
542
|
+
id?: Key;
|
|
543
|
+
/**
|
|
544
|
+
* The object value that this item represents. When using dynamic collections, this is set
|
|
545
|
+
* automatically.
|
|
546
|
+
*/
|
|
547
|
+
value?: T;
|
|
548
|
+
/** A string representation of the item's contents, used for features like typeahead. */
|
|
549
|
+
textValue?: string;
|
|
550
|
+
/** Whether the item is disabled. */
|
|
551
|
+
isDisabled?: boolean;
|
|
552
|
+
/**
|
|
553
|
+
* Handler that is called when a user performs an action on the item. The exact user event depends
|
|
554
|
+
* on the collection's `selectionBehavior` prop and the interaction modality.
|
|
555
|
+
*/
|
|
556
|
+
onAction?: () => void;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* A GridListItem represents an individual item in a GridList.
|
|
561
|
+
*/
|
|
562
|
+
export const GridListItem: <T extends object = object>(
|
|
563
|
+
props: GridListItemProps<T> & { ref?: any },
|
|
564
|
+
) => any = /*#__PURE__*/ createLeafComponent(
|
|
565
|
+
ItemNode,
|
|
566
|
+
// The third (item) parameter is declared so `render.length === 3` keeps the
|
|
567
|
+
// engine's "cannot be rendered outside a collection" guard; it is always
|
|
568
|
+
// provided when rendered from a collection node.
|
|
569
|
+
function GridListItem<T>(props: GridListItemProps<T>, forwardedRef: any, item?: Node<T>): any {
|
|
570
|
+
const slot = S('GridListItem');
|
|
571
|
+
let state = useContext(ListStateContext)!;
|
|
572
|
+
let { dragAndDropHooks, dragState, dropState } = useContext(DragAndDropContext);
|
|
573
|
+
let ref = useObjectRef<HTMLDivElement>(forwardedRef, subSlot(slot, 'objectRef'));
|
|
574
|
+
let { isVirtualized } = useContext(CollectionRendererContext);
|
|
575
|
+
let isDraggable =
|
|
576
|
+
dragState && !(dragState.isDisabled || dragState.selectionManager.isDisabled(item!.key));
|
|
577
|
+
let { rowProps, gridCellProps, descriptionProps, ...states } = useGridListItem(
|
|
578
|
+
{
|
|
579
|
+
node: item!,
|
|
580
|
+
shouldSelectOnPressUp: !!dragState,
|
|
581
|
+
isVirtualized,
|
|
582
|
+
},
|
|
583
|
+
state,
|
|
584
|
+
ref,
|
|
585
|
+
subSlot(slot, 'gridListItem'),
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
let { hoverProps, isHovered } = useHover(
|
|
589
|
+
{
|
|
590
|
+
// because of https://bugs.webkit.org/show_bug.cgi?id=214609, supporting hover styles when a item is ONLY isDraggable
|
|
591
|
+
// results in hover styles sticking around after a reorder/drop operation...
|
|
592
|
+
isDisabled: !states.allowsSelection && !states.hasAction && !isDraggable,
|
|
593
|
+
onHoverStart: item!.props.onHoverStart,
|
|
594
|
+
onHoverChange: item!.props.onHoverChange,
|
|
595
|
+
onHoverEnd: item!.props.onHoverEnd,
|
|
596
|
+
},
|
|
597
|
+
subSlot(slot, 'hover'),
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
let { isFocusVisible, focusProps } = useFocusRing(undefined, subSlot(slot, 'focusRing'));
|
|
601
|
+
let { isFocusVisible: isFocusVisibleWithin, focusProps: focusWithinProps } = useFocusRing(
|
|
602
|
+
{
|
|
603
|
+
within: true,
|
|
604
|
+
},
|
|
605
|
+
subSlot(slot, 'focusRingWithin'),
|
|
606
|
+
);
|
|
607
|
+
let { checkboxProps } = useGridListSelectionCheckbox(
|
|
608
|
+
{ key: item!.key },
|
|
609
|
+
state,
|
|
610
|
+
subSlot(slot, 'checkbox'),
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
let buttonProps =
|
|
614
|
+
state.selectionManager.disabledBehavior === 'all' && states.isDisabled
|
|
615
|
+
? { isDisabled: true }
|
|
616
|
+
: {};
|
|
617
|
+
|
|
618
|
+
// PHASE-7: consumer-provided dnd item hooks (unreachable until useDragAndDrop is ported).
|
|
619
|
+
let draggableItem: DraggableItemResult | null = null;
|
|
620
|
+
if (dragState && dragAndDropHooks) {
|
|
621
|
+
draggableItem = dragAndDropHooks.useDraggableItem!(
|
|
622
|
+
{ key: item!.key, hasDragButton: true },
|
|
623
|
+
dragState,
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
let dropIndicator: DropIndicatorAria | null = null;
|
|
628
|
+
let dropIndicatorRef = useRef<HTMLDivElement | null>(null, subSlot(slot, 'dropIndicatorRef'));
|
|
629
|
+
let { visuallyHiddenProps } = useVisuallyHidden(undefined, subSlot(slot, 'visuallyHidden'));
|
|
630
|
+
if (dropState && dragAndDropHooks) {
|
|
631
|
+
dropIndicator = dragAndDropHooks.useDropIndicator!(
|
|
632
|
+
{
|
|
633
|
+
target: { type: 'item', key: item!.key, dropPosition: 'on' },
|
|
634
|
+
},
|
|
635
|
+
dropState,
|
|
636
|
+
dropIndicatorRef,
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
let isDragging = dragState && dragState.isDragging(item!.key);
|
|
641
|
+
let renderProps = useRenderProps<GridListItemRenderProps, any>(
|
|
642
|
+
{
|
|
643
|
+
...props,
|
|
644
|
+
id: undefined,
|
|
645
|
+
children: item!.rendered,
|
|
646
|
+
defaultClassName: 'react-aria-GridListItem',
|
|
647
|
+
values: {
|
|
648
|
+
...states,
|
|
649
|
+
isHovered,
|
|
650
|
+
isFocusVisible,
|
|
651
|
+
isFocusVisibleWithin,
|
|
652
|
+
selectionMode: state.selectionManager.selectionMode,
|
|
653
|
+
selectionBehavior: state.selectionManager.selectionBehavior,
|
|
654
|
+
allowsDragging: !!dragState,
|
|
655
|
+
isDragging,
|
|
656
|
+
isDropTarget: dropIndicator?.isDropTarget,
|
|
657
|
+
id: item!.key,
|
|
658
|
+
state,
|
|
659
|
+
},
|
|
660
|
+
} as any,
|
|
661
|
+
subSlot(slot, 'render'),
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
let dragButtonRef = useRef<HTMLButtonElement | null>(null, subSlot(slot, 'dragButton'));
|
|
665
|
+
useEffect(
|
|
666
|
+
() => {
|
|
667
|
+
if (dragState && !dragButtonRef.current) {
|
|
668
|
+
console.warn(
|
|
669
|
+
'Draggable items in a GridList must contain a <Button slot="drag"> element so that keyboard and screen reader users can drag them.',
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
[],
|
|
674
|
+
subSlot(slot, 'dragButtonWarn'),
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
useEffect(
|
|
678
|
+
() => {
|
|
679
|
+
if (!item!.textValue && process.env.NODE_ENV !== 'production') {
|
|
680
|
+
console.warn(
|
|
681
|
+
'A `textValue` prop is required for <GridListItem> elements with non-plain text children in order to support accessibility features such as type to select.',
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
[item!.textValue],
|
|
686
|
+
subSlot(slot, 'textValueWarn'),
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
let DOMProps = filterDOMProps(props as any, { global: true });
|
|
690
|
+
delete DOMProps.id;
|
|
691
|
+
delete DOMProps.onClick;
|
|
692
|
+
|
|
693
|
+
return createElement(
|
|
694
|
+
Fragment,
|
|
695
|
+
null,
|
|
696
|
+
dropIndicator && !dropIndicator.isHidden
|
|
697
|
+
? createElement('div', {
|
|
698
|
+
role: 'row',
|
|
699
|
+
style: { position: 'absolute' },
|
|
700
|
+
children: createElement('div', {
|
|
701
|
+
role: 'gridcell',
|
|
702
|
+
children: createElement('div', {
|
|
703
|
+
role: 'button',
|
|
704
|
+
...visuallyHiddenProps,
|
|
705
|
+
...dropIndicator?.dropIndicatorProps,
|
|
706
|
+
ref: dropIndicatorRef,
|
|
707
|
+
}),
|
|
708
|
+
}),
|
|
709
|
+
})
|
|
710
|
+
: null,
|
|
711
|
+
createElement(dom.div, {
|
|
712
|
+
...mergeProps(
|
|
713
|
+
DOMProps,
|
|
714
|
+
renderProps,
|
|
715
|
+
rowProps,
|
|
716
|
+
focusProps,
|
|
717
|
+
focusWithinProps,
|
|
718
|
+
hoverProps,
|
|
719
|
+
draggableItem?.dragProps,
|
|
720
|
+
),
|
|
721
|
+
ref,
|
|
722
|
+
'data-selected': states.isSelected || undefined,
|
|
723
|
+
'data-disabled': states.isDisabled || undefined,
|
|
724
|
+
'data-hovered': isHovered || undefined,
|
|
725
|
+
'data-focused': states.isFocused || undefined,
|
|
726
|
+
'data-focus-visible': isFocusVisible || undefined,
|
|
727
|
+
'data-focus-visible-within': isFocusVisibleWithin || undefined,
|
|
728
|
+
'data-pressed': states.isPressed || undefined,
|
|
729
|
+
'data-allows-dragging': !!dragState || undefined,
|
|
730
|
+
'data-dragging': isDragging || undefined,
|
|
731
|
+
'data-drop-target': dropIndicator?.isDropTarget || undefined,
|
|
732
|
+
'data-selection-mode':
|
|
733
|
+
state.selectionManager.selectionMode === 'none'
|
|
734
|
+
? undefined
|
|
735
|
+
: state.selectionManager.selectionMode,
|
|
736
|
+
children: createElement('div', {
|
|
737
|
+
...gridCellProps,
|
|
738
|
+
style: { display: 'contents' },
|
|
739
|
+
children: createElement(Provider, {
|
|
740
|
+
values: [
|
|
741
|
+
[
|
|
742
|
+
CheckboxContext,
|
|
743
|
+
{
|
|
744
|
+
slots: {
|
|
745
|
+
[DEFAULT_SLOT]: {},
|
|
746
|
+
selection: checkboxProps,
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
],
|
|
750
|
+
[
|
|
751
|
+
CheckboxFieldContext,
|
|
752
|
+
{
|
|
753
|
+
slots: {
|
|
754
|
+
[DEFAULT_SLOT]: {},
|
|
755
|
+
selection: checkboxProps,
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
],
|
|
759
|
+
[
|
|
760
|
+
ButtonContext,
|
|
761
|
+
{
|
|
762
|
+
slots: {
|
|
763
|
+
[DEFAULT_SLOT]: buttonProps,
|
|
764
|
+
drag: {
|
|
765
|
+
...draggableItem?.dragButtonProps,
|
|
766
|
+
ref: dragButtonRef,
|
|
767
|
+
style: {
|
|
768
|
+
pointerEvents: 'none',
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
},
|
|
772
|
+
},
|
|
773
|
+
],
|
|
774
|
+
[
|
|
775
|
+
TextContext,
|
|
776
|
+
{
|
|
777
|
+
slots: {
|
|
778
|
+
[DEFAULT_SLOT]: {},
|
|
779
|
+
description: descriptionProps,
|
|
780
|
+
},
|
|
781
|
+
},
|
|
782
|
+
],
|
|
783
|
+
[CollectionRendererContext, DefaultCollectionRenderer],
|
|
784
|
+
[ListStateContext, null],
|
|
785
|
+
[SelectableCollectionContext, null],
|
|
786
|
+
[FieldInputContext, null],
|
|
787
|
+
[SelectionIndicatorContext, { isSelected: states.isSelected }],
|
|
788
|
+
] as any,
|
|
789
|
+
children: renderProps.children,
|
|
790
|
+
}),
|
|
791
|
+
}),
|
|
792
|
+
}),
|
|
793
|
+
);
|
|
794
|
+
},
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
function GridListDropIndicatorWrapper(props: DropIndicatorProps, forwardedRef: any): any {
|
|
798
|
+
const slot = S('GridListDropIndicatorWrapper');
|
|
799
|
+
let ref = useObjectRef<HTMLElement>(forwardedRef, subSlot(slot, 'objectRef'));
|
|
800
|
+
let { dragAndDropHooks, dropState } = useContext(DragAndDropContext);
|
|
801
|
+
let buttonRef = useRef<HTMLDivElement | null>(null, subSlot(slot, 'buttonRef'));
|
|
802
|
+
let { dropIndicatorProps, isHidden, isDropTarget } = dragAndDropHooks!.useDropIndicator!(
|
|
803
|
+
props,
|
|
804
|
+
dropState!,
|
|
805
|
+
buttonRef,
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
if (isHidden) {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
return createElement(GridListDropIndicator, {
|
|
813
|
+
...props,
|
|
814
|
+
dropIndicatorProps,
|
|
815
|
+
isDropTarget,
|
|
816
|
+
buttonRef,
|
|
817
|
+
ref,
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
interface GridListDropIndicatorProps extends DropIndicatorProps {
|
|
822
|
+
dropIndicatorProps: Record<string, any>;
|
|
823
|
+
isDropTarget: boolean;
|
|
824
|
+
buttonRef: RefObject<HTMLDivElement | null>;
|
|
825
|
+
ref?: any;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// octane adaptation: no forwardRef — the forwarded ref arrives as `props.ref` (upstream wraps
|
|
829
|
+
// this in `forwardRef` as GridListDropIndicatorForwardRef).
|
|
830
|
+
function GridListDropIndicator(props: GridListDropIndicatorProps): any {
|
|
831
|
+
const slot = S('GridListDropIndicator');
|
|
832
|
+
let { dropIndicatorProps, isDropTarget, buttonRef, ref, ...otherProps } = props;
|
|
833
|
+
|
|
834
|
+
let { visuallyHiddenProps } = useVisuallyHidden(undefined, subSlot(slot, 'visuallyHidden'));
|
|
835
|
+
let renderProps = useRenderProps(
|
|
836
|
+
{
|
|
837
|
+
...otherProps,
|
|
838
|
+
defaultClassName: 'react-aria-DropIndicator',
|
|
839
|
+
values: {
|
|
840
|
+
isDropTarget,
|
|
841
|
+
},
|
|
842
|
+
} as any,
|
|
843
|
+
subSlot(slot, 'render'),
|
|
844
|
+
);
|
|
845
|
+
|
|
846
|
+
return createElement(dom.div, {
|
|
847
|
+
...renderProps,
|
|
848
|
+
role: 'row',
|
|
849
|
+
ref,
|
|
850
|
+
'data-drop-target': isDropTarget || undefined,
|
|
851
|
+
// Positional (non-array) children inside the gridcell, exactly like upstream's JSX.
|
|
852
|
+
children: createElement('div', {
|
|
853
|
+
role: 'gridcell',
|
|
854
|
+
children: createElement(
|
|
855
|
+
Fragment,
|
|
856
|
+
null,
|
|
857
|
+
createElement('div', {
|
|
858
|
+
...visuallyHiddenProps,
|
|
859
|
+
role: 'button',
|
|
860
|
+
...dropIndicatorProps,
|
|
861
|
+
ref: buttonRef,
|
|
862
|
+
}),
|
|
863
|
+
renderProps.children,
|
|
864
|
+
),
|
|
865
|
+
}),
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function RootDropIndicator(): any {
|
|
870
|
+
const slot = S('GridListRootDropIndicator');
|
|
871
|
+
let { dragAndDropHooks, dropState } = useContext(DragAndDropContext);
|
|
872
|
+
let ref = useRef<HTMLDivElement | null>(null, subSlot(slot, 'ref'));
|
|
873
|
+
let { dropIndicatorProps } = dragAndDropHooks!.useDropIndicator!(
|
|
874
|
+
{
|
|
875
|
+
target: { type: 'root' },
|
|
876
|
+
},
|
|
877
|
+
dropState!,
|
|
878
|
+
ref,
|
|
879
|
+
);
|
|
880
|
+
let isDropTarget = dropState!.isDropTarget({ type: 'root' });
|
|
881
|
+
let { visuallyHiddenProps } = useVisuallyHidden(undefined, subSlot(slot, 'visuallyHidden'));
|
|
882
|
+
|
|
883
|
+
if (!isDropTarget && dropIndicatorProps['aria-hidden']) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
return createElement('div', {
|
|
888
|
+
role: 'row',
|
|
889
|
+
'aria-hidden': dropIndicatorProps['aria-hidden'],
|
|
890
|
+
style: { position: 'absolute' },
|
|
891
|
+
children: createElement('div', {
|
|
892
|
+
role: 'gridcell',
|
|
893
|
+
children: createElement('div', {
|
|
894
|
+
role: 'button',
|
|
895
|
+
...visuallyHiddenProps,
|
|
896
|
+
...dropIndicatorProps,
|
|
897
|
+
ref,
|
|
898
|
+
}),
|
|
899
|
+
}),
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
export interface GridListLoadMoreItemProps
|
|
904
|
+
extends
|
|
905
|
+
Omit<LoadMoreSentinelProps, 'collection'>,
|
|
906
|
+
StyleProps,
|
|
907
|
+
DOMRenderProps<'div', undefined>,
|
|
908
|
+
GlobalDOMAttributes {
|
|
909
|
+
/**
|
|
910
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
911
|
+
* element.
|
|
912
|
+
*
|
|
913
|
+
* @default 'react-aria-GridListLoadMoreItem'
|
|
914
|
+
*/
|
|
915
|
+
className?: string;
|
|
916
|
+
/**
|
|
917
|
+
* The load more spinner to render when loading additional items.
|
|
918
|
+
*/
|
|
919
|
+
children?: ReactNode;
|
|
920
|
+
/**
|
|
921
|
+
* Whether or not the loading spinner should be rendered or not.
|
|
922
|
+
*/
|
|
923
|
+
isLoading?: boolean;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
export const GridListLoadMoreItem: (props: GridListLoadMoreItemProps & { ref?: any }) => any =
|
|
927
|
+
createLeafComponent(
|
|
928
|
+
LoaderNode,
|
|
929
|
+
// Third (item) parameter declared for the engine's `render.length === 3` guard.
|
|
930
|
+
function GridListLoadingIndicator(
|
|
931
|
+
props: GridListLoadMoreItemProps,
|
|
932
|
+
ref: any,
|
|
933
|
+
item?: Node<object>,
|
|
934
|
+
): any {
|
|
935
|
+
const slot = S('GridListLoadMoreItem');
|
|
936
|
+
let state = useContext(ListStateContext)!;
|
|
937
|
+
let { isVirtualized } = useContext(CollectionRendererContext);
|
|
938
|
+
let { isLoading, onLoadMore, scrollOffset, ...otherProps } = props;
|
|
939
|
+
|
|
940
|
+
let sentinelRef = useRef<HTMLDivElement | null>(null, subSlot(slot, 'sentinel'));
|
|
941
|
+
let memoedLoadMoreProps = useMemo(
|
|
942
|
+
() => ({
|
|
943
|
+
onLoadMore,
|
|
944
|
+
collection: state?.collection,
|
|
945
|
+
sentinelRef,
|
|
946
|
+
scrollOffset,
|
|
947
|
+
}),
|
|
948
|
+
[onLoadMore, scrollOffset, state?.collection],
|
|
949
|
+
subSlot(slot, 'loadMoreProps'),
|
|
950
|
+
);
|
|
951
|
+
useLoadMoreSentinel(
|
|
952
|
+
memoedLoadMoreProps as any,
|
|
953
|
+
sentinelRef as any,
|
|
954
|
+
subSlot(slot, 'loadMore'),
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
let renderProps = useRenderProps(
|
|
958
|
+
{
|
|
959
|
+
...otherProps,
|
|
960
|
+
id: undefined,
|
|
961
|
+
children: item!.rendered,
|
|
962
|
+
defaultClassName: 'react-aria-GridListLoadingIndicator',
|
|
963
|
+
values: undefined,
|
|
964
|
+
} as any,
|
|
965
|
+
subSlot(slot, 'render'),
|
|
966
|
+
);
|
|
967
|
+
// For now don't include aria-posinset and aria-setsize on loader since they aren't keyboard focusable
|
|
968
|
+
// Arguably shouldn't include them ever since it might be confusing to the user to include the loaders as part of the
|
|
969
|
+
// item count
|
|
970
|
+
|
|
971
|
+
return createElement(
|
|
972
|
+
Fragment,
|
|
973
|
+
null,
|
|
974
|
+
// Alway render the sentinel. For now onus is on the user for styling when using flex + gap
|
|
975
|
+
// (this would introduce a gap even though it doesn't take room). octane adaptation:
|
|
976
|
+
// upstream `inertValue` is React <19 string compat; octane follows React 19 boolean
|
|
977
|
+
// `inert` semantics.
|
|
978
|
+
createElement('div', {
|
|
979
|
+
style: { position: 'relative', width: 0, height: 0 },
|
|
980
|
+
inert: true,
|
|
981
|
+
children: createElement('div', {
|
|
982
|
+
'data-testid': 'loadMoreSentinel',
|
|
983
|
+
ref: sentinelRef,
|
|
984
|
+
style: { position: 'absolute', height: 1, width: 1 },
|
|
985
|
+
}),
|
|
986
|
+
}),
|
|
987
|
+
isLoading && renderProps.children
|
|
988
|
+
? createElement(dom.div, {
|
|
989
|
+
...renderProps,
|
|
990
|
+
...filterDOMProps(props, { global: true }),
|
|
991
|
+
role: 'row',
|
|
992
|
+
ref,
|
|
993
|
+
children: createElement('div', {
|
|
994
|
+
'aria-colindex': isVirtualized ? 1 : undefined,
|
|
995
|
+
role: 'gridcell',
|
|
996
|
+
children: renderProps.children,
|
|
997
|
+
}),
|
|
998
|
+
})
|
|
999
|
+
: null,
|
|
1000
|
+
);
|
|
1001
|
+
},
|
|
1002
|
+
);
|
|
1003
|
+
|
|
1004
|
+
export interface GridListSectionProps<T> extends SectionProps<T>, DOMRenderProps<'div', undefined> {
|
|
1005
|
+
/**
|
|
1006
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
1007
|
+
* element.
|
|
1008
|
+
*
|
|
1009
|
+
* @default 'react-aria-GridListSection'
|
|
1010
|
+
*/
|
|
1011
|
+
className?: string;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* A GridListSection represents a section within a GridList.
|
|
1016
|
+
*/
|
|
1017
|
+
export const GridListSection: <T extends object = object>(
|
|
1018
|
+
props: GridListSectionProps<T> & { ref?: any },
|
|
1019
|
+
) => any = /*#__PURE__*/ createBranchComponent(SectionNode, function GridListSection<
|
|
1020
|
+
T,
|
|
1021
|
+
>(props: GridListSectionProps<T>, forwardedRef: any, item?: Node<T>): any {
|
|
1022
|
+
const slot = S('GridListSection');
|
|
1023
|
+
let state = useContext(ListStateContext)!;
|
|
1024
|
+
let { CollectionBranch } = useContext(CollectionRendererContext);
|
|
1025
|
+
let headingRef = useRef<HTMLElement | null>(null, subSlot(slot, 'heading'));
|
|
1026
|
+
let ref = useObjectRef<HTMLDivElement>(forwardedRef, subSlot(slot, 'objectRef'));
|
|
1027
|
+
let { rowHeaderProps, rowProps, rowGroupProps } = useGridListSection(
|
|
1028
|
+
{
|
|
1029
|
+
'aria-label': props['aria-label'] ?? undefined,
|
|
1030
|
+
},
|
|
1031
|
+
state,
|
|
1032
|
+
ref,
|
|
1033
|
+
subSlot(slot, 'section'),
|
|
1034
|
+
);
|
|
1035
|
+
let renderProps = useRenderProps(
|
|
1036
|
+
{
|
|
1037
|
+
...props,
|
|
1038
|
+
id: undefined,
|
|
1039
|
+
children: undefined,
|
|
1040
|
+
defaultClassName: 'react-aria-GridListSection',
|
|
1041
|
+
values: undefined,
|
|
1042
|
+
} as any,
|
|
1043
|
+
subSlot(slot, 'render'),
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
let DOMProps = filterDOMProps(props as any, { global: true });
|
|
1047
|
+
delete DOMProps.id;
|
|
1048
|
+
|
|
1049
|
+
return createElement(dom.div, {
|
|
1050
|
+
...mergeProps(DOMProps, renderProps, rowGroupProps),
|
|
1051
|
+
ref,
|
|
1052
|
+
children: createElement(Provider, {
|
|
1053
|
+
values: [
|
|
1054
|
+
[GridListHeaderContext, { ...rowProps, ref: headingRef }],
|
|
1055
|
+
[GridListHeaderInnerContext, { ...rowHeaderProps }],
|
|
1056
|
+
] as any,
|
|
1057
|
+
children: createElement(CollectionBranch, {
|
|
1058
|
+
collection: state.collection,
|
|
1059
|
+
parent: item!,
|
|
1060
|
+
}),
|
|
1061
|
+
}),
|
|
1062
|
+
});
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
export interface GridListHeaderProps
|
|
1066
|
+
extends DOMRenderProps<'div', undefined>, DOMProps, GlobalDOMAttributes {}
|
|
1067
|
+
|
|
1068
|
+
export const GridListHeaderContext = createContext<
|
|
1069
|
+
ContextValue<GridListHeaderProps, HTMLDivElement>
|
|
1070
|
+
>({});
|
|
1071
|
+
export const GridListHeaderInnerContext = createContext<HTMLAttributes | null>(null);
|
|
1072
|
+
|
|
1073
|
+
export const GridListHeader: (props: GridListHeaderProps & { ref?: any }) => any =
|
|
1074
|
+
/*#__PURE__*/ createLeafComponent(
|
|
1075
|
+
HeaderNode,
|
|
1076
|
+
function Header(props: GridListHeaderProps, forwardedRef: any): any {
|
|
1077
|
+
const slot = S('GridListHeader');
|
|
1078
|
+
let ref: any;
|
|
1079
|
+
[props, ref] = useContextProps(
|
|
1080
|
+
props,
|
|
1081
|
+
forwardedRef,
|
|
1082
|
+
GridListHeaderContext,
|
|
1083
|
+
subSlot(slot, 'ctx'),
|
|
1084
|
+
);
|
|
1085
|
+
let rowHeaderProps = useContext(GridListHeaderInnerContext);
|
|
1086
|
+
|
|
1087
|
+
return createElement(dom.div, {
|
|
1088
|
+
render: props.render,
|
|
1089
|
+
className: 'react-aria-GridListHeader',
|
|
1090
|
+
ref,
|
|
1091
|
+
...props,
|
|
1092
|
+
children: createElement('div', {
|
|
1093
|
+
...rowHeaderProps,
|
|
1094
|
+
style: { display: 'contents' },
|
|
1095
|
+
children: props.children,
|
|
1096
|
+
}),
|
|
1097
|
+
});
|
|
1098
|
+
},
|
|
1099
|
+
);
|