@octanejs/aria 0.0.3 → 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,146 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Group.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 type { AriaLabelingProps, DOMProps } from '@react-types/shared';
7
+ import { createContext, createElement } from 'octane';
8
+
9
+ import { useFocusRing } from '../focus/useFocusRing';
10
+ import { type HoverProps, useHover } from '../interactions/useHover';
11
+ import { S, subSlot } from '../internal';
12
+ import { mergeProps } from '../utils/mergeProps';
13
+ import {
14
+ type ClassNameOrFunction,
15
+ type ContextValue,
16
+ dom,
17
+ type RenderProps,
18
+ type SlotProps,
19
+ useContextProps,
20
+ useRenderProps,
21
+ } from './utils';
22
+
23
+ // octane adaptation: structural prop bag (upstream extends React's HTMLAttributes).
24
+ type HTMLAttributes = Record<string, any>;
25
+
26
+ export interface GroupRenderProps {
27
+ /**
28
+ * Whether the group is currently hovered with a mouse.
29
+ *
30
+ * @selector [data-hovered]
31
+ */
32
+ isHovered: boolean;
33
+ /**
34
+ * Whether an element within the group is focused, either via a mouse or keyboard.
35
+ *
36
+ * @selector [data-focus-within]
37
+ */
38
+ isFocusWithin: boolean;
39
+ /**
40
+ * Whether an element within the group is keyboard focused.
41
+ *
42
+ * @selector [data-focus-visible]
43
+ */
44
+ isFocusVisible: boolean;
45
+ /**
46
+ * Whether the group is disabled.
47
+ *
48
+ * @selector [data-disabled]
49
+ */
50
+ isDisabled: boolean;
51
+ /**
52
+ * Whether the group is invalid.
53
+ *
54
+ * @selector [data-invalid]
55
+ */
56
+ isInvalid: boolean;
57
+ }
58
+
59
+ export interface GroupProps
60
+ extends
61
+ AriaLabelingProps,
62
+ Omit<HTMLAttributes, 'children' | 'className' | 'style' | 'render' | 'role' | 'slot'>,
63
+ DOMProps,
64
+ HoverProps,
65
+ RenderProps<GroupRenderProps>,
66
+ SlotProps {
67
+ /**
68
+ * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
69
+ * element. A function may be provided to compute the class based on component state.
70
+ *
71
+ * @default 'react-aria-Group'
72
+ */
73
+ className?: ClassNameOrFunction<GroupRenderProps>;
74
+ /** Whether the group is disabled. */
75
+ isDisabled?: boolean;
76
+ /** Whether the group is invalid. */
77
+ isInvalid?: boolean;
78
+ /** Whether the group is read only. */
79
+ isReadOnly?: boolean;
80
+ /**
81
+ * An accessibility role for the group. By default, this is set to `'group'`.
82
+ * Use `'region'` when the contents of the group is important enough to be
83
+ * included in the page table of contents. Use `'presentation'` if the group
84
+ * is visual only and does not represent a semantic grouping of controls.
85
+ *
86
+ * @default 'group'
87
+ */
88
+ role?: 'group' | 'region' | 'presentation';
89
+ }
90
+
91
+ export const GroupContext = createContext<ContextValue<GroupProps, HTMLDivElement>>({});
92
+
93
+ /**
94
+ * A group represents a set of related UI controls, and supports interactive states for styling.
95
+ */
96
+ export function Group(props: GroupProps): any {
97
+ const slot = S('Group');
98
+ let ref: any;
99
+ [props, ref] = useContextProps(props, props.ref, GroupContext, subSlot(slot, 'ctx'));
100
+ let {
101
+ isDisabled,
102
+ isInvalid,
103
+ isReadOnly,
104
+ onHoverStart,
105
+ onHoverChange,
106
+ onHoverEnd,
107
+ ...otherProps
108
+ } = props;
109
+ isDisabled ??= !!props['aria-disabled'] && props['aria-disabled'] !== 'false';
110
+ isInvalid ??= !!props['aria-invalid'] && props['aria-invalid'] !== 'false';
111
+
112
+ let { hoverProps, isHovered } = useHover(
113
+ { onHoverStart, onHoverChange, onHoverEnd, isDisabled },
114
+ subSlot(slot, 'hover'),
115
+ );
116
+ let { isFocused, isFocusVisible, focusProps } = useFocusRing(
117
+ {
118
+ within: true,
119
+ },
120
+ subSlot(slot, 'focusRing'),
121
+ );
122
+
123
+ let renderProps = useRenderProps(
124
+ {
125
+ ...props,
126
+ values: { isHovered, isFocusWithin: isFocused, isFocusVisible, isDisabled, isInvalid },
127
+ defaultClassName: 'react-aria-Group',
128
+ },
129
+ subSlot(slot, 'render'),
130
+ );
131
+
132
+ return createElement(dom.div, {
133
+ ...mergeProps(otherProps, focusProps, hoverProps),
134
+ ...renderProps,
135
+ ref,
136
+ role: props.role ?? 'group',
137
+ slot: props.slot ?? undefined,
138
+ 'data-focus-within': isFocused || undefined,
139
+ 'data-hovered': isHovered || undefined,
140
+ 'data-focus-visible': isFocusVisible || undefined,
141
+ 'data-disabled': isDisabled || undefined,
142
+ 'data-invalid': isInvalid || undefined,
143
+ 'data-readonly': isReadOnly || undefined,
144
+ children: renderProps.children,
145
+ });
146
+ }
@@ -0,0 +1,32 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Header.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; React's
5
+ // HTMLAttributes prop bag → a structural record.
6
+ import { createContext, createElement } from 'octane';
7
+
8
+ import { HeaderNode } from '../collections/BaseCollection';
9
+ import { createLeafComponent } from '../collections/CollectionBuilder';
10
+ import { S, subSlot } from '../internal';
11
+ import { type ContextValue, dom, type DOMRenderProps, useContextProps } from './utils';
12
+
13
+ // octane adaptation: structural prop bag (upstream extends React's HTMLAttributes).
14
+ type HTMLAttributes = Record<string, any>;
15
+
16
+ export interface HeaderProps extends HTMLAttributes, DOMRenderProps<'header', undefined> {}
17
+
18
+ export const HeaderContext = createContext<ContextValue<HeaderProps, HTMLElement>>({});
19
+
20
+ export const Header = /*#__PURE__*/ createLeafComponent(
21
+ HeaderNode,
22
+ function Header(props: HeaderProps, ref: any) {
23
+ const slot = S('Header');
24
+ [props, ref] = useContextProps(props, ref, HeaderContext, subSlot(slot, 'ctx'));
25
+ return createElement(dom.header, {
26
+ className: 'react-aria-Header',
27
+ ...props,
28
+ ref,
29
+ children: props.children,
30
+ });
31
+ },
32
+ );
@@ -0,0 +1,45 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Heading.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 HeadingProps extends HTMLAttributes, DOMRenderProps<'h1', undefined> {
15
+ /**
16
+ * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
17
+ * element.
18
+ *
19
+ * @default 'react-aria-Heading'
20
+ */
21
+ className?: string;
22
+ /**
23
+ * The heading level.
24
+ *
25
+ * @default 3
26
+ */
27
+ level?: number;
28
+ }
29
+
30
+ export const HeadingContext = createContext<ContextValue<HeadingProps, HTMLHeadingElement>>({});
31
+
32
+ export function Heading(props: HeadingProps): any {
33
+ const slot = S('Heading');
34
+ let ref: any;
35
+ [props, ref] = useContextProps(props, props.ref, HeadingContext, subSlot(slot, 'ctx'));
36
+ let { children, level = 3, className, ...domProps } = props;
37
+ let Element = dom[`h${level}`];
38
+
39
+ return createElement(Element, {
40
+ ...domProps,
41
+ ref,
42
+ className: className ?? 'react-aria-Heading',
43
+ children,
44
+ });
45
+ }
@@ -0,0 +1,139 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Input.tsx).
2
+ // octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded ref
3
+ // arrives positionally from `createHideableComponent` (which forwards `props.ref`); the
4
+ // plain-`.ts` component uses the S()/subSlot component-slot convention; React's
5
+ // InputHTMLAttributes prop bag → a structural record. NATIVE EVENTS: this is a text control —
6
+ // per-keystroke wiring rides octane's native `onInput` (props produced by useTextField et al.
7
+ // pass through unchanged); no synthetic `onChange` is added.
8
+ import type { HoverEvents } from '@react-types/shared';
9
+ import { createContext, createElement } from 'octane';
10
+
11
+ import { createHideableComponent } from '../collections/Hidden';
12
+ import { useFocusRing } from '../focus/useFocusRing';
13
+ import { useHover } from '../interactions/useHover';
14
+ import { S, subSlot } from '../internal';
15
+ import { mergeProps } from '../utils/mergeProps';
16
+ import {
17
+ type ClassNameOrFunction,
18
+ type ContextValue,
19
+ dom,
20
+ type StyleRenderProps,
21
+ useContextProps,
22
+ useRenderProps,
23
+ } from './utils';
24
+
25
+ // octane adaptation: structural prop bag (upstream extends React's InputHTMLAttributes).
26
+ type InputHTMLAttributes = Record<string, any>;
27
+
28
+ export interface InputRenderProps {
29
+ /**
30
+ * Whether the input is currently hovered with a mouse.
31
+ *
32
+ * @selector [data-hovered]
33
+ */
34
+ isHovered: boolean;
35
+ /**
36
+ * Whether the input is focused, either via a mouse or keyboard.
37
+ *
38
+ * @selector [data-focused]
39
+ */
40
+ isFocused: boolean;
41
+ /**
42
+ * Whether the input is keyboard focused.
43
+ *
44
+ * @selector [data-focus-visible]
45
+ */
46
+ isFocusVisible: boolean;
47
+ /**
48
+ * Whether the input is disabled.
49
+ *
50
+ * @selector [data-disabled]
51
+ */
52
+ isDisabled: boolean;
53
+ /**
54
+ * Whether the input is invalid.
55
+ *
56
+ * @selector [data-invalid]
57
+ */
58
+ isInvalid: boolean;
59
+ }
60
+
61
+ export interface InputProps
62
+ extends
63
+ Omit<InputHTMLAttributes, 'className' | 'style'>,
64
+ HoverEvents,
65
+ StyleRenderProps<InputRenderProps, 'input'> {
66
+ /**
67
+ * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
68
+ * element. A function may be provided to compute the class based on component state.
69
+ *
70
+ * @default 'react-aria-Input'
71
+ */
72
+ className?: ClassNameOrFunction<InputRenderProps>;
73
+ /**
74
+ * Temporary text that occupies the text input when it is empty.
75
+ * See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/placeholder).
76
+ */
77
+ placeholder?: string;
78
+ }
79
+
80
+ export const InputContext = createContext<ContextValue<InputProps, HTMLInputElement>>({});
81
+
82
+ let filterHoverProps = (props: InputProps) => {
83
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
+ let { onHoverStart, onHoverChange, onHoverEnd, ...otherProps } = props;
85
+ return otherProps;
86
+ };
87
+
88
+ /**
89
+ * An input allows a user to input text.
90
+ */
91
+ export const Input = /*#__PURE__*/ createHideableComponent(function Input(
92
+ props: InputProps,
93
+ ref: any,
94
+ ) {
95
+ const slot = S('Input');
96
+ [props, ref] = useContextProps(props, ref, InputContext, subSlot(slot, 'ctx'));
97
+
98
+ let { hoverProps, isHovered } = useHover(
99
+ {
100
+ ...props,
101
+ isDisabled: props.disabled,
102
+ },
103
+ subSlot(slot, 'hover'),
104
+ );
105
+ let { isFocused, isFocusVisible, focusProps } = useFocusRing(
106
+ {
107
+ isTextInput: true,
108
+ autoFocus: props.autoFocus,
109
+ },
110
+ subSlot(slot, 'focusRing'),
111
+ );
112
+
113
+ let isInvalid = !!props['aria-invalid'] && props['aria-invalid'] !== 'false';
114
+ let renderProps = useRenderProps(
115
+ {
116
+ ...props,
117
+ values: {
118
+ isHovered,
119
+ isFocused,
120
+ isFocusVisible,
121
+ isDisabled: props.disabled || false,
122
+ isInvalid,
123
+ },
124
+ defaultClassName: 'react-aria-Input',
125
+ },
126
+ subSlot(slot, 'render'),
127
+ );
128
+
129
+ return createElement(dom.input, {
130
+ ...mergeProps(filterHoverProps(props), focusProps, hoverProps),
131
+ ...renderProps,
132
+ ref,
133
+ 'data-focused': isFocused || undefined,
134
+ 'data-disabled': props.disabled || undefined,
135
+ 'data-hovered': isHovered || undefined,
136
+ 'data-focus-visible': isFocusVisible || undefined,
137
+ 'data-invalid': isInvalid || undefined,
138
+ });
139
+ });
@@ -0,0 +1,23 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Keyboard.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 KeyboardProps extends HTMLAttributes, DOMRenderProps<'kbd', undefined> {}
15
+
16
+ export const KeyboardContext = createContext<ContextValue<KeyboardProps, HTMLElement>>({});
17
+
18
+ export function Keyboard(props: KeyboardProps): any {
19
+ const slot = S('Keyboard');
20
+ let ref: any;
21
+ [props, ref] = useContextProps(props, props.ref, KeyboardContext, subSlot(slot, 'ctx'));
22
+ return createElement(dom.kbd, { dir: 'ltr', ...props, ref });
23
+ }
@@ -0,0 +1,30 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Label.tsx).
2
+ // octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef — the forwarded ref
3
+ // arrives positionally from `createHideableComponent` (which forwards `props.ref`); the
4
+ // plain-`.ts` component uses the S()/subSlot component-slot convention; React's
5
+ // LabelHTMLAttributes prop bag → a structural record.
6
+ import { createContext, createElement } from 'octane';
7
+
8
+ import { createHideableComponent } from '../collections/Hidden';
9
+ import { S, subSlot } from '../internal';
10
+ import { type ContextValue, dom, type DOMRenderProps, useContextProps } from './utils';
11
+
12
+ // octane adaptation: structural prop bag (upstream extends React's LabelHTMLAttributes).
13
+ type LabelHTMLAttributes = Record<string, any>;
14
+
15
+ export interface LabelProps extends LabelHTMLAttributes, DOMRenderProps<'label', undefined> {
16
+ elementType?: string;
17
+ }
18
+
19
+ export const LabelContext = createContext<ContextValue<LabelProps, HTMLLabelElement>>({});
20
+
21
+ export const Label = /*#__PURE__*/ createHideableComponent(function Label(
22
+ props: LabelProps,
23
+ ref: any,
24
+ ) {
25
+ const slot = S('Label');
26
+ [props, ref] = useContextProps(props, ref, LabelContext, subSlot(slot, 'ctx'));
27
+ let { elementType = 'label', ...labelProps } = props;
28
+ let ElementType = dom[elementType];
29
+ return createElement(ElementType, { className: 'react-aria-Label', ...labelProps, ref });
30
+ });
@@ -0,0 +1,137 @@
1
+ // Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/Link.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.
5
+ import type { DOMProps, HoverEvents } from '@react-types/shared';
6
+ import { createContext, createElement } from 'octane';
7
+
8
+ import { useFocusRing } from '../focus/useFocusRing';
9
+ import { useHover } from '../interactions/useHover';
10
+ import { S, subSlot } from '../internal';
11
+ import { type AriaLinkOptions, useLink } from '../link/useLink';
12
+ import { filterDOMProps } from '../utils/filterDOMProps';
13
+ import { mergeProps } from '../utils/mergeProps';
14
+ import {
15
+ type ClassNameOrFunction,
16
+ type ContextValue,
17
+ dom,
18
+ type PossibleLinkDOMRenderProps,
19
+ type RenderProps,
20
+ type SlotProps,
21
+ useContextProps,
22
+ useRenderProps,
23
+ } from './utils';
24
+
25
+ // octane adaptation: structural bag (upstream's `GlobalDOMAttributes` drags React handler types).
26
+ type GlobalDOMAttributes = Record<string, any>;
27
+
28
+ export interface LinkProps
29
+ extends
30
+ Omit<AriaLinkOptions, 'elementType'>,
31
+ HoverEvents,
32
+ Omit<RenderProps<LinkRenderProps>, 'render'>,
33
+ PossibleLinkDOMRenderProps<'span', LinkRenderProps>,
34
+ SlotProps,
35
+ DOMProps,
36
+ Omit<GlobalDOMAttributes, 'onClick'> {
37
+ /**
38
+ * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the
39
+ * element. A function may be provided to compute the class based on component state.
40
+ *
41
+ * @default 'react-aria-Link'
42
+ */
43
+ className?: ClassNameOrFunction<LinkRenderProps>;
44
+ }
45
+
46
+ export interface LinkRenderProps {
47
+ /**
48
+ * Whether the link is the current item within a list.
49
+ *
50
+ * @selector [data-current]
51
+ */
52
+ isCurrent: boolean;
53
+ /**
54
+ * Whether the link is currently hovered with a mouse.
55
+ *
56
+ * @selector [data-hovered]
57
+ */
58
+ isHovered: boolean;
59
+ /**
60
+ * Whether the link is currently in a pressed state.
61
+ *
62
+ * @selector [data-pressed]
63
+ */
64
+ isPressed: boolean;
65
+ /**
66
+ * Whether the link is focused, either via a mouse or keyboard.
67
+ *
68
+ * @selector [data-focused]
69
+ */
70
+ isFocused: boolean;
71
+ /**
72
+ * Whether the link is keyboard focused.
73
+ *
74
+ * @selector [data-focus-visible]
75
+ */
76
+ isFocusVisible: boolean;
77
+ /**
78
+ * Whether the link is disabled.
79
+ *
80
+ * @selector [data-disabled]
81
+ */
82
+ isDisabled: boolean;
83
+ }
84
+
85
+ export const LinkContext = createContext<ContextValue<LinkProps, HTMLAnchorElement>>(null);
86
+
87
+ /**
88
+ * A link allows a user to navigate to another page or resource within a web page
89
+ * or application.
90
+ */
91
+ export function Link(props: LinkProps): any {
92
+ const slot = S('Link');
93
+ let ref: any;
94
+ [props, ref] = useContextProps(props, props.ref, LinkContext, subSlot(slot, 'ctx'));
95
+
96
+ let elementType = props.href && !props.isDisabled ? 'a' : 'span';
97
+ let { linkProps, isPressed } = useLink({ ...props, elementType }, ref, subSlot(slot, 'link'));
98
+ let ElementType = dom[elementType];
99
+
100
+ let { hoverProps, isHovered } = useHover(props, subSlot(slot, 'hover'));
101
+ let { focusProps, isFocused, isFocusVisible } = useFocusRing(
102
+ undefined,
103
+ subSlot(slot, 'focusRing'),
104
+ );
105
+
106
+ let renderProps = useRenderProps<LinkRenderProps, 'span' | 'a'>(
107
+ {
108
+ ...props,
109
+ defaultClassName: 'react-aria-Link',
110
+ values: {
111
+ isCurrent: !!props['aria-current'],
112
+ isDisabled: props.isDisabled || false,
113
+ isPressed,
114
+ isHovered,
115
+ isFocused,
116
+ isFocusVisible,
117
+ },
118
+ },
119
+ subSlot(slot, 'render'),
120
+ );
121
+
122
+ let DOMProps = filterDOMProps(props, { global: true });
123
+ delete DOMProps.onClick;
124
+
125
+ return createElement(ElementType, {
126
+ ref,
127
+ slot: props.slot || undefined,
128
+ ...mergeProps(DOMProps, renderProps, linkProps, hoverProps, focusProps),
129
+ 'data-focused': isFocused || undefined,
130
+ 'data-hovered': isHovered || undefined,
131
+ 'data-pressed': isPressed || undefined,
132
+ 'data-focus-visible': isFocusVisible || undefined,
133
+ 'data-current': !!props['aria-current'] || undefined,
134
+ 'data-disabled': props.isDisabled || undefined,
135
+ children: renderProps.children,
136
+ });
137
+ }