@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.
Files changed (93) hide show
  1. package/package.json +7 -5
  2. package/src/autocomplete/useAutocomplete.ts +656 -0
  3. package/src/collections/BaseCollection.ts +374 -0
  4. package/src/collections/CollectionBuilder.ts +343 -0
  5. package/src/collections/Document.ts +451 -0
  6. package/src/collections/Hidden.ts +84 -0
  7. package/src/collections/useCachedChildren.ts +103 -0
  8. package/src/components/Autocomplete.ts +124 -0
  9. package/src/components/Breadcrumbs.ts +170 -0
  10. package/src/components/Button.ts +209 -0
  11. package/src/components/Checkbox.ts +593 -0
  12. package/src/components/Collection.ts +301 -0
  13. package/src/components/ComboBox.ts +477 -0
  14. package/src/components/Dialog.ts +195 -0
  15. package/src/components/Disclosure.ts +322 -0
  16. package/src/components/DragAndDrop.ts +177 -0
  17. package/src/components/FieldError.ts +79 -0
  18. package/src/components/Form.ts +65 -0
  19. package/src/components/GridList.ts +1099 -0
  20. package/src/components/Group.ts +146 -0
  21. package/src/components/Header.ts +32 -0
  22. package/src/components/Heading.ts +45 -0
  23. package/src/components/Input.ts +139 -0
  24. package/src/components/Keyboard.ts +23 -0
  25. package/src/components/Label.ts +30 -0
  26. package/src/components/Link.ts +137 -0
  27. package/src/components/ListBox.ts +886 -0
  28. package/src/components/Menu.ts +685 -0
  29. package/src/components/Meter.ts +100 -0
  30. package/src/components/Modal.ts +352 -0
  31. package/src/components/NumberField.ts +221 -0
  32. package/src/components/OverlayArrow.ts +117 -0
  33. package/src/components/Popover.ts +385 -0
  34. package/src/components/ProgressBar.ts +112 -0
  35. package/src/components/RadioGroup.ts +542 -0
  36. package/src/components/SearchField.ts +208 -0
  37. package/src/components/Select.ts +505 -0
  38. package/src/components/SelectionIndicator.ts +52 -0
  39. package/src/components/Separator.ts +97 -0
  40. package/src/components/SharedElementTransition.ts +209 -0
  41. package/src/components/Slider.ts +472 -0
  42. package/src/components/Switch.ts +449 -0
  43. package/src/components/Tabs.ts +675 -0
  44. package/src/components/TagGroup.ts +443 -0
  45. package/src/components/Text.ts +27 -0
  46. package/src/components/TextArea.ts +92 -0
  47. package/src/components/TextField.ts +200 -0
  48. package/src/components/ToggleButton.ts +147 -0
  49. package/src/components/ToggleButtonGroup.ts +106 -0
  50. package/src/components/Toolbar.ts +74 -0
  51. package/src/components/Tooltip.ts +249 -0
  52. package/src/components/index.ts +302 -0
  53. package/src/components/useDragAndDrop.ts +164 -0
  54. package/src/components/utils.ts +521 -0
  55. package/src/intl/autocomplete/ar-AE.json +3 -0
  56. package/src/intl/autocomplete/bg-BG.json +3 -0
  57. package/src/intl/autocomplete/cs-CZ.json +3 -0
  58. package/src/intl/autocomplete/da-DK.json +3 -0
  59. package/src/intl/autocomplete/de-DE.json +3 -0
  60. package/src/intl/autocomplete/el-GR.json +3 -0
  61. package/src/intl/autocomplete/en-US.json +3 -0
  62. package/src/intl/autocomplete/es-ES.json +3 -0
  63. package/src/intl/autocomplete/et-EE.json +3 -0
  64. package/src/intl/autocomplete/fi-FI.json +3 -0
  65. package/src/intl/autocomplete/fr-FR.json +3 -0
  66. package/src/intl/autocomplete/he-IL.json +3 -0
  67. package/src/intl/autocomplete/hr-HR.json +3 -0
  68. package/src/intl/autocomplete/hu-HU.json +3 -0
  69. package/src/intl/autocomplete/index.ts +75 -0
  70. package/src/intl/autocomplete/it-IT.json +3 -0
  71. package/src/intl/autocomplete/ja-JP.json +3 -0
  72. package/src/intl/autocomplete/ko-KR.json +3 -0
  73. package/src/intl/autocomplete/lt-LT.json +3 -0
  74. package/src/intl/autocomplete/lv-LV.json +3 -0
  75. package/src/intl/autocomplete/nb-NO.json +3 -0
  76. package/src/intl/autocomplete/nl-NL.json +3 -0
  77. package/src/intl/autocomplete/pl-PL.json +3 -0
  78. package/src/intl/autocomplete/pt-BR.json +3 -0
  79. package/src/intl/autocomplete/pt-PT.json +3 -0
  80. package/src/intl/autocomplete/ro-RO.json +3 -0
  81. package/src/intl/autocomplete/ru-RU.json +3 -0
  82. package/src/intl/autocomplete/sk-SK.json +3 -0
  83. package/src/intl/autocomplete/sl-SI.json +3 -0
  84. package/src/intl/autocomplete/sr-SP.json +3 -0
  85. package/src/intl/autocomplete/sv-SE.json +3 -0
  86. package/src/intl/autocomplete/tr-TR.json +3 -0
  87. package/src/intl/autocomplete/uk-UA.json +3 -0
  88. package/src/intl/autocomplete/zh-CN.json +3 -0
  89. package/src/intl/autocomplete/zh-TW.json +3 -0
  90. package/src/stately/autocomplete/useAutocompleteState.ts +85 -0
  91. package/src/utils/animation.ts +149 -0
  92. package/src/utils/useLoadMoreSentinel.ts +78 -0
  93. package/src/utils/useViewportSize.ts +123 -0
@@ -0,0 +1,52 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/SelectionIndicator.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 on the underlying SharedElement; the plain-`.ts` component uses the S()/subSlot
5
+ // component-slot convention.
6
+ import { createContext, createElement } from 'octane';
7
+
8
+ import { S, subSlot } from '../internal';
9
+ import {
10
+ SharedElement,
11
+ type SharedElementPropsBase,
12
+ type SharedElementRenderProps,
13
+ } from './SharedElementTransition';
14
+ import { type ClassNameOrFunction, type ContextValue, useContextProps } from './utils';
15
+
16
+ export interface SelectionIndicatorProps extends SharedElementPropsBase {
17
+ /**
18
+ * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
19
+ * element. A function may be provided to compute the class based on component state.
20
+ *
21
+ * @default 'react-aria-SelectionIndicator'
22
+ */
23
+ className?: ClassNameOrFunction<SharedElementRenderProps>;
24
+ /**
25
+ * Whether the SelectionIndicator is visible. This is usually set automatically by the parent
26
+ * component.
27
+ */
28
+ isSelected?: boolean;
29
+ }
30
+
31
+ export const SelectionIndicatorContext = createContext<
32
+ ContextValue<SelectionIndicatorProps, HTMLDivElement>
33
+ >({
34
+ isSelected: false,
35
+ });
36
+
37
+ /**
38
+ * An animated indicator of selection state within a group of items.
39
+ */
40
+ export function SelectionIndicator(props: SelectionIndicatorProps): any {
41
+ const slot = S('SelectionIndicator');
42
+ let ref: any;
43
+ [props, ref] = useContextProps(props, props.ref, SelectionIndicatorContext, subSlot(slot, 'ctx'));
44
+ let { isSelected, ...otherProps } = props;
45
+ return createElement(SharedElement, {
46
+ ...otherProps,
47
+ ref,
48
+ className: props.className || 'react-aria-SelectionIndicator',
49
+ name: 'SelectionIndicator',
50
+ isVisible: isSelected,
51
+ });
52
+ }
@@ -0,0 +1,97 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Separator.tsx).
2
+ // octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded ref
3
+ // arrives positionally from `createLeafComponent` (which forwards `props.ref`); the
4
+ // plain-`.ts` component uses the S()/subSlot component-slot convention; the collection engine
5
+ // classes come from the binding's `../collections/BaseCollection` port.
6
+ import { createContext, createElement } from 'octane';
7
+
8
+ import { BaseCollection, CollectionNode } from '../collections/BaseCollection';
9
+ import { createLeafComponent } from '../collections/CollectionBuilder';
10
+ import { S, subSlot } from '../internal';
11
+ import { type SeparatorProps as AriaSeparatorProps, useSeparator } from '../separator/useSeparator';
12
+ import { filterDOMProps } from '../utils/filterDOMProps';
13
+ import { mergeProps } from '../utils/mergeProps';
14
+ import {
15
+ type ContextValue,
16
+ dom,
17
+ type DOMRenderProps,
18
+ type SlotProps,
19
+ type StyleProps,
20
+ useContextProps,
21
+ } from './utils';
22
+
23
+ // octane adaptation: structural bag (upstream's `GlobalDOMAttributes` drags React handler types).
24
+ type GlobalDOMAttributes = Record<string, any>;
25
+
26
+ export interface SeparatorProps
27
+ extends
28
+ AriaSeparatorProps,
29
+ StyleProps,
30
+ SlotProps,
31
+ DOMRenderProps<'hr' | 'div', undefined>,
32
+ GlobalDOMAttributes {
33
+ /**
34
+ * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
35
+ * element.
36
+ *
37
+ * @default 'react-aria-Separator'
38
+ */
39
+ className?: string;
40
+ }
41
+
42
+ export const SeparatorContext = createContext<ContextValue<SeparatorProps, HTMLElement>>({});
43
+
44
+ export class SeparatorNode extends CollectionNode<any> {
45
+ static readonly type = 'separator';
46
+
47
+ filter(
48
+ collection: BaseCollection<any>,
49
+ newCollection: BaseCollection<any>,
50
+ ): CollectionNode<any> | null {
51
+ let prevItem = newCollection.getItem(this.prevKey!);
52
+ if (prevItem && prevItem.type !== 'separator') {
53
+ let clone = this.clone();
54
+ newCollection.addDescendants(clone, collection);
55
+ return clone;
56
+ }
57
+
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * A separator is a visual divider between two groups of content, e.g. groups of menu items or
64
+ * sections of a page.
65
+ */
66
+ export const Separator = /*#__PURE__*/ createLeafComponent(
67
+ SeparatorNode,
68
+ function Separator(props: SeparatorProps, ref: any) {
69
+ const slot = S('Separator');
70
+ [props, ref] = useContextProps(props, ref, SeparatorContext, subSlot(slot, 'ctx'));
71
+
72
+ let { elementType, orientation, style, className, slot: slotProp, ...otherProps } = props;
73
+ let Element = elementType || 'hr';
74
+ if (Element === 'hr' && orientation === 'vertical') {
75
+ Element = 'div';
76
+ }
77
+
78
+ let ElementType = dom[Element];
79
+
80
+ let { separatorProps } = useSeparator({
81
+ ...otherProps,
82
+ elementType,
83
+ orientation,
84
+ });
85
+
86
+ let DOMProps = filterDOMProps(props, { global: true });
87
+
88
+ return createElement(ElementType, {
89
+ render: props.render,
90
+ ...mergeProps(DOMProps, separatorProps),
91
+ style,
92
+ className: className ?? 'react-aria-Separator',
93
+ ref,
94
+ slot: slotProp || undefined,
95
+ });
96
+ },
97
+ );
@@ -0,0 +1,209 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/SharedElementTransition.tsx).
2
+ // octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded ref
3
+ // is `props.ref`, adapted with `useObjectRef` exactly like upstream's forwarded ref; `flushSync`
4
+ // comes from `'octane'` (upstream: react-dom); the plain-`.ts` components use the S()/subSlot
5
+ // component-slot convention (`useContext` stays slotless — context-identity keyed); React's
6
+ // HTMLAttributes/ReactNode/RefObject types → structural aliases; the explicit dep array is
7
+ // preserved verbatim; the FLIP snapshot/animation logic is verbatim — it reads live rects,
8
+ // computed styles, and Web Animations (all inert in jsdom, real in browsers).
9
+ import { createContext, createElement, flushSync, useContext, useRef, useState } from 'octane';
10
+
11
+ import { S, subSlot } from '../internal';
12
+ import { useLayoutEffect } from '../utils/useLayoutEffect';
13
+ import { useObjectRef } from '../utils/useObjectRef';
14
+ import { dom, type RenderProps, useRenderProps } from './utils';
15
+
16
+ // octane adaptation: structural aliases for the React types upstream drags along.
17
+ type HTMLAttributes = Record<string, any>;
18
+ type ReactNode = any;
19
+ type RefObject<T> = { current: T };
20
+
21
+ interface Snapshot {
22
+ rect: DOMRect;
23
+ style: [string, string][];
24
+ }
25
+
26
+ const SharedElementContext = createContext<RefObject<{ [name: string]: Snapshot }> | null>(null);
27
+
28
+ export interface SharedElementTransitionProps {
29
+ children: ReactNode;
30
+ }
31
+
32
+ /**
33
+ * A scope for SharedElements, which animate between parents.
34
+ */
35
+ export function SharedElementTransition(props: SharedElementTransitionProps): any {
36
+ const slot = S('SharedElementTransition');
37
+ let ref = useRef<{ [name: string]: Snapshot }>({}, subSlot(slot, 'scope'));
38
+ return createElement(SharedElementContext.Provider, { value: ref, children: props.children });
39
+ }
40
+
41
+ export interface SharedElementRenderProps {
42
+ /**
43
+ * Whether the element is currently entering.
44
+ *
45
+ * @selector [data-entering]
46
+ */
47
+ isEntering: boolean;
48
+ /**
49
+ * Whether the element is currently exiting.
50
+ *
51
+ * @selector [data-exiting]
52
+ */
53
+ isExiting: boolean;
54
+ }
55
+
56
+ export interface SharedElementPropsBase
57
+ extends
58
+ Omit<HTMLAttributes, 'children' | 'className' | 'style'>,
59
+ RenderProps<SharedElementRenderProps> {}
60
+
61
+ export interface SharedElementProps extends SharedElementPropsBase {
62
+ name: string;
63
+ isVisible?: boolean;
64
+ }
65
+
66
+ /**
67
+ * An element that animates between its old and new position when moving between parents.
68
+ */
69
+ export function SharedElement(props: SharedElementProps): any {
70
+ const slot = S('SharedElement');
71
+ let {
72
+ name,
73
+ isVisible = true,
74
+ children,
75
+ className,
76
+ style,
77
+ render,
78
+ ref: forwardedRef,
79
+ ...divProps
80
+ } = props;
81
+ let [state, setState] = useState<string>(
82
+ isVisible ? 'visible' : 'hidden',
83
+ subSlot(slot, 'state'),
84
+ );
85
+ let scopeRef = useContext(SharedElementContext);
86
+ if (!scopeRef) {
87
+ throw new Error('<SharedElement> must be rendered inside a <SharedElementTransition>');
88
+ }
89
+
90
+ if (isVisible && state === 'hidden') {
91
+ setState('visible');
92
+ }
93
+
94
+ let ref = useObjectRef<HTMLDivElement>(forwardedRef, subSlot(slot, 'objectRef'));
95
+ useLayoutEffect(
96
+ () => {
97
+ let element = ref.current;
98
+ let scope = scopeRef.current;
99
+ let prevSnapshot = scope[name];
100
+ let frame: number | null = null;
101
+
102
+ if (element && isVisible && prevSnapshot) {
103
+ // Element is transitioning from a previous instance.
104
+ setState('visible');
105
+ let animations = element.getAnimations();
106
+
107
+ // Set properties to animate from.
108
+ let values = prevSnapshot.style.map(([property, prevValue]) => {
109
+ let value = (element.style as any)[property];
110
+ if (property === 'translate') {
111
+ let prevRect = prevSnapshot.rect;
112
+ let currentItem = element.getBoundingClientRect();
113
+ let deltaX = prevRect.left - currentItem?.left;
114
+ let deltaY = prevRect.top - currentItem?.top;
115
+ element.style.translate = `${deltaX}px ${deltaY}px`;
116
+ } else {
117
+ (element.style as any)[property] = prevValue;
118
+ }
119
+ return [property, value];
120
+ });
121
+
122
+ // Cancel any new animations triggered by these properties.
123
+ for (let a of element.getAnimations()) {
124
+ if (!animations.includes(a)) {
125
+ a.cancel();
126
+ }
127
+ }
128
+
129
+ // Remove overrides after one frame to animate to the current values.
130
+ frame = requestAnimationFrame(() => {
131
+ frame = null;
132
+ for (let [property, value] of values) {
133
+ (element!.style as any)[property] = value;
134
+ }
135
+ });
136
+
137
+ delete scope[name];
138
+ } else if (element && isVisible && !prevSnapshot) {
139
+ // No previous instance exists, apply the entering state.
140
+ queueMicrotask(() => flushSync(() => setState('entering')));
141
+ frame = requestAnimationFrame(() => {
142
+ frame = null;
143
+ setState('visible');
144
+ });
145
+ } else if (element && !isVisible) {
146
+ // Wait until layout effects finish, and check if a snapshot still exists.
147
+ // If so, no new SharedElement consumed it, so enter the exiting state.
148
+ queueMicrotask(() => {
149
+ if (scope[name]) {
150
+ delete scope[name];
151
+ flushSync(() => setState('exiting'));
152
+ Promise.all(element!.getAnimations().map((a) => a.finished))
153
+ .then(() => setState('hidden'))
154
+ .catch(() => {});
155
+ } else {
156
+ // Snapshot was consumed by another instance, unmount.
157
+ setState('hidden');
158
+ }
159
+ });
160
+ }
161
+
162
+ return () => {
163
+ if (frame != null) {
164
+ cancelAnimationFrame(frame);
165
+ }
166
+
167
+ if (element && element.isConnected && !element.hasAttribute('data-exiting')) {
168
+ // On unmount, store a snapshot of the rectangle and computed style for transitioning properties.
169
+ let style = window.getComputedStyle(element);
170
+ if (style.transitionProperty !== 'none') {
171
+ let transitionProperty = style.transitionProperty.split(/\s*,\s*/);
172
+ scope[name] = {
173
+ rect: element.getBoundingClientRect(),
174
+ style: transitionProperty.map((p) => [p, (style as any)[p]] as [string, string]),
175
+ };
176
+ }
177
+ }
178
+ };
179
+ },
180
+ [ref, scopeRef, name, isVisible],
181
+ subSlot(slot, 'transition'),
182
+ );
183
+
184
+ let renderProps = useRenderProps(
185
+ {
186
+ children,
187
+ className,
188
+ style,
189
+ render,
190
+ values: {
191
+ isEntering: state === 'entering',
192
+ isExiting: state === 'exiting',
193
+ },
194
+ },
195
+ subSlot(slot, 'renderProps'),
196
+ );
197
+
198
+ if (state === 'hidden') {
199
+ return null;
200
+ }
201
+
202
+ return createElement(dom.div, {
203
+ ...divProps,
204
+ ...renderProps,
205
+ ref,
206
+ 'data-entering': state === 'entering' || undefined,
207
+ 'data-exiting': state === 'exiting' || undefined,
208
+ });
209
+ }