@arun-dev/headless 4.5.1 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2S4XG4VD.js +33 -0
- package/dist/menu/index.cjs +438 -0
- package/dist/menu/index.d.cts +115 -0
- package/dist/menu/index.d.ts +115 -0
- package/dist/menu/index.js +255 -0
- package/dist/tabs/index.cjs +408 -0
- package/dist/tabs/index.d.cts +127 -0
- package/dist/tabs/index.d.ts +127 -0
- package/dist/tabs/index.js +242 -0
- package/package.json +21 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Roving focus for composite widgets — one Tab stop for the group, arrow keys within it.
|
|
6
|
+
* Tabs first; Menu and Toolbar reuse it. Internal: shared between components, not exported
|
|
7
|
+
* (decision 9).
|
|
8
|
+
*
|
|
9
|
+
* The caller owns the list of items and which are enabled — from component props, never
|
|
10
|
+
* read back from the DOM (decision 10). This only answers "where does this key go".
|
|
11
|
+
*/
|
|
12
|
+
type Orientation = 'horizontal' | 'vertical';
|
|
13
|
+
|
|
14
|
+
type TabsActivationMode = 'automatic' | 'manual';
|
|
15
|
+
/** The state Tabs.Root shares with its parts, and projects as `data-*` attributes. */
|
|
16
|
+
type TabsState = {
|
|
17
|
+
value: string | undefined;
|
|
18
|
+
orientation: Orientation;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type TabsRootOwnProps = {
|
|
22
|
+
/**
|
|
23
|
+
* Controlled selection: the `value` of the selected Tab. Provide `onValueChange` alongside.
|
|
24
|
+
*
|
|
25
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
26
|
+
*/
|
|
27
|
+
value?: string;
|
|
28
|
+
/** Initial selection when uncontrolled. Read once, at mount. Without one, none is selected. */
|
|
29
|
+
defaultValue?: string;
|
|
30
|
+
/** Called with the Tab's `value` whenever the selection changes. */
|
|
31
|
+
onValueChange?: (value: string) => void;
|
|
32
|
+
/** The axis the tabs run along, and so which arrow keys move between them. */
|
|
33
|
+
orientation?: Orientation;
|
|
34
|
+
/**
|
|
35
|
+
* `automatic` selects a tab as soon as it has focus; `manual` waits for Enter, Space or a
|
|
36
|
+
* click. Manual suits panels that are slow to show.
|
|
37
|
+
*/
|
|
38
|
+
activationMode?: TabsActivationMode;
|
|
39
|
+
/**
|
|
40
|
+
* Keeps disabled tabs in the arrow-key sequence, so a screen reader announces them as
|
|
41
|
+
* unavailable. They still cannot be selected. Only with `activationMode="manual"`: under
|
|
42
|
+
* `"automatic"` moving focus to a tab selects it, so disabled tabs are always skipped and
|
|
43
|
+
* this has no effect.
|
|
44
|
+
*/
|
|
45
|
+
focusableWhenDisabled?: boolean;
|
|
46
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
47
|
+
render?: ReactElement;
|
|
48
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
49
|
+
ref?: Ref<HTMLElement>;
|
|
50
|
+
};
|
|
51
|
+
type TabsRootProps = TabsRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TabsRootOwnProps | 'defaultValue' | 'onChange'>;
|
|
52
|
+
/**
|
|
53
|
+
* Tabs, by the WAI-ARIA pattern: a tablist of tabs, one panel each, one selected.
|
|
54
|
+
*
|
|
55
|
+
* There is no native element to lean on (decision 7), so the behaviour is built here: the
|
|
56
|
+
* selection, arrow-key focus with a single Tab stop (roving tabindex, `core/rovingFocus`),
|
|
57
|
+
* and the ids that wire each tab to its panel.
|
|
58
|
+
*
|
|
59
|
+
* Each Tab registers its own `value` and `disabled` from its props; the Root never reads
|
|
60
|
+
* them back from the DOM (decision 10). Elements are kept only to move focus to.
|
|
61
|
+
*/
|
|
62
|
+
declare function TabsRoot({ value: valueProp, defaultValue, onValueChange, orientation, activationMode, focusableWhenDisabled: focusableWhenDisabledProp, className, children, render, ...rest }: TabsRootProps): react.JSX.Element;
|
|
63
|
+
|
|
64
|
+
type TabsListOwnProps = {
|
|
65
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
66
|
+
render?: ReactElement;
|
|
67
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
68
|
+
ref?: Ref<HTMLElement>;
|
|
69
|
+
};
|
|
70
|
+
type TabsListProps = TabsListOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TabsListOwnProps>;
|
|
71
|
+
/**
|
|
72
|
+
* The tablist. Holds the keyboard: arrows along the orientation, wrapping at the ends,
|
|
73
|
+
* Home and End — moving focus among the enabled tabs, and the disabled ones too with
|
|
74
|
+
* `focusableWhenDisabled`. Whether focusing a tab also
|
|
75
|
+
* selects it is the Tab's to decide, by `activationMode`.
|
|
76
|
+
*
|
|
77
|
+
* Name it with `aria-label` or `aria-labelledby` when the page has more than one.
|
|
78
|
+
*/
|
|
79
|
+
declare function TabsList({ className, children, render, ...rest }: TabsListProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
80
|
+
|
|
81
|
+
type TabsTabOwnProps = {
|
|
82
|
+
/** Identifies this tab, and the Panel with the same `value`. */
|
|
83
|
+
value: string;
|
|
84
|
+
/**
|
|
85
|
+
* Cannot be selected. Skipped by the arrow keys, unless the Root sets
|
|
86
|
+
* `focusableWhenDisabled` with `activationMode="manual"`.
|
|
87
|
+
*/
|
|
88
|
+
disabled?: boolean;
|
|
89
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
90
|
+
render?: ReactElement;
|
|
91
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
92
|
+
ref?: Ref<HTMLElement>;
|
|
93
|
+
};
|
|
94
|
+
type TabsTabProps = TabsTabOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof TabsTabOwnProps | 'value'>;
|
|
95
|
+
/**
|
|
96
|
+
* One tab. A native `<button>`, so Enter, Space and a click select it with no key handling
|
|
97
|
+
* here. Only the tab holding the group's Tab stop has `tabIndex={0}` — roving tabindex — so
|
|
98
|
+
* Tab enters the list at the selected tab and leaves it after one press.
|
|
99
|
+
*
|
|
100
|
+
* With `activationMode="automatic"` (the default) focus selects: the arrow keys change the
|
|
101
|
+
* panel as they move. With `"manual"` they only move focus.
|
|
102
|
+
*/
|
|
103
|
+
declare function TabsTab({ value, disabled, onClick, className, children, render, ...rest }: TabsTabProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
104
|
+
|
|
105
|
+
type TabsPanelOwnProps = {
|
|
106
|
+
/** The `value` of the Tab this panel belongs to. */
|
|
107
|
+
value: string;
|
|
108
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
109
|
+
render?: ReactElement;
|
|
110
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
111
|
+
ref?: Ref<HTMLElement>;
|
|
112
|
+
};
|
|
113
|
+
type TabsPanelProps = TabsPanelOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TabsPanelOwnProps>;
|
|
114
|
+
/**
|
|
115
|
+
* The content for one tab, labelled by it. Hidden with the `hidden` attribute while its tab
|
|
116
|
+
* is not selected, and stays mounted, so what is inside keeps its state between visits.
|
|
117
|
+
*
|
|
118
|
+
* `tabIndex={0}`, as the pattern recommends, so a panel with nothing focusable inside is
|
|
119
|
+
* still reached by Tab after the tablist.
|
|
120
|
+
*/
|
|
121
|
+
declare function TabsPanel({ value, className, children, render, ...rest }: TabsPanelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
122
|
+
|
|
123
|
+
declare namespace index_parts {
|
|
124
|
+
export { TabsList as List, TabsPanel as Panel, TabsRoot as Root, TabsTab as Tab };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { index_parts as Tabs, type TabsActivationMode, type TabsListProps, type TabsPanelProps, type TabsRootProps, type TabsState, type TabsTabProps };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Roving focus for composite widgets — one Tab stop for the group, arrow keys within it.
|
|
6
|
+
* Tabs first; Menu and Toolbar reuse it. Internal: shared between components, not exported
|
|
7
|
+
* (decision 9).
|
|
8
|
+
*
|
|
9
|
+
* The caller owns the list of items and which are enabled — from component props, never
|
|
10
|
+
* read back from the DOM (decision 10). This only answers "where does this key go".
|
|
11
|
+
*/
|
|
12
|
+
type Orientation = 'horizontal' | 'vertical';
|
|
13
|
+
|
|
14
|
+
type TabsActivationMode = 'automatic' | 'manual';
|
|
15
|
+
/** The state Tabs.Root shares with its parts, and projects as `data-*` attributes. */
|
|
16
|
+
type TabsState = {
|
|
17
|
+
value: string | undefined;
|
|
18
|
+
orientation: Orientation;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type TabsRootOwnProps = {
|
|
22
|
+
/**
|
|
23
|
+
* Controlled selection: the `value` of the selected Tab. Provide `onValueChange` alongside.
|
|
24
|
+
*
|
|
25
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
26
|
+
*/
|
|
27
|
+
value?: string;
|
|
28
|
+
/** Initial selection when uncontrolled. Read once, at mount. Without one, none is selected. */
|
|
29
|
+
defaultValue?: string;
|
|
30
|
+
/** Called with the Tab's `value` whenever the selection changes. */
|
|
31
|
+
onValueChange?: (value: string) => void;
|
|
32
|
+
/** The axis the tabs run along, and so which arrow keys move between them. */
|
|
33
|
+
orientation?: Orientation;
|
|
34
|
+
/**
|
|
35
|
+
* `automatic` selects a tab as soon as it has focus; `manual` waits for Enter, Space or a
|
|
36
|
+
* click. Manual suits panels that are slow to show.
|
|
37
|
+
*/
|
|
38
|
+
activationMode?: TabsActivationMode;
|
|
39
|
+
/**
|
|
40
|
+
* Keeps disabled tabs in the arrow-key sequence, so a screen reader announces them as
|
|
41
|
+
* unavailable. They still cannot be selected. Only with `activationMode="manual"`: under
|
|
42
|
+
* `"automatic"` moving focus to a tab selects it, so disabled tabs are always skipped and
|
|
43
|
+
* this has no effect.
|
|
44
|
+
*/
|
|
45
|
+
focusableWhenDisabled?: boolean;
|
|
46
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
47
|
+
render?: ReactElement;
|
|
48
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
49
|
+
ref?: Ref<HTMLElement>;
|
|
50
|
+
};
|
|
51
|
+
type TabsRootProps = TabsRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TabsRootOwnProps | 'defaultValue' | 'onChange'>;
|
|
52
|
+
/**
|
|
53
|
+
* Tabs, by the WAI-ARIA pattern: a tablist of tabs, one panel each, one selected.
|
|
54
|
+
*
|
|
55
|
+
* There is no native element to lean on (decision 7), so the behaviour is built here: the
|
|
56
|
+
* selection, arrow-key focus with a single Tab stop (roving tabindex, `core/rovingFocus`),
|
|
57
|
+
* and the ids that wire each tab to its panel.
|
|
58
|
+
*
|
|
59
|
+
* Each Tab registers its own `value` and `disabled` from its props; the Root never reads
|
|
60
|
+
* them back from the DOM (decision 10). Elements are kept only to move focus to.
|
|
61
|
+
*/
|
|
62
|
+
declare function TabsRoot({ value: valueProp, defaultValue, onValueChange, orientation, activationMode, focusableWhenDisabled: focusableWhenDisabledProp, className, children, render, ...rest }: TabsRootProps): react.JSX.Element;
|
|
63
|
+
|
|
64
|
+
type TabsListOwnProps = {
|
|
65
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
66
|
+
render?: ReactElement;
|
|
67
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
68
|
+
ref?: Ref<HTMLElement>;
|
|
69
|
+
};
|
|
70
|
+
type TabsListProps = TabsListOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TabsListOwnProps>;
|
|
71
|
+
/**
|
|
72
|
+
* The tablist. Holds the keyboard: arrows along the orientation, wrapping at the ends,
|
|
73
|
+
* Home and End — moving focus among the enabled tabs, and the disabled ones too with
|
|
74
|
+
* `focusableWhenDisabled`. Whether focusing a tab also
|
|
75
|
+
* selects it is the Tab's to decide, by `activationMode`.
|
|
76
|
+
*
|
|
77
|
+
* Name it with `aria-label` or `aria-labelledby` when the page has more than one.
|
|
78
|
+
*/
|
|
79
|
+
declare function TabsList({ className, children, render, ...rest }: TabsListProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
80
|
+
|
|
81
|
+
type TabsTabOwnProps = {
|
|
82
|
+
/** Identifies this tab, and the Panel with the same `value`. */
|
|
83
|
+
value: string;
|
|
84
|
+
/**
|
|
85
|
+
* Cannot be selected. Skipped by the arrow keys, unless the Root sets
|
|
86
|
+
* `focusableWhenDisabled` with `activationMode="manual"`.
|
|
87
|
+
*/
|
|
88
|
+
disabled?: boolean;
|
|
89
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
90
|
+
render?: ReactElement;
|
|
91
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
92
|
+
ref?: Ref<HTMLElement>;
|
|
93
|
+
};
|
|
94
|
+
type TabsTabProps = TabsTabOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof TabsTabOwnProps | 'value'>;
|
|
95
|
+
/**
|
|
96
|
+
* One tab. A native `<button>`, so Enter, Space and a click select it with no key handling
|
|
97
|
+
* here. Only the tab holding the group's Tab stop has `tabIndex={0}` — roving tabindex — so
|
|
98
|
+
* Tab enters the list at the selected tab and leaves it after one press.
|
|
99
|
+
*
|
|
100
|
+
* With `activationMode="automatic"` (the default) focus selects: the arrow keys change the
|
|
101
|
+
* panel as they move. With `"manual"` they only move focus.
|
|
102
|
+
*/
|
|
103
|
+
declare function TabsTab({ value, disabled, onClick, className, children, render, ...rest }: TabsTabProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
104
|
+
|
|
105
|
+
type TabsPanelOwnProps = {
|
|
106
|
+
/** The `value` of the Tab this panel belongs to. */
|
|
107
|
+
value: string;
|
|
108
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
109
|
+
render?: ReactElement;
|
|
110
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
111
|
+
ref?: Ref<HTMLElement>;
|
|
112
|
+
};
|
|
113
|
+
type TabsPanelProps = TabsPanelOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof TabsPanelOwnProps>;
|
|
114
|
+
/**
|
|
115
|
+
* The content for one tab, labelled by it. Hidden with the `hidden` attribute while its tab
|
|
116
|
+
* is not selected, and stays mounted, so what is inside keeps its state between visits.
|
|
117
|
+
*
|
|
118
|
+
* `tabIndex={0}`, as the pattern recommends, so a panel with nothing focusable inside is
|
|
119
|
+
* still reached by Tab after the tablist.
|
|
120
|
+
*/
|
|
121
|
+
declare function TabsPanel({ value, className, children, render, ...rest }: TabsPanelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
122
|
+
|
|
123
|
+
declare namespace index_parts {
|
|
124
|
+
export { TabsList as List, TabsPanel as Panel, TabsRoot as Root, TabsTab as Tab };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { index_parts as Tabs, type TabsActivationMode, type TabsListProps, type TabsPanelProps, type TabsRootProps, type TabsState, type TabsTabProps };
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {
|
|
2
|
+
byDocumentOrder,
|
|
3
|
+
rovingIndex
|
|
4
|
+
} from "../chunk-2S4XG4VD.js";
|
|
5
|
+
import {
|
|
6
|
+
useControlled
|
|
7
|
+
} from "../chunk-UEIB3I3T.js";
|
|
8
|
+
import {
|
|
9
|
+
__export,
|
|
10
|
+
useRender
|
|
11
|
+
} from "../chunk-OQTWWZ4B.js";
|
|
12
|
+
|
|
13
|
+
// src/tabs/index.parts.ts
|
|
14
|
+
var index_parts_exports = {};
|
|
15
|
+
__export(index_parts_exports, {
|
|
16
|
+
List: () => TabsList,
|
|
17
|
+
Panel: () => TabsPanel,
|
|
18
|
+
Root: () => TabsRoot,
|
|
19
|
+
Tab: () => TabsTab
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// src/tabs/TabsRoot.tsx
|
|
23
|
+
import { useCallback, useId, useMemo, useState } from "react";
|
|
24
|
+
|
|
25
|
+
// src/tabs/TabsRootContext.ts
|
|
26
|
+
import { createContext, useContext } from "react";
|
|
27
|
+
var TabsRootContext = createContext(null);
|
|
28
|
+
function useTabsRootContext(part) {
|
|
29
|
+
const context = useContext(TabsRootContext);
|
|
30
|
+
if (context === null) {
|
|
31
|
+
throw new Error(`<Tabs.${part}> must be rendered inside <Tabs.Root>.`);
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/tabs/tabsDataAttributes.ts
|
|
37
|
+
function orientationDataAttributes(orientation) {
|
|
38
|
+
return { "data-orientation": orientation };
|
|
39
|
+
}
|
|
40
|
+
function selectedDataAttributes(selected, disabled = false) {
|
|
41
|
+
return {
|
|
42
|
+
"data-selected": selected ? "" : void 0,
|
|
43
|
+
"data-unselected": selected ? void 0 : "",
|
|
44
|
+
"data-disabled": disabled ? "" : void 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/tabs/TabsRoot.tsx
|
|
49
|
+
import { jsx } from "react/jsx-runtime";
|
|
50
|
+
function idPart(value) {
|
|
51
|
+
return value.replace(/\s+/g, "_");
|
|
52
|
+
}
|
|
53
|
+
function TabsRoot({
|
|
54
|
+
value: valueProp,
|
|
55
|
+
defaultValue,
|
|
56
|
+
onValueChange,
|
|
57
|
+
orientation = "horizontal",
|
|
58
|
+
activationMode = "automatic",
|
|
59
|
+
focusableWhenDisabled: focusableWhenDisabledProp = false,
|
|
60
|
+
className,
|
|
61
|
+
children,
|
|
62
|
+
render,
|
|
63
|
+
...rest
|
|
64
|
+
}) {
|
|
65
|
+
const [value, setValueState] = useControlled({
|
|
66
|
+
controlled: valueProp,
|
|
67
|
+
default: defaultValue,
|
|
68
|
+
name: "Tabs.Root",
|
|
69
|
+
state: "value"
|
|
70
|
+
});
|
|
71
|
+
const setValue = useCallback(
|
|
72
|
+
(next) => {
|
|
73
|
+
if (next === value) return;
|
|
74
|
+
setValueState(next);
|
|
75
|
+
onValueChange?.(next);
|
|
76
|
+
},
|
|
77
|
+
[value, setValueState, onValueChange]
|
|
78
|
+
);
|
|
79
|
+
const [tabs, setTabs] = useState([]);
|
|
80
|
+
const register = useCallback((entry) => {
|
|
81
|
+
setTabs(
|
|
82
|
+
(current) => [...current.filter((t) => t.value !== entry.value), entry].sort(byDocumentOrder)
|
|
83
|
+
);
|
|
84
|
+
return () => setTabs((current) => current.filter((t) => t !== entry));
|
|
85
|
+
}, []);
|
|
86
|
+
const focusableWhenDisabled = focusableWhenDisabledProp && activationMode === "manual";
|
|
87
|
+
const navigableTabs = useMemo(
|
|
88
|
+
() => focusableWhenDisabled ? tabs : tabs.filter((t) => !t.disabled),
|
|
89
|
+
[tabs, focusableWhenDisabled]
|
|
90
|
+
);
|
|
91
|
+
const tabStopValue = tabs.length === 0 ? value : (navigableTabs.find((t) => t.value === value) ?? navigableTabs[0])?.value;
|
|
92
|
+
const baseId = useId();
|
|
93
|
+
const context = useMemo(
|
|
94
|
+
() => ({
|
|
95
|
+
value,
|
|
96
|
+
setValue,
|
|
97
|
+
orientation,
|
|
98
|
+
activationMode,
|
|
99
|
+
navigableTabs,
|
|
100
|
+
focusableWhenDisabled,
|
|
101
|
+
tabStopValue,
|
|
102
|
+
register,
|
|
103
|
+
tabId: (v) => `${baseId}-tab-${idPart(v)}`,
|
|
104
|
+
panelId: (v) => `${baseId}-panel-${idPart(v)}`
|
|
105
|
+
}),
|
|
106
|
+
[
|
|
107
|
+
value,
|
|
108
|
+
setValue,
|
|
109
|
+
orientation,
|
|
110
|
+
activationMode,
|
|
111
|
+
navigableTabs,
|
|
112
|
+
focusableWhenDisabled,
|
|
113
|
+
tabStopValue,
|
|
114
|
+
register,
|
|
115
|
+
baseId
|
|
116
|
+
]
|
|
117
|
+
);
|
|
118
|
+
const element = useRender({
|
|
119
|
+
render,
|
|
120
|
+
defaultTagName: "div",
|
|
121
|
+
props: { ...orientationDataAttributes(orientation), className, children },
|
|
122
|
+
consumerProps: rest
|
|
123
|
+
});
|
|
124
|
+
return /* @__PURE__ */ jsx(TabsRootContext.Provider, { value: context, children: element });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/tabs/TabsList.tsx
|
|
128
|
+
import { useRef } from "react";
|
|
129
|
+
function TabsList({ className, children, render, ...rest }) {
|
|
130
|
+
const { orientation, navigableTabs } = useTabsRootContext("List");
|
|
131
|
+
const elementRef = useRef(null);
|
|
132
|
+
return useRender({
|
|
133
|
+
render,
|
|
134
|
+
defaultTagName: "div",
|
|
135
|
+
props: {
|
|
136
|
+
role: "tablist",
|
|
137
|
+
// Horizontal is the ARIA default for a tablist, so it is only stated when vertical.
|
|
138
|
+
"aria-orientation": orientation === "vertical" ? "vertical" : void 0,
|
|
139
|
+
...orientationDataAttributes(orientation),
|
|
140
|
+
className,
|
|
141
|
+
children,
|
|
142
|
+
ref: elementRef,
|
|
143
|
+
onKeyDown(event) {
|
|
144
|
+
const current = navigableTabs.findIndex((t) => t.ref.current === event.target);
|
|
145
|
+
if (current < 0) return;
|
|
146
|
+
const list = elementRef.current;
|
|
147
|
+
const rtl = list ? getComputedStyle(list).direction === "rtl" : false;
|
|
148
|
+
const next = rovingIndex(event.key, {
|
|
149
|
+
count: navigableTabs.length,
|
|
150
|
+
current,
|
|
151
|
+
orientation,
|
|
152
|
+
rtl
|
|
153
|
+
});
|
|
154
|
+
if (next === null) return;
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
navigableTabs[next]?.ref.current?.focus();
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
consumerProps: rest
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/tabs/TabsTab.tsx
|
|
164
|
+
import { useLayoutEffect, useRef as useRef2 } from "react";
|
|
165
|
+
function TabsTab({
|
|
166
|
+
value,
|
|
167
|
+
disabled = false,
|
|
168
|
+
onClick,
|
|
169
|
+
className,
|
|
170
|
+
children,
|
|
171
|
+
render,
|
|
172
|
+
...rest
|
|
173
|
+
}) {
|
|
174
|
+
const {
|
|
175
|
+
value: selectedValue,
|
|
176
|
+
setValue,
|
|
177
|
+
activationMode,
|
|
178
|
+
focusableWhenDisabled,
|
|
179
|
+
tabStopValue,
|
|
180
|
+
register,
|
|
181
|
+
tabId,
|
|
182
|
+
panelId
|
|
183
|
+
} = useTabsRootContext("Tab");
|
|
184
|
+
const elementRef = useRef2(null);
|
|
185
|
+
const selected = value === selectedValue;
|
|
186
|
+
useLayoutEffect(
|
|
187
|
+
() => register({ value, disabled, ref: elementRef }),
|
|
188
|
+
[register, value, disabled]
|
|
189
|
+
);
|
|
190
|
+
return useRender({
|
|
191
|
+
render,
|
|
192
|
+
defaultTagName: "button",
|
|
193
|
+
props: {
|
|
194
|
+
type: "button",
|
|
195
|
+
role: "tab",
|
|
196
|
+
id: tabId(value),
|
|
197
|
+
"aria-selected": selected,
|
|
198
|
+
"aria-controls": panelId(value),
|
|
199
|
+
tabIndex: value === tabStopValue ? 0 : -1,
|
|
200
|
+
// The platform keeps a disabled button out of focus and activation. Focusable when
|
|
201
|
+
// disabled, it is aria-disabled instead, and activation is blocked here.
|
|
202
|
+
disabled: disabled && !focusableWhenDisabled || void 0,
|
|
203
|
+
"aria-disabled": disabled && focusableWhenDisabled || void 0,
|
|
204
|
+
...selectedDataAttributes(selected, disabled),
|
|
205
|
+
className,
|
|
206
|
+
children,
|
|
207
|
+
ref: elementRef,
|
|
208
|
+
onClick() {
|
|
209
|
+
if (!disabled) setValue(value);
|
|
210
|
+
},
|
|
211
|
+
onFocus() {
|
|
212
|
+
if (activationMode === "automatic" && !disabled) setValue(value);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
// A native `disabled` would block your onClick; aria-disabled does not, so it is left off.
|
|
216
|
+
consumerProps: disabled ? rest : { ...rest, onClick }
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/tabs/TabsPanel.tsx
|
|
221
|
+
function TabsPanel({ value, className, children, render, ...rest }) {
|
|
222
|
+
const { value: selectedValue, tabId, panelId } = useTabsRootContext("Panel");
|
|
223
|
+
const selected = value === selectedValue;
|
|
224
|
+
return useRender({
|
|
225
|
+
render,
|
|
226
|
+
defaultTagName: "div",
|
|
227
|
+
props: {
|
|
228
|
+
role: "tabpanel",
|
|
229
|
+
id: panelId(value),
|
|
230
|
+
"aria-labelledby": tabId(value),
|
|
231
|
+
tabIndex: 0,
|
|
232
|
+
hidden: !selected || void 0,
|
|
233
|
+
...selectedDataAttributes(selected),
|
|
234
|
+
className,
|
|
235
|
+
children
|
|
236
|
+
},
|
|
237
|
+
consumerProps: rest
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
export {
|
|
241
|
+
index_parts_exports as Tabs
|
|
242
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arun-dev/headless",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Unstyled React behaviour primitives — render engine, controlled/uncontrolled state, and data-* state projection. Ships no CSS and no class names.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -64,6 +64,16 @@
|
|
|
64
64
|
"default": "./dist/dialog/index.cjs"
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
|
+
"./menu": {
|
|
68
|
+
"import": {
|
|
69
|
+
"types": "./dist/menu/index.d.ts",
|
|
70
|
+
"default": "./dist/menu/index.js"
|
|
71
|
+
},
|
|
72
|
+
"require": {
|
|
73
|
+
"types": "./dist/menu/index.d.cts",
|
|
74
|
+
"default": "./dist/menu/index.cjs"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
67
77
|
"./popover": {
|
|
68
78
|
"import": {
|
|
69
79
|
"types": "./dist/popover/index.d.ts",
|
|
@@ -94,6 +104,16 @@
|
|
|
94
104
|
"default": "./dist/switch/index.cjs"
|
|
95
105
|
}
|
|
96
106
|
},
|
|
107
|
+
"./tabs": {
|
|
108
|
+
"import": {
|
|
109
|
+
"types": "./dist/tabs/index.d.ts",
|
|
110
|
+
"default": "./dist/tabs/index.js"
|
|
111
|
+
},
|
|
112
|
+
"require": {
|
|
113
|
+
"types": "./dist/tabs/index.d.cts",
|
|
114
|
+
"default": "./dist/tabs/index.cjs"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
97
117
|
"./tooltip": {
|
|
98
118
|
"import": {
|
|
99
119
|
"types": "./dist/tooltip/index.d.ts",
|