@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,443 @@
|
|
|
1
|
+
// Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/TagGroup.tsx).
|
|
2
|
+
// octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded
|
|
3
|
+
// ref is `props.ref` (TagGroup passes it into `useContextProps` explicitly; Tag adapts its
|
|
4
|
+
// forwarded ref with `useObjectRef` exactly like upstream); the plain-`.ts` components use
|
|
5
|
+
// the S()/subSlot component-slot convention. The collection composes the Phase-4 engine:
|
|
6
|
+
// `CollectionBuilder`/`createLeafComponent` from `../collections/CollectionBuilder` and the
|
|
7
|
+
// renderer's `CollectionRoot` via `CollectionRendererContext`. Upstream's RAC-local
|
|
8
|
+
// `CollectionProps` import is our `ItemCollectionProps` (see ./Collection.ts). Explicit dep
|
|
9
|
+
// arrays are preserved verbatim.
|
|
10
|
+
import type { HoverEvents, Key, LinkDOMProps, Node, PressEvents } from '@react-types/shared';
|
|
11
|
+
import { createContext, createElement, useContext, useEffect, useRef } from 'octane';
|
|
12
|
+
|
|
13
|
+
import { ItemNode } from '../collections/BaseCollection';
|
|
14
|
+
import { CollectionBuilder, createLeafComponent } from '../collections/CollectionBuilder';
|
|
15
|
+
import { useFocusRing } from '../focus/useFocusRing';
|
|
16
|
+
// octane adaptation: the ported NATIVE-event handler bag (upstream: FocusEvents from
|
|
17
|
+
// '@react-types/shared', typed over React synthetic events).
|
|
18
|
+
import { type FocusEvents } from '../interactions/useFocus';
|
|
19
|
+
import { useHover } from '../interactions/useHover';
|
|
20
|
+
import { S, subSlot } from '../internal';
|
|
21
|
+
import { type AriaTagGroupProps, useTagGroup } from '../tag/useTagGroup';
|
|
22
|
+
import { useTag } from '../tag/useTag';
|
|
23
|
+
import {
|
|
24
|
+
type ListState,
|
|
25
|
+
UNSTABLE_useFilteredListState,
|
|
26
|
+
useListState,
|
|
27
|
+
} from '../stately/list/useListState';
|
|
28
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
29
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
30
|
+
import { useObjectRef } from '../utils/useObjectRef';
|
|
31
|
+
import { SelectableCollectionContext, type SelectableCollectionContextValue } from './Autocomplete';
|
|
32
|
+
import { ButtonContext } from './Button';
|
|
33
|
+
import {
|
|
34
|
+
Collection,
|
|
35
|
+
CollectionRendererContext,
|
|
36
|
+
DefaultCollectionRenderer,
|
|
37
|
+
type ItemCollectionProps,
|
|
38
|
+
type ItemRenderProps,
|
|
39
|
+
usePersistedKeys,
|
|
40
|
+
} from './Collection';
|
|
41
|
+
import { LabelContext } from './Label';
|
|
42
|
+
import { ListStateContext } from './ListBox';
|
|
43
|
+
import { SelectionIndicatorContext } from './SelectionIndicator';
|
|
44
|
+
import { SharedElementTransition } from './SharedElementTransition';
|
|
45
|
+
import { TextContext } from './Text';
|
|
46
|
+
import {
|
|
47
|
+
type ClassNameOrFunction,
|
|
48
|
+
type ContextValue,
|
|
49
|
+
dom,
|
|
50
|
+
type DOMProps,
|
|
51
|
+
type DOMRenderProps,
|
|
52
|
+
Provider,
|
|
53
|
+
type RenderProps,
|
|
54
|
+
type SlotProps,
|
|
55
|
+
type StyleRenderProps,
|
|
56
|
+
useContextProps,
|
|
57
|
+
useRenderProps,
|
|
58
|
+
useSlot,
|
|
59
|
+
} from './utils';
|
|
60
|
+
|
|
61
|
+
// octane adaptation: structural bag (upstream's `GlobalDOMAttributes` drags React handler types).
|
|
62
|
+
type GlobalDOMAttributes = Record<string, any>;
|
|
63
|
+
type RefObject<T> = { current: T };
|
|
64
|
+
type ReactNode = any;
|
|
65
|
+
|
|
66
|
+
export interface TagGroupProps
|
|
67
|
+
extends
|
|
68
|
+
Omit<
|
|
69
|
+
AriaTagGroupProps<unknown>,
|
|
70
|
+
'children' | 'items' | 'label' | 'description' | 'errorMessage' | 'keyboardDelegate'
|
|
71
|
+
>,
|
|
72
|
+
DOMProps,
|
|
73
|
+
SlotProps,
|
|
74
|
+
DOMRenderProps<'div', undefined>,
|
|
75
|
+
GlobalDOMAttributes {
|
|
76
|
+
/**
|
|
77
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
78
|
+
* element.
|
|
79
|
+
*
|
|
80
|
+
* @default 'react-aria-TagGroup'
|
|
81
|
+
*/
|
|
82
|
+
className?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface TagListRenderProps {
|
|
86
|
+
/**
|
|
87
|
+
* Whether the tag list has no items and should display its empty state.
|
|
88
|
+
*
|
|
89
|
+
* @selector [data-empty]
|
|
90
|
+
*/
|
|
91
|
+
isEmpty: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Whether the tag list is currently focused.
|
|
94
|
+
*
|
|
95
|
+
* @selector [data-focused]
|
|
96
|
+
*/
|
|
97
|
+
isFocused: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Whether the tag list is currently keyboard focused.
|
|
100
|
+
*
|
|
101
|
+
* @selector [data-focus-visible]
|
|
102
|
+
*/
|
|
103
|
+
isFocusVisible: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* State of the TagGroup.
|
|
106
|
+
*/
|
|
107
|
+
state: ListState<unknown>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface TagListProps<T>
|
|
111
|
+
extends
|
|
112
|
+
Omit<ItemCollectionProps<T>, 'disabledKeys'>,
|
|
113
|
+
StyleRenderProps<TagListRenderProps, 'div'>,
|
|
114
|
+
GlobalDOMAttributes {
|
|
115
|
+
/**
|
|
116
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
117
|
+
* element. A function may be provided to compute the class based on component state.
|
|
118
|
+
*
|
|
119
|
+
* @default 'react-aria-TagList'
|
|
120
|
+
*/
|
|
121
|
+
className?: ClassNameOrFunction<TagListRenderProps>;
|
|
122
|
+
/** Provides content to display when there are no items in the tag list. */
|
|
123
|
+
renderEmptyState?: (props: TagListRenderProps) => ReactNode;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const TagGroupContext = createContext<ContextValue<TagGroupProps, HTMLDivElement>>(null);
|
|
127
|
+
export const TagListContext = createContext<ContextValue<TagListProps<any>, HTMLDivElement>>(null);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A tag group is a focusable list of labels, categories, keywords, filters, or other items, with
|
|
131
|
+
* support for keyboard navigation, selection, and removal.
|
|
132
|
+
*/
|
|
133
|
+
export function TagGroup(props: TagGroupProps): any {
|
|
134
|
+
const slot = S('TagGroup');
|
|
135
|
+
let ref: any;
|
|
136
|
+
[props, ref] = useContextProps(props, (props as any).ref, TagGroupContext, subSlot(slot, 'ctx'));
|
|
137
|
+
return createElement(ListStateContext.Provider, {
|
|
138
|
+
value: null,
|
|
139
|
+
children: createElement(CollectionBuilder, {
|
|
140
|
+
content: props.children,
|
|
141
|
+
children: (collection: any) =>
|
|
142
|
+
createElement(TagGroupInner, { props, forwardedRef: ref, collection }),
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface TagGroupInnerProps<T> {
|
|
148
|
+
props: TagGroupProps & SelectableCollectionContextValue<T>;
|
|
149
|
+
forwardedRef: RefObject<HTMLDivElement | null>;
|
|
150
|
+
collection: any;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function TagGroupInner<T>({ props, forwardedRef: ref, collection }: TagGroupInnerProps<T>): any {
|
|
154
|
+
const slot = S('TagGroupInner');
|
|
155
|
+
let tagListRef = useRef<HTMLElement | null>(null, subSlot(slot, 'tagListRef'));
|
|
156
|
+
// Extract the user provided id so it doesn't clash with the collection id provided by Autocomplete
|
|
157
|
+
let { id, ...otherProps } = props;
|
|
158
|
+
[otherProps, tagListRef] = useContextProps(
|
|
159
|
+
otherProps,
|
|
160
|
+
tagListRef as any,
|
|
161
|
+
SelectableCollectionContext,
|
|
162
|
+
subSlot(slot, 'ctx'),
|
|
163
|
+
) as any;
|
|
164
|
+
let { filter, shouldUseVirtualFocus: _shouldUseVirtualFocus, ...DOMCollectionProps } = otherProps;
|
|
165
|
+
let [labelRef, label] = useSlot(
|
|
166
|
+
!props['aria-label'] && !props['aria-labelledby'],
|
|
167
|
+
subSlot(slot, 'label'),
|
|
168
|
+
);
|
|
169
|
+
let tagGroupState = useListState(
|
|
170
|
+
{
|
|
171
|
+
...DOMCollectionProps,
|
|
172
|
+
children: undefined,
|
|
173
|
+
collection,
|
|
174
|
+
} as any,
|
|
175
|
+
subSlot(slot, 'state'),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
let filteredState = UNSTABLE_useFilteredListState(
|
|
179
|
+
tagGroupState as ListState<T>,
|
|
180
|
+
filter,
|
|
181
|
+
subSlot(slot, 'filtered'),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Prevent DOM props from going to two places.
|
|
185
|
+
let domProps = filterDOMProps(otherProps, { global: true });
|
|
186
|
+
let domPropOverrides = Object.fromEntries(
|
|
187
|
+
Object.entries(domProps).map(([k, val]) => [k, k === 'id' ? val : undefined]),
|
|
188
|
+
);
|
|
189
|
+
let { gridProps, labelProps, descriptionProps, errorMessageProps } = useTagGroup(
|
|
190
|
+
{
|
|
191
|
+
...DOMCollectionProps,
|
|
192
|
+
...domPropOverrides,
|
|
193
|
+
label,
|
|
194
|
+
} as any,
|
|
195
|
+
filteredState,
|
|
196
|
+
tagListRef,
|
|
197
|
+
subSlot(slot, 'tagGroup'),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return createElement(dom.div, {
|
|
201
|
+
render: props.render,
|
|
202
|
+
...domProps,
|
|
203
|
+
id,
|
|
204
|
+
ref,
|
|
205
|
+
slot: props.slot || undefined,
|
|
206
|
+
className: props.className ?? 'react-aria-TagGroup',
|
|
207
|
+
style: props.style,
|
|
208
|
+
children: createElement(Provider, {
|
|
209
|
+
values: [
|
|
210
|
+
[LabelContext, { ...labelProps, elementType: 'span', ref: labelRef }],
|
|
211
|
+
[TagListContext, { ...gridProps, ref: tagListRef as RefObject<HTMLDivElement> }],
|
|
212
|
+
[ListStateContext, filteredState],
|
|
213
|
+
[
|
|
214
|
+
TextContext,
|
|
215
|
+
{
|
|
216
|
+
slots: {
|
|
217
|
+
description: descriptionProps,
|
|
218
|
+
errorMessage: errorMessageProps,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
] as any,
|
|
223
|
+
children: props.children,
|
|
224
|
+
}),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* A tag list is a container for tags within a TagGroup.
|
|
230
|
+
*/
|
|
231
|
+
export function TagList<T extends object>(props: TagListProps<T>): any {
|
|
232
|
+
let state = useContext(ListStateContext);
|
|
233
|
+
let { ref: forwardedRef, ...otherProps } = props as any;
|
|
234
|
+
return state
|
|
235
|
+
? createElement(TagListInner, { props: otherProps, forwardedRef })
|
|
236
|
+
: createElement(Collection, otherProps);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
interface TagListInnerProps<T> {
|
|
240
|
+
props: TagListProps<T>;
|
|
241
|
+
forwardedRef: RefObject<HTMLDivElement | null>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function TagListInner<T>({ props, forwardedRef }: TagListInnerProps<T>): any {
|
|
245
|
+
const slot = S('TagListInner');
|
|
246
|
+
let state = useContext(ListStateContext)!;
|
|
247
|
+
let { CollectionRoot } = useContext(CollectionRendererContext);
|
|
248
|
+
let [gridProps, ref] = useContextProps(
|
|
249
|
+
{} as any,
|
|
250
|
+
forwardedRef as any,
|
|
251
|
+
TagListContext,
|
|
252
|
+
subSlot(slot, 'ctx'),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
let { focusProps, isFocused, isFocusVisible } = useFocusRing(
|
|
256
|
+
undefined,
|
|
257
|
+
subSlot(slot, 'focusRing'),
|
|
258
|
+
);
|
|
259
|
+
let renderValues = {
|
|
260
|
+
isEmpty: state.collection.size === 0,
|
|
261
|
+
isFocused,
|
|
262
|
+
isFocusVisible,
|
|
263
|
+
state,
|
|
264
|
+
};
|
|
265
|
+
let renderProps = useRenderProps(
|
|
266
|
+
{
|
|
267
|
+
...props,
|
|
268
|
+
children: undefined,
|
|
269
|
+
defaultClassName: 'react-aria-TagList',
|
|
270
|
+
values: renderValues,
|
|
271
|
+
} as any,
|
|
272
|
+
subSlot(slot, 'render'),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
let persistedKeys = usePersistedKeys(
|
|
276
|
+
state.selectionManager.focusedKey,
|
|
277
|
+
subSlot(slot, 'persisted'),
|
|
278
|
+
);
|
|
279
|
+
let DOMProps = filterDOMProps(props, { global: true });
|
|
280
|
+
|
|
281
|
+
return createElement(dom.div, {
|
|
282
|
+
...mergeProps(DOMProps, renderProps, gridProps, focusProps),
|
|
283
|
+
ref,
|
|
284
|
+
'data-empty': state.collection.size === 0 || undefined,
|
|
285
|
+
'data-focused': isFocused || undefined,
|
|
286
|
+
'data-focus-visible': isFocusVisible || undefined,
|
|
287
|
+
children: createElement(SharedElementTransition, {
|
|
288
|
+
children:
|
|
289
|
+
state.collection.size === 0 && props.renderEmptyState
|
|
290
|
+
? props.renderEmptyState(renderValues)
|
|
291
|
+
: createElement(CollectionRoot, {
|
|
292
|
+
collection: state.collection,
|
|
293
|
+
persistedKeys,
|
|
294
|
+
}),
|
|
295
|
+
}),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface TagRenderProps extends Omit<
|
|
300
|
+
ItemRenderProps,
|
|
301
|
+
'allowsDragging' | 'isDragging' | 'isDropTarget'
|
|
302
|
+
> {
|
|
303
|
+
/**
|
|
304
|
+
* Whether the tag group allows items to be removed.
|
|
305
|
+
*
|
|
306
|
+
* @selector [data-allows-removing]
|
|
307
|
+
*/
|
|
308
|
+
allowsRemoving: boolean;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface TagProps
|
|
312
|
+
extends
|
|
313
|
+
RenderProps<TagRenderProps, 'div'>,
|
|
314
|
+
LinkDOMProps,
|
|
315
|
+
HoverEvents,
|
|
316
|
+
FocusEvents<HTMLDivElement>,
|
|
317
|
+
PressEvents,
|
|
318
|
+
Omit<GlobalDOMAttributes, 'onClick'> {
|
|
319
|
+
/**
|
|
320
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
321
|
+
* element. A function may be provided to compute the class based on component state.
|
|
322
|
+
*
|
|
323
|
+
* @default 'react-aria-Tag'
|
|
324
|
+
*/
|
|
325
|
+
className?: ClassNameOrFunction<TagRenderProps>;
|
|
326
|
+
/** A unique id for the tag. */
|
|
327
|
+
id?: Key;
|
|
328
|
+
/**
|
|
329
|
+
* A string representation of the tags's contents, used for accessibility.
|
|
330
|
+
* Required if children is not a plain text string.
|
|
331
|
+
*/
|
|
332
|
+
textValue?: string;
|
|
333
|
+
/** Whether the tag is disabled. */
|
|
334
|
+
isDisabled?: boolean;
|
|
335
|
+
/**
|
|
336
|
+
* Handler that is called when a user performs an action on the item. The exact user event depends
|
|
337
|
+
* on the collection's `selectionBehavior` prop and the interaction modality.
|
|
338
|
+
*/
|
|
339
|
+
onAction?: () => void;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* A Tag is an individual item within a TagList.
|
|
344
|
+
*/
|
|
345
|
+
export const Tag: (props: TagProps & { ref?: any }) => any = /*#__PURE__*/ createLeafComponent(
|
|
346
|
+
ItemNode,
|
|
347
|
+
// The third (item) parameter is declared so `render.length === 3` keeps the
|
|
348
|
+
// engine's "cannot be rendered outside a collection" guard; it is always
|
|
349
|
+
// provided when rendered from a collection node.
|
|
350
|
+
function Tag(props: TagProps, forwardedRef: any, item?: Node<unknown>): any {
|
|
351
|
+
const slot = S('Tag');
|
|
352
|
+
let state = useContext(ListStateContext)!;
|
|
353
|
+
let ref = useObjectRef<HTMLDivElement>(forwardedRef, subSlot(slot, 'objectRef'));
|
|
354
|
+
let { focusProps, isFocusVisible } = useFocusRing(
|
|
355
|
+
{ within: false },
|
|
356
|
+
subSlot(slot, 'focusRing'),
|
|
357
|
+
);
|
|
358
|
+
let { rowProps, gridCellProps, removeButtonProps, ...states } = useTag(
|
|
359
|
+
{ item: item! },
|
|
360
|
+
state,
|
|
361
|
+
ref,
|
|
362
|
+
subSlot(slot, 'tag'),
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
let { hoverProps, isHovered } = useHover(
|
|
366
|
+
{
|
|
367
|
+
isDisabled: !states.allowsSelection && !states.hasAction,
|
|
368
|
+
onHoverStart: item!.props.onHoverStart,
|
|
369
|
+
onHoverChange: item!.props.onHoverChange,
|
|
370
|
+
onHoverEnd: item!.props.onHoverEnd,
|
|
371
|
+
},
|
|
372
|
+
subSlot(slot, 'hover'),
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
let renderProps = useRenderProps(
|
|
376
|
+
{
|
|
377
|
+
...props,
|
|
378
|
+
id: undefined,
|
|
379
|
+
children: item!.rendered,
|
|
380
|
+
defaultClassName: 'react-aria-Tag',
|
|
381
|
+
values: {
|
|
382
|
+
...states,
|
|
383
|
+
isFocusVisible,
|
|
384
|
+
isHovered,
|
|
385
|
+
selectionMode: state.selectionManager.selectionMode,
|
|
386
|
+
selectionBehavior: state.selectionManager.selectionBehavior,
|
|
387
|
+
},
|
|
388
|
+
} as any,
|
|
389
|
+
subSlot(slot, 'render'),
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
useEffect(
|
|
393
|
+
() => {
|
|
394
|
+
if (!item!.textValue && process.env.NODE_ENV !== 'production') {
|
|
395
|
+
console.warn(
|
|
396
|
+
'A `textValue` prop is required for <Tag> elements with non-plain text children for accessibility.',
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
[item!.textValue],
|
|
401
|
+
subSlot(slot, 'textValueWarn'),
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
let DOMProps = filterDOMProps(props as any, { global: true });
|
|
405
|
+
delete DOMProps.id;
|
|
406
|
+
delete DOMProps.onClick;
|
|
407
|
+
|
|
408
|
+
return createElement(dom.div, {
|
|
409
|
+
ref,
|
|
410
|
+
...mergeProps(DOMProps, renderProps, rowProps, focusProps, hoverProps),
|
|
411
|
+
'data-selected': states.isSelected || undefined,
|
|
412
|
+
'data-disabled': states.isDisabled || undefined,
|
|
413
|
+
'data-hovered': isHovered || undefined,
|
|
414
|
+
'data-focused': states.isFocused || undefined,
|
|
415
|
+
'data-focus-visible': isFocusVisible || undefined,
|
|
416
|
+
'data-pressed': states.isPressed || undefined,
|
|
417
|
+
'data-allows-removing': states.allowsRemoving || undefined,
|
|
418
|
+
'data-selection-mode':
|
|
419
|
+
state.selectionManager.selectionMode === 'none'
|
|
420
|
+
? undefined
|
|
421
|
+
: state.selectionManager.selectionMode,
|
|
422
|
+
children: createElement('div', {
|
|
423
|
+
...gridCellProps,
|
|
424
|
+
style: { display: 'contents' },
|
|
425
|
+
children: createElement(Provider, {
|
|
426
|
+
values: [
|
|
427
|
+
[
|
|
428
|
+
ButtonContext,
|
|
429
|
+
{
|
|
430
|
+
slots: {
|
|
431
|
+
remove: removeButtonProps,
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
[CollectionRendererContext, DefaultCollectionRenderer],
|
|
436
|
+
[SelectionIndicatorContext, { isSelected: states.isSelected }],
|
|
437
|
+
] as any,
|
|
438
|
+
children: renderProps.children,
|
|
439
|
+
}),
|
|
440
|
+
}),
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Text.tsx).
|
|
2
|
+
// octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded ref
|
|
3
|
+
// is `props.ref`, passed into `useContextProps` explicitly and re-applied as the merged object
|
|
4
|
+
// ref; the plain-`.ts` component uses the S()/subSlot component-slot convention; React's
|
|
5
|
+
// HTMLAttributes prop bag → a structural record.
|
|
6
|
+
import { createContext, createElement } from 'octane';
|
|
7
|
+
|
|
8
|
+
import { S, subSlot } from '../internal';
|
|
9
|
+
import { type ContextValue, dom, type DOMRenderProps, useContextProps } from './utils';
|
|
10
|
+
|
|
11
|
+
// octane adaptation: structural prop bag (upstream extends React's HTMLAttributes).
|
|
12
|
+
type HTMLAttributes = Record<string, any>;
|
|
13
|
+
|
|
14
|
+
export interface TextProps extends HTMLAttributes, DOMRenderProps<any, any> {
|
|
15
|
+
elementType?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const TextContext = createContext<ContextValue<TextProps, HTMLElement>>({});
|
|
19
|
+
|
|
20
|
+
export function Text(props: TextProps): any {
|
|
21
|
+
const slot = S('Text');
|
|
22
|
+
let ref: any;
|
|
23
|
+
[props, ref] = useContextProps(props, props.ref, TextContext, subSlot(slot, 'ctx'));
|
|
24
|
+
let { elementType = 'span', ...domProps } = props;
|
|
25
|
+
let ElementType = dom[elementType];
|
|
26
|
+
return createElement(ElementType, { className: 'react-aria-Text', ...domProps, ref });
|
|
27
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/TextArea.tsx).
|
|
2
|
+
// octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded ref
|
|
3
|
+
// is `props.ref`, passed into `useContextProps` explicitly and re-applied as the merged object
|
|
4
|
+
// ref; the plain-`.ts` component uses the S()/subSlot component-slot convention; React's
|
|
5
|
+
// TextareaHTMLAttributes prop bag → a structural record. NATIVE EVENTS: this is a text control —
|
|
6
|
+
// per-keystroke wiring rides octane's native `onInput`; no synthetic `onChange` is added.
|
|
7
|
+
import type { HoverEvents } from '@react-types/shared';
|
|
8
|
+
import { createContext, createElement } from 'octane';
|
|
9
|
+
|
|
10
|
+
import { useFocusRing } from '../focus/useFocusRing';
|
|
11
|
+
import { useHover } from '../interactions/useHover';
|
|
12
|
+
import { S, subSlot } from '../internal';
|
|
13
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
14
|
+
import type { InputRenderProps } from './Input';
|
|
15
|
+
import {
|
|
16
|
+
type ClassNameOrFunction,
|
|
17
|
+
type ContextValue,
|
|
18
|
+
dom,
|
|
19
|
+
type StyleRenderProps,
|
|
20
|
+
useContextProps,
|
|
21
|
+
useRenderProps,
|
|
22
|
+
} from './utils';
|
|
23
|
+
|
|
24
|
+
// octane adaptation: structural prop bag (upstream extends React's TextareaHTMLAttributes).
|
|
25
|
+
type TextareaHTMLAttributes = Record<string, any>;
|
|
26
|
+
|
|
27
|
+
export interface TextAreaProps
|
|
28
|
+
extends
|
|
29
|
+
Omit<TextareaHTMLAttributes, 'className' | 'style'>,
|
|
30
|
+
HoverEvents,
|
|
31
|
+
StyleRenderProps<InputRenderProps, 'textarea'> {
|
|
32
|
+
/**
|
|
33
|
+
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
|
|
34
|
+
* element. A function may be provided to compute the class based on component state.
|
|
35
|
+
*
|
|
36
|
+
* @default 'react-aria-TextArea'
|
|
37
|
+
*/
|
|
38
|
+
className?: ClassNameOrFunction<InputRenderProps>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const TextAreaContext = createContext<ContextValue<TextAreaProps, HTMLTextAreaElement>>({});
|
|
42
|
+
|
|
43
|
+
let filterHoverProps = (props: TextAreaProps): TextAreaProps => {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
45
|
+
let { onHoverStart, onHoverChange, onHoverEnd, ...otherProps } = props;
|
|
46
|
+
return otherProps;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A textarea allows a user to input mult-line text.
|
|
51
|
+
*/
|
|
52
|
+
export function TextArea(props: TextAreaProps): any {
|
|
53
|
+
const slot = S('TextArea');
|
|
54
|
+
let ref: any;
|
|
55
|
+
[props, ref] = useContextProps(props, props.ref, TextAreaContext, subSlot(slot, 'ctx'));
|
|
56
|
+
|
|
57
|
+
let { hoverProps, isHovered } = useHover(props, subSlot(slot, 'hover'));
|
|
58
|
+
let { isFocused, isFocusVisible, focusProps } = useFocusRing(
|
|
59
|
+
{
|
|
60
|
+
isTextInput: true,
|
|
61
|
+
autoFocus: props.autoFocus,
|
|
62
|
+
},
|
|
63
|
+
subSlot(slot, 'focusRing'),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
let isInvalid = !!props['aria-invalid'] && props['aria-invalid'] !== 'false';
|
|
67
|
+
let renderProps = useRenderProps(
|
|
68
|
+
{
|
|
69
|
+
...props,
|
|
70
|
+
values: {
|
|
71
|
+
isHovered,
|
|
72
|
+
isFocused,
|
|
73
|
+
isFocusVisible,
|
|
74
|
+
isDisabled: props.disabled || false,
|
|
75
|
+
isInvalid,
|
|
76
|
+
},
|
|
77
|
+
defaultClassName: 'react-aria-TextArea',
|
|
78
|
+
},
|
|
79
|
+
subSlot(slot, 'render'),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return createElement(dom.textarea, {
|
|
83
|
+
...mergeProps(filterHoverProps(props), focusProps, hoverProps),
|
|
84
|
+
...renderProps,
|
|
85
|
+
ref,
|
|
86
|
+
'data-focused': isFocused || undefined,
|
|
87
|
+
'data-disabled': props.disabled || undefined,
|
|
88
|
+
'data-hovered': isHovered || undefined,
|
|
89
|
+
'data-focus-visible': isFocusVisible || undefined,
|
|
90
|
+
'data-invalid': isInvalid || undefined,
|
|
91
|
+
});
|
|
92
|
+
}
|