Skip to content

Commit 280a0ea

Browse files
committed
fixes
1 parent 3650d79 commit 280a0ea

File tree

4 files changed

+98
-99
lines changed

4 files changed

+98
-99
lines changed

packages/react/src/tabs/indicator/TabsIndicator.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ import { generateId } from '../../utils/generateId';
44
import { useForcedRerendering } from '../../utils/useForcedRerendering';
55
import { useRenderElement } from '../../utils/useRenderElement';
66
import { useOnMount } from '../../utils/useOnMount';
7-
import type { BaseUIComponentProps } from '../../utils/types';
8-
import type { TabsOrientation, TabsRoot } from '../root/TabsRoot';
7+
import type { BaseUIComponentProps, Orientation } from '../../utils/types';
8+
import type { TabsRoot } from '../root/TabsRoot';
99
import { useTabsRootContext } from '../root/TabsRootContext';
1010
import { tabsStyleHookMapping } from '../root/styleHooks';
1111
import { useTabsListContext } from '../list/TabsListContext';
1212
import { script as prehydrationScript } from './prehydrationScript.min';
1313
import { TabsIndicatorCssVars } from './TabsIndicatorCssVars';
1414

15-
interface TabPosition {
15+
export interface TabPosition {
1616
left: number;
1717
right: number;
1818
top: number;
1919
bottom: number;
2020
}
2121

22-
interface TabSize {
22+
export interface TabSize {
2323
width: number;
2424
height: number;
2525
}
@@ -205,7 +205,7 @@ export namespace TabsIndicator {
205205
export interface State extends TabsRoot.State {
206206
selectedTabPosition: TabPosition | null;
207207
selectedTabSize: TabSize | null;
208-
orientation: TabsOrientation;
208+
orientation: Orientation;
209209
}
210210

211211
export interface Props extends BaseUIComponentProps<'span', State> {

packages/react/src/tabs/list/TabsList.tsx

+87-87
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,18 @@
11
'use client';
22
import * as React from 'react';
3-
import { BaseUIComponentProps } from '../../utils/types';
3+
import { BaseUIComponentProps, Orientation } from '../../utils/types';
44
import { useEventCallback } from '../../utils/useEventCallback';
55
import { useModernLayoutEffect } from '../../utils/useModernLayoutEffect';
66
import { useRenderElement } from '../../utils/useRenderElement';
77
import { CompositeRoot } from '../../composite/root/CompositeRoot';
88
import { tabsStyleHookMapping } from '../root/styleHooks';
99
import { useTabsRootContext } from '../root/TabsRootContext';
10-
import { type TabsRoot, type TabsOrientation, type TabActivationDirection } from '../root/TabsRoot';
10+
import { type TabsRoot, type TabActivationDirection } from '../root/TabsRoot';
1111
import { type TabMetadata } from '../tab/TabsTab';
1212
import { TabsListContext } from './TabsListContext';
1313

1414
const EMPTY_ARRAY: number[] = [];
1515

16-
function getInset(tab: HTMLElement, tabsList: HTMLElement) {
17-
const { left: tabLeft, top: tabTop } = tab.getBoundingClientRect();
18-
const { left: listLeft, top: listTop } = tabsList.getBoundingClientRect();
19-
20-
const left = tabLeft - listLeft;
21-
const top = tabTop - listTop;
22-
23-
return { left, top };
24-
}
25-
26-
function useActivationDirectionDetector(
27-
// the old value
28-
selectedTabValue: any,
29-
orientation: TabsOrientation,
30-
tabsListRef: React.RefObject<HTMLElement | null>,
31-
getTabElement: (selectedValue: any) => HTMLElement | null,
32-
): (newValue: any) => TabActivationDirection {
33-
const previousTabEdge = React.useRef<number | null>(null);
34-
35-
useModernLayoutEffect(() => {
36-
// Whenever orientation changes, reset the state.
37-
if (selectedTabValue == null || tabsListRef.current == null) {
38-
previousTabEdge.current = null;
39-
return;
40-
}
41-
42-
const activeTab = getTabElement(selectedTabValue);
43-
if (activeTab == null) {
44-
previousTabEdge.current = null;
45-
return;
46-
}
47-
48-
const { left, top } = getInset(activeTab, tabsListRef.current);
49-
previousTabEdge.current = orientation === 'horizontal' ? left : top;
50-
}, [orientation, getTabElement, tabsListRef, selectedTabValue]);
51-
52-
return React.useCallback(
53-
(newValue: any) => {
54-
if (newValue === selectedTabValue) {
55-
return 'none';
56-
}
57-
58-
if (newValue == null) {
59-
previousTabEdge.current = null;
60-
return 'none';
61-
}
62-
63-
if (newValue != null && tabsListRef.current != null) {
64-
const selectedTabElement = getTabElement(newValue);
65-
66-
if (selectedTabElement != null) {
67-
const { left, top } = getInset(selectedTabElement, tabsListRef.current);
68-
69-
if (previousTabEdge.current == null) {
70-
previousTabEdge.current = orientation === 'horizontal' ? left : top;
71-
return 'none';
72-
}
73-
74-
if (orientation === 'horizontal') {
75-
if (left < previousTabEdge.current) {
76-
previousTabEdge.current = left;
77-
return 'left';
78-
}
79-
if (left > previousTabEdge.current) {
80-
previousTabEdge.current = left;
81-
return 'right';
82-
}
83-
} else if (top < previousTabEdge.current) {
84-
previousTabEdge.current = top;
85-
return 'up';
86-
} else if (top > previousTabEdge.current) {
87-
previousTabEdge.current = top;
88-
return 'down';
89-
}
90-
}
91-
}
92-
93-
return 'none';
94-
},
95-
[getTabElement, orientation, previousTabEdge, tabsListRef, selectedTabValue],
96-
);
97-
}
98-
9916
/**
10017
* Groups the individual tab buttons.
10118
* Renders a `<div>` element.
@@ -199,10 +116,93 @@ export const TabsList = React.forwardRef(function TabsList(
199116
);
200117
});
201118

119+
function getInset(tab: HTMLElement, tabsList: HTMLElement) {
120+
const { left: tabLeft, top: tabTop } = tab.getBoundingClientRect();
121+
const { left: listLeft, top: listTop } = tabsList.getBoundingClientRect();
122+
123+
const left = tabLeft - listLeft;
124+
const top = tabTop - listTop;
125+
126+
return { left, top };
127+
}
128+
129+
function useActivationDirectionDetector(
130+
// the old value
131+
selectedTabValue: any,
132+
orientation: Orientation,
133+
tabsListRef: React.RefObject<HTMLElement | null>,
134+
getTabElement: (selectedValue: any) => HTMLElement | null,
135+
): (newValue: any) => TabActivationDirection {
136+
const previousTabEdge = React.useRef<number | null>(null);
137+
138+
useModernLayoutEffect(() => {
139+
// Whenever orientation changes, reset the state.
140+
if (selectedTabValue == null || tabsListRef.current == null) {
141+
previousTabEdge.current = null;
142+
return;
143+
}
144+
145+
const activeTab = getTabElement(selectedTabValue);
146+
if (activeTab == null) {
147+
previousTabEdge.current = null;
148+
return;
149+
}
150+
151+
const { left, top } = getInset(activeTab, tabsListRef.current);
152+
previousTabEdge.current = orientation === 'horizontal' ? left : top;
153+
}, [orientation, getTabElement, tabsListRef, selectedTabValue]);
154+
155+
return React.useCallback(
156+
(newValue: any) => {
157+
if (newValue === selectedTabValue) {
158+
return 'none';
159+
}
160+
161+
if (newValue == null) {
162+
previousTabEdge.current = null;
163+
return 'none';
164+
}
165+
166+
if (newValue != null && tabsListRef.current != null) {
167+
const selectedTabElement = getTabElement(newValue);
168+
169+
if (selectedTabElement != null) {
170+
const { left, top } = getInset(selectedTabElement, tabsListRef.current);
171+
172+
if (previousTabEdge.current == null) {
173+
previousTabEdge.current = orientation === 'horizontal' ? left : top;
174+
return 'none';
175+
}
176+
177+
if (orientation === 'horizontal') {
178+
if (left < previousTabEdge.current) {
179+
previousTabEdge.current = left;
180+
return 'left';
181+
}
182+
if (left > previousTabEdge.current) {
183+
previousTabEdge.current = left;
184+
return 'right';
185+
}
186+
} else if (top < previousTabEdge.current) {
187+
previousTabEdge.current = top;
188+
return 'up';
189+
} else if (top > previousTabEdge.current) {
190+
previousTabEdge.current = top;
191+
return 'down';
192+
}
193+
}
194+
}
195+
196+
return 'none';
197+
},
198+
[getTabElement, orientation, previousTabEdge, tabsListRef, selectedTabValue],
199+
);
200+
}
201+
202202
export namespace TabsList {
203-
export type State = TabsRoot.State;
203+
export interface State extends TabsRoot.State {}
204204

205-
export interface Props extends BaseUIComponentProps<'div', TabsRoot.State> {
205+
export interface Props extends BaseUIComponentProps<'div', State> {
206206
/**
207207
* Whether to automatically change the active tab on arrow key focus.
208208
* Otherwise, tabs will be activated using Enter or Spacebar key press.

packages/react/src/tabs/root/TabsRoot.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22
import * as React from 'react';
3-
import type { BaseUIComponentProps } from '../../utils/types';
3+
import type { BaseUIComponentProps, Orientation } from '../../utils/types';
44
import { useControlled } from '../../utils/useControlled';
55
import { useEventCallback } from '../../utils/useEventCallback';
66
import { useRenderElement } from '../../utils/useRenderElement';
@@ -185,13 +185,12 @@ export const TabsRoot = React.forwardRef(function TabsRoot(
185185
);
186186
});
187187

188-
export type TabsOrientation = 'horizontal' | 'vertical';
189188
export type TabActivationDirection = 'left' | 'right' | 'up' | 'down' | 'none';
190189
export type TabValue = any | null;
191190

192191
export namespace TabsRoot {
193192
export type State = {
194-
orientation: TabsOrientation;
193+
orientation: Orientation;
195194
tabActivationDirection: TabActivationDirection;
196195
};
197196

@@ -211,7 +210,7 @@ export namespace TabsRoot {
211210
* The component orientation (layout flow direction).
212211
* @default 'horizontal'
213212
*/
214-
orientation?: TabsOrientation;
213+
orientation?: Orientation;
215214
/**
216215
* Callback invoked when new value is being set.
217216
*/

packages/react/src/tabs/tab/TabsTab.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { ownerDocument } from '../../utils/owner';
44
import { useBaseUiId } from '../../utils/useBaseUiId';
55
import { useModernLayoutEffect } from '../../utils/useModernLayoutEffect';
66
import { useRenderElement } from '../../utils/useRenderElement';
7-
import type { BaseUIComponentProps } from '../../utils/types';
7+
import type { BaseUIComponentProps, Orientation } from '../../utils/types';
88
import { useButton } from '../../use-button';
99
import { useCompositeItem } from '../../composite/item/useCompositeItem';
10-
import type { TabsOrientation, TabValue } from '../root/TabsRoot';
10+
import type { TabValue } from '../root/TabsRoot';
1111
import { useTabsRootContext } from '../root/TabsRootContext';
1212
import { useTabsListContext } from '../list/TabsListContext';
1313

@@ -179,7 +179,7 @@ export namespace TabsTab {
179179
*/
180180
disabled: boolean;
181181
selected: boolean;
182-
orientation: TabsOrientation;
182+
orientation: Orientation;
183183
}
184184

185185
export interface Props extends BaseUIComponentProps<'button', State> {

0 commit comments

Comments
 (0)