@tamagui/tabs 1.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/LICENSE +21 -0
- package/dist/cjs/Tabs.js +340 -0
- package/dist/cjs/Tabs.js.map +7 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/esm/Tabs.js +314 -0
- package/dist/esm/Tabs.js.map +7 -0
- package/dist/esm/Tabs.mjs +314 -0
- package/dist/esm/Tabs.mjs.map +7 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/index.mjs.map +7 -0
- package/dist/jsx/Tabs.js +287 -0
- package/dist/jsx/Tabs.js.map +7 -0
- package/dist/jsx/Tabs.mjs +287 -0
- package/dist/jsx/Tabs.mjs.map +7 -0
- package/dist/jsx/index.js +2 -0
- package/dist/jsx/index.js.map +7 -0
- package/dist/jsx/index.mjs +2 -0
- package/dist/jsx/index.mjs.map +7 -0
- package/package.json +42 -0
- package/src/Tabs.tsx +443 -0
- package/src/index.ts +1 -0
- package/types/Tabs.d.ts +619 -0
- package/types/Tabs.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -0
package/dist/esm/Tabs.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContextScope } from "@tamagui/create-context";
|
|
3
|
+
import { getButtonSized } from "@tamagui/get-button-sized";
|
|
4
|
+
import { Group, useGroupItem } from "@tamagui/group";
|
|
5
|
+
import { RovingFocusGroup, createRovingFocusGroupScope } from "@tamagui/roving-focus";
|
|
6
|
+
import { SizableStack, ThemeableStack } from "@tamagui/stacks";
|
|
7
|
+
import { useControllableState } from "@tamagui/use-controllable-state";
|
|
8
|
+
import { useDirection } from "@tamagui/use-direction";
|
|
9
|
+
import {
|
|
10
|
+
Theme,
|
|
11
|
+
composeEventHandlers,
|
|
12
|
+
composeRefs,
|
|
13
|
+
isWeb,
|
|
14
|
+
styled,
|
|
15
|
+
useId,
|
|
16
|
+
withStaticProperties
|
|
17
|
+
} from "@tamagui/web";
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
const TAB_LIST_NAME = "TabsList";
|
|
20
|
+
const TabsListFrame = styled(Group, {
|
|
21
|
+
name: TAB_LIST_NAME,
|
|
22
|
+
focusable: true
|
|
23
|
+
// defaultVariants: {
|
|
24
|
+
// flexGrow: 0,
|
|
25
|
+
// },
|
|
26
|
+
});
|
|
27
|
+
const TabsList = React.forwardRef(
|
|
28
|
+
(props, forwardedRef) => {
|
|
29
|
+
const { __scopeTabs, loop = true, children, ...listProps } = props;
|
|
30
|
+
const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);
|
|
31
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
32
|
+
return /* @__PURE__ */ jsx(
|
|
33
|
+
RovingFocusGroup,
|
|
34
|
+
{
|
|
35
|
+
asChild: true,
|
|
36
|
+
orientation: context.orientation,
|
|
37
|
+
dir: context.dir,
|
|
38
|
+
loop,
|
|
39
|
+
...rovingFocusGroupScope,
|
|
40
|
+
children: /* @__PURE__ */ jsx(
|
|
41
|
+
TabsListFrame,
|
|
42
|
+
{
|
|
43
|
+
role: "tablist",
|
|
44
|
+
"aria-orientation": context.orientation,
|
|
45
|
+
ref: forwardedRef,
|
|
46
|
+
axis: context.orientation,
|
|
47
|
+
...listProps,
|
|
48
|
+
children
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
TabsList.displayName = TAB_LIST_NAME;
|
|
56
|
+
const TRIGGER_NAME = "TabsTrigger";
|
|
57
|
+
const TabsTriggerFrame = styled(ThemeableStack, {
|
|
58
|
+
name: TRIGGER_NAME,
|
|
59
|
+
justifyContent: "center",
|
|
60
|
+
alignItems: "center",
|
|
61
|
+
flexWrap: "nowrap",
|
|
62
|
+
flexDirection: "row",
|
|
63
|
+
cursor: "pointer",
|
|
64
|
+
variants: {
|
|
65
|
+
size: {
|
|
66
|
+
"...size": getButtonSized
|
|
67
|
+
},
|
|
68
|
+
disabled: {
|
|
69
|
+
true: {
|
|
70
|
+
pointerEvents: "none"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
theme: {
|
|
74
|
+
Button: {
|
|
75
|
+
focusable: true,
|
|
76
|
+
hoverTheme: true,
|
|
77
|
+
pressTheme: true,
|
|
78
|
+
backgrounded: true,
|
|
79
|
+
borderWidth: 1,
|
|
80
|
+
borderColor: "transparent",
|
|
81
|
+
pressStyle: {
|
|
82
|
+
borderColor: "transparent"
|
|
83
|
+
},
|
|
84
|
+
hoverStyle: {
|
|
85
|
+
borderColor: "transparent"
|
|
86
|
+
},
|
|
87
|
+
focusStyle: {
|
|
88
|
+
borderColor: "$borderColorFocus"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const TabsTrigger = TabsTriggerFrame.extractable(
|
|
95
|
+
React.forwardRef(
|
|
96
|
+
(props, forwardedRef) => {
|
|
97
|
+
const {
|
|
98
|
+
__scopeTabs,
|
|
99
|
+
value,
|
|
100
|
+
disabled = false,
|
|
101
|
+
onInteraction,
|
|
102
|
+
...triggerProps
|
|
103
|
+
} = props;
|
|
104
|
+
const context = useTabsContext(TRIGGER_NAME, __scopeTabs);
|
|
105
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
106
|
+
const triggerId = makeTriggerId(context.baseId, value);
|
|
107
|
+
const contentId = makeContentId(context.baseId, value);
|
|
108
|
+
const isSelected = value === context.value;
|
|
109
|
+
const [layout, setLayout] = React.useState(null);
|
|
110
|
+
const triggerRef = React.useRef(null);
|
|
111
|
+
const groupItemProps = useGroupItem({ disabled });
|
|
112
|
+
React.useEffect(() => {
|
|
113
|
+
if (!triggerRef.current || !isWeb)
|
|
114
|
+
return;
|
|
115
|
+
function getTriggerSize() {
|
|
116
|
+
if (!triggerRef.current)
|
|
117
|
+
return;
|
|
118
|
+
setLayout({
|
|
119
|
+
width: triggerRef.current.offsetWidth,
|
|
120
|
+
height: triggerRef.current.offsetHeight,
|
|
121
|
+
x: triggerRef.current.offsetLeft,
|
|
122
|
+
y: triggerRef.current.offsetTop
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
getTriggerSize();
|
|
126
|
+
const observer = new ResizeObserver(getTriggerSize);
|
|
127
|
+
observer.observe(triggerRef.current);
|
|
128
|
+
return () => {
|
|
129
|
+
if (!triggerRef.current)
|
|
130
|
+
return;
|
|
131
|
+
observer.unobserve(triggerRef.current);
|
|
132
|
+
};
|
|
133
|
+
}, []);
|
|
134
|
+
React.useEffect(() => {
|
|
135
|
+
if (isSelected && layout) {
|
|
136
|
+
onInteraction == null ? void 0 : onInteraction("select", layout);
|
|
137
|
+
}
|
|
138
|
+
}, [isSelected, value, layout]);
|
|
139
|
+
return /* @__PURE__ */ jsx(Theme, { forceClassName: true, name: isSelected ? "active" : null, children: /* @__PURE__ */ jsx(
|
|
140
|
+
RovingFocusGroup.Item,
|
|
141
|
+
{
|
|
142
|
+
asChild: true,
|
|
143
|
+
...rovingFocusGroupScope,
|
|
144
|
+
focusable: !disabled,
|
|
145
|
+
active: isSelected,
|
|
146
|
+
children: /* @__PURE__ */ jsx(
|
|
147
|
+
TabsTriggerFrame,
|
|
148
|
+
{
|
|
149
|
+
onLayout: (event) => {
|
|
150
|
+
if (!isWeb) {
|
|
151
|
+
setLayout(event.nativeEvent.layout);
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
onHoverIn: composeEventHandlers(props.onHoverIn, () => {
|
|
155
|
+
if (layout) {
|
|
156
|
+
onInteraction == null ? void 0 : onInteraction("hover", layout);
|
|
157
|
+
}
|
|
158
|
+
}),
|
|
159
|
+
onHoverOut: composeEventHandlers(props.onHoverOut, () => {
|
|
160
|
+
onInteraction == null ? void 0 : onInteraction("hover", null);
|
|
161
|
+
}),
|
|
162
|
+
role: "tab",
|
|
163
|
+
"aria-selected": isSelected,
|
|
164
|
+
"aria-controls": contentId,
|
|
165
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
166
|
+
"data-disabled": disabled ? "" : void 0,
|
|
167
|
+
disabled,
|
|
168
|
+
id: triggerId,
|
|
169
|
+
size: context.size,
|
|
170
|
+
...triggerProps,
|
|
171
|
+
ref: composeRefs(forwardedRef, triggerRef),
|
|
172
|
+
onPress: composeEventHandlers(props.onPress ?? void 0, (event) => {
|
|
173
|
+
const webChecks = !isWeb || event.button === 0 && event.ctrlKey === false;
|
|
174
|
+
if (!disabled && !isSelected && webChecks) {
|
|
175
|
+
context.onChange(value);
|
|
176
|
+
} else {
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
}
|
|
179
|
+
}),
|
|
180
|
+
...isWeb && {
|
|
181
|
+
type: "button",
|
|
182
|
+
onKeyDown: composeEventHandlers(
|
|
183
|
+
props.onKeyDown,
|
|
184
|
+
(event) => {
|
|
185
|
+
if ([" ", "Enter"].includes(event.key)) {
|
|
186
|
+
context.onChange(value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
onFocus: composeEventHandlers(props.onFocus, (event) => {
|
|
191
|
+
if (layout) {
|
|
192
|
+
onInteraction == null ? void 0 : onInteraction("focus", layout);
|
|
193
|
+
}
|
|
194
|
+
const isAutomaticActivation = context.activationMode !== "manual";
|
|
195
|
+
if (!isSelected && !disabled && isAutomaticActivation) {
|
|
196
|
+
context.onChange(value);
|
|
197
|
+
}
|
|
198
|
+
}),
|
|
199
|
+
onBlur: composeEventHandlers(props.onFocus, () => {
|
|
200
|
+
onInteraction == null ? void 0 : onInteraction("focus", null);
|
|
201
|
+
})
|
|
202
|
+
},
|
|
203
|
+
...groupItemProps
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
) });
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
TabsTrigger.displayName = TRIGGER_NAME;
|
|
212
|
+
const CONTENT_NAME = "TabsContent";
|
|
213
|
+
const TabsContentFrame = styled(ThemeableStack, {
|
|
214
|
+
name: CONTENT_NAME
|
|
215
|
+
});
|
|
216
|
+
const TabsContent = React.forwardRef(
|
|
217
|
+
(props, forwardedRef) => {
|
|
218
|
+
const { __scopeTabs, value, forceMount, children, ...contentProps } = props;
|
|
219
|
+
const context = useTabsContext(CONTENT_NAME, __scopeTabs);
|
|
220
|
+
const isSelected = value === context.value;
|
|
221
|
+
const show = forceMount || isSelected;
|
|
222
|
+
const triggerId = makeTriggerId(context.baseId, value);
|
|
223
|
+
const contentId = makeContentId(context.baseId, value);
|
|
224
|
+
if (!show)
|
|
225
|
+
return null;
|
|
226
|
+
return /* @__PURE__ */ jsx(
|
|
227
|
+
TabsContentFrame,
|
|
228
|
+
{
|
|
229
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
230
|
+
"data-orientation": context.orientation,
|
|
231
|
+
role: "tabpanel",
|
|
232
|
+
"aria-labelledby": triggerId,
|
|
233
|
+
hidden: !show,
|
|
234
|
+
id: contentId,
|
|
235
|
+
tabIndex: 0,
|
|
236
|
+
...contentProps,
|
|
237
|
+
ref: forwardedRef,
|
|
238
|
+
children
|
|
239
|
+
},
|
|
240
|
+
value
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
TabsContent.displayName = CONTENT_NAME;
|
|
245
|
+
const TABS_NAME = "Tabs";
|
|
246
|
+
const [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [
|
|
247
|
+
createRovingFocusGroupScope
|
|
248
|
+
]);
|
|
249
|
+
const useRovingFocusGroupScope = createRovingFocusGroupScope();
|
|
250
|
+
const [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);
|
|
251
|
+
const TabsFrame = styled(SizableStack, {
|
|
252
|
+
name: TABS_NAME
|
|
253
|
+
});
|
|
254
|
+
const Tabs = withStaticProperties(
|
|
255
|
+
React.forwardRef(
|
|
256
|
+
(props, forwardedRef) => {
|
|
257
|
+
const {
|
|
258
|
+
__scopeTabs,
|
|
259
|
+
value: valueProp,
|
|
260
|
+
onValueChange,
|
|
261
|
+
defaultValue,
|
|
262
|
+
orientation = "horizontal",
|
|
263
|
+
dir,
|
|
264
|
+
activationMode = "automatic",
|
|
265
|
+
size = "$true",
|
|
266
|
+
...tabsProps
|
|
267
|
+
} = props;
|
|
268
|
+
const direction = useDirection(dir);
|
|
269
|
+
const [value, setValue] = useControllableState({
|
|
270
|
+
prop: valueProp,
|
|
271
|
+
onChange: onValueChange,
|
|
272
|
+
defaultProp: defaultValue ?? ""
|
|
273
|
+
});
|
|
274
|
+
return /* @__PURE__ */ jsx(
|
|
275
|
+
TabsProvider,
|
|
276
|
+
{
|
|
277
|
+
scope: __scopeTabs,
|
|
278
|
+
baseId: useId(),
|
|
279
|
+
value,
|
|
280
|
+
onChange: setValue,
|
|
281
|
+
orientation,
|
|
282
|
+
dir: direction,
|
|
283
|
+
activationMode,
|
|
284
|
+
size,
|
|
285
|
+
children: /* @__PURE__ */ jsx(
|
|
286
|
+
TabsFrame,
|
|
287
|
+
{
|
|
288
|
+
direction,
|
|
289
|
+
"data-orientation": orientation,
|
|
290
|
+
...tabsProps,
|
|
291
|
+
ref: forwardedRef
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
),
|
|
298
|
+
{
|
|
299
|
+
List: TabsList,
|
|
300
|
+
Trigger: TabsTrigger,
|
|
301
|
+
Content: TabsContent
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
Tabs.displayName = TABS_NAME;
|
|
305
|
+
function makeTriggerId(baseId, value) {
|
|
306
|
+
return `${baseId}-trigger-${value}`;
|
|
307
|
+
}
|
|
308
|
+
function makeContentId(baseId, value) {
|
|
309
|
+
return `${baseId}-content-${value}`;
|
|
310
|
+
}
|
|
311
|
+
export {
|
|
312
|
+
Tabs
|
|
313
|
+
};
|
|
314
|
+
//# sourceMappingURL=Tabs.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/Tabs.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { Scope } from '@tamagui/create-context'\nimport { createContextScope } from '@tamagui/create-context'\nimport { getButtonSized } from '@tamagui/get-button-sized'\nimport { Group, GroupProps, useGroupItem } from '@tamagui/group'\nimport { RovingFocusGroup, createRovingFocusGroupScope } from '@tamagui/roving-focus'\nimport { SizableStack, ThemeableStack, ThemeableStackProps } from '@tamagui/stacks'\nimport { useControllableState } from '@tamagui/use-controllable-state'\nimport { useDirection } from '@tamagui/use-direction'\nimport {\n GetProps,\n SizeTokens,\n Theme,\n composeEventHandlers,\n composeRefs,\n isWeb,\n styled,\n useId,\n withStaticProperties,\n} from '@tamagui/web'\nimport * as React from 'react'\nimport type { LayoutRectangle } from 'react-native'\n\n/* -------------------------------------------------------------------------------------------------\n * TabsList\n * -----------------------------------------------------------------------------------------------*/\n\nconst TAB_LIST_NAME = 'TabsList'\n\nconst TabsListFrame = styled(Group, {\n name: TAB_LIST_NAME,\n focusable: true,\n // defaultVariants: {\n // flexGrow: 0,\n // },\n})\n\ntype TabsListFrameProps = GroupProps\n\ntype TabsListProps = TabsListFrameProps & {\n /**\n * Whether to loop over after reaching the end or start of the items\n * @default true\n */\n loop?: boolean\n}\n\nconst TabsList = React.forwardRef<HTMLDivElement, TabsListProps>(\n (props: ScopedProps<TabsListProps>, forwardedRef) => {\n const { __scopeTabs, loop = true, children, ...listProps } = props\n const context = useTabsContext(TAB_LIST_NAME, __scopeTabs)\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs)\n\n return (\n <RovingFocusGroup\n asChild\n orientation={context.orientation}\n dir={context.dir}\n loop={loop}\n {...rovingFocusGroupScope}\n >\n <TabsListFrame\n role=\"tablist\"\n aria-orientation={context.orientation}\n ref={forwardedRef}\n axis={context.orientation}\n {...listProps}\n >\n {children}\n </TabsListFrame>\n </RovingFocusGroup>\n )\n }\n)\n\nTabsList.displayName = TAB_LIST_NAME\n\n/* -------------------------------------------------------------------------------------------------\n * TabsTrigger\n * -----------------------------------------------------------------------------------------------*/\n\nconst TRIGGER_NAME = 'TabsTrigger'\n\nconst TabsTriggerFrame = styled(ThemeableStack, {\n name: TRIGGER_NAME,\n justifyContent: 'center',\n alignItems: 'center',\n flexWrap: 'nowrap',\n flexDirection: 'row',\n cursor: 'pointer',\n\n variants: {\n size: {\n '...size': getButtonSized,\n },\n disabled: {\n true: {\n pointerEvents: 'none',\n },\n },\n theme: {\n Button: {\n focusable: true,\n hoverTheme: true,\n pressTheme: true,\n backgrounded: true,\n borderWidth: 1,\n borderColor: 'transparent',\n\n pressStyle: {\n borderColor: 'transparent',\n },\n\n hoverStyle: {\n borderColor: 'transparent',\n },\n\n focusStyle: {\n borderColor: '$borderColorFocus',\n },\n },\n },\n },\n})\n\ntype TabTriggerLayout = LayoutRectangle\ntype InteractionType = 'select' | 'focus' | 'hover'\n\ntype TabsTriggerFrameProps = ThemeableStackProps\ntype TabsTriggerProps = TabsTriggerFrameProps & {\n /** The value for the tabs state to be changed to after activation of the trigger */\n value: string\n\n /** Used for making custom indicators when trigger interacted with */\n onInteraction?: (type: InteractionType, layout: TabTriggerLayout | null) => void\n}\n\nconst TabsTrigger = TabsTriggerFrame.extractable(\n React.forwardRef<HTMLButtonElement, TabsTriggerProps>(\n (props: ScopedProps<TabsTriggerProps>, forwardedRef) => {\n const {\n __scopeTabs,\n value,\n disabled = false,\n onInteraction,\n ...triggerProps\n } = props\n const context = useTabsContext(TRIGGER_NAME, __scopeTabs)\n const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs)\n const triggerId = makeTriggerId(context.baseId, value)\n const contentId = makeContentId(context.baseId, value)\n const isSelected = value === context.value\n const [layout, setLayout] = React.useState<TabTriggerLayout | null>(null)\n const triggerRef = React.useRef<HTMLButtonElement>(null)\n const groupItemProps = useGroupItem({ disabled })\n React.useEffect(() => {\n if (!triggerRef.current || !isWeb) return\n\n function getTriggerSize() {\n if (!triggerRef.current) return\n setLayout({\n width: triggerRef.current.offsetWidth,\n height: triggerRef.current.offsetHeight,\n x: triggerRef.current.offsetLeft,\n y: triggerRef.current.offsetTop,\n })\n }\n getTriggerSize()\n\n const observer = new ResizeObserver(getTriggerSize)\n observer.observe(triggerRef.current)\n\n return () => {\n if (!triggerRef.current) return\n observer.unobserve(triggerRef.current)\n }\n }, [])\n\n React.useEffect(() => {\n if (isSelected && layout) {\n onInteraction?.('select', layout)\n }\n }, [isSelected, value, layout])\n\n return (\n <Theme forceClassName name={isSelected ? 'active' : null}>\n <RovingFocusGroup.Item\n asChild\n {...rovingFocusGroupScope}\n focusable={!disabled}\n active={isSelected}\n >\n <TabsTriggerFrame\n onLayout={(event) => {\n if (!isWeb) {\n setLayout(event.nativeEvent.layout)\n }\n }}\n onHoverIn={composeEventHandlers(props.onHoverIn, () => {\n if (layout) {\n onInteraction?.('hover', layout)\n }\n })}\n onHoverOut={composeEventHandlers(props.onHoverOut, () => {\n onInteraction?.('hover', null)\n })}\n role=\"tab\"\n aria-selected={isSelected}\n aria-controls={contentId}\n data-state={isSelected ? 'active' : 'inactive'}\n data-disabled={disabled ? '' : undefined}\n disabled={disabled}\n id={triggerId}\n // @ts-ignore\n size={context.size}\n {...triggerProps}\n ref={composeRefs(forwardedRef, triggerRef)}\n onPress={composeEventHandlers(props.onPress ?? undefined, (event) => {\n // only call handler if it's the left button (mousedown gets triggered by all mouse buttons)\n // but not when the control key is pressed (avoiding MacOS right click)\n\n const webChecks =\n !isWeb ||\n ((event as unknown as React.MouseEvent).button === 0 &&\n (event as unknown as React.MouseEvent).ctrlKey === false)\n if (!disabled && !isSelected && webChecks) {\n context.onChange(value)\n } else {\n // prevent focus to avoid accidental activation\n event.preventDefault()\n }\n })}\n {...(isWeb && {\n type: 'button',\n onKeyDown: composeEventHandlers(\n (props as React.HTMLProps<HTMLButtonElement>).onKeyDown,\n (event) => {\n if ([' ', 'Enter'].includes(event.key)) {\n context.onChange(value)\n }\n }\n ),\n onFocus: composeEventHandlers(props.onFocus, (event) => {\n if (layout) {\n onInteraction?.('focus', layout)\n }\n // handle \"automatic\" activation if necessary\n // ie. activate tab following focus\n const isAutomaticActivation = context.activationMode !== 'manual'\n if (!isSelected && !disabled && isAutomaticActivation) {\n context.onChange(value)\n }\n }),\n onBlur: composeEventHandlers(props.onFocus, () => {\n onInteraction?.('focus', null)\n }),\n })}\n {...groupItemProps}\n />\n </RovingFocusGroup.Item>\n </Theme>\n )\n }\n )\n)\n\nTabsTrigger.displayName = TRIGGER_NAME\n\n/* -------------------------------------------------------------------------------------------------\n * TabsContent\n * -----------------------------------------------------------------------------------------------*/\n\nconst CONTENT_NAME = 'TabsContent'\n\nconst TabsContentFrame = styled(ThemeableStack, {\n name: CONTENT_NAME,\n})\ntype TabsContentFrameProps = GetProps<typeof TabsContentFrame>\ntype TabsContentProps = TabsContentFrameProps & {\n /** Will show the content when the value matches the state of Tabs root */\n value: string\n\n /**\n * Used to force mounting when more control is needed. Useful when\n * controlling animation with Tamagui animations.\n */\n forceMount?: true\n}\n\nconst TabsContent = React.forwardRef<HTMLDivElement, TabsContentProps>(\n (props: ScopedProps<TabsContentProps>, forwardedRef) => {\n const { __scopeTabs, value, forceMount, children, ...contentProps } = props\n const context = useTabsContext(CONTENT_NAME, __scopeTabs)\n const isSelected = value === context.value\n const show = forceMount || isSelected\n\n const triggerId = makeTriggerId(context.baseId, value)\n const contentId = makeContentId(context.baseId, value)\n\n if (!show) return null\n return (\n <TabsContentFrame\n key={value}\n data-state={isSelected ? 'active' : 'inactive'}\n data-orientation={context.orientation}\n role=\"tabpanel\"\n aria-labelledby={triggerId}\n // @ts-ignore\n hidden={!show}\n id={contentId}\n tabIndex={0}\n {...contentProps}\n ref={forwardedRef}\n >\n {children}\n </TabsContentFrame>\n )\n }\n)\n\nTabsContent.displayName = CONTENT_NAME\n\n/* -------------------------------------------------------------------------------------------------\n * Tabs\n * -----------------------------------------------------------------------------------------------*/\n\nconst TABS_NAME = 'Tabs'\n\ntype ScopedProps<P> = P & { __scopeTabs?: Scope }\nconst [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [\n createRovingFocusGroupScope,\n])\nconst useRovingFocusGroupScope = createRovingFocusGroupScope()\n\ntype TabsContextValue = {\n baseId: string\n value?: string\n onChange: (value: string) => void\n orientation?: TabsProps['orientation']\n dir?: TabsProps['dir']\n activationMode?: TabsProps['activationMode']\n size: SizeTokens\n}\n\nconst [TabsProvider, useTabsContext] = createTabsContext<TabsContextValue>(TABS_NAME)\n\nconst TabsFrame = styled(SizableStack, {\n name: TABS_NAME,\n})\ntype RovingFocusGroupProps = React.ComponentPropsWithoutRef<typeof RovingFocusGroup>\ntype TabsFrameProps = GetProps<typeof TabsFrame>\ntype TabsProps = TabsFrameProps & {\n /** The value for the selected tab, if controlled */\n value?: string\n /** The value of the tab to select by default, if uncontrolled */\n defaultValue?: string\n /** A function called when a new tab is selected */\n onValueChange?: (value: string) => void\n /**\n * The orientation the tabs are layed out.\n * Mainly so arrow navigation is done accordingly (left & right vs. up & down)\n * @defaultValue horizontal\n */\n orientation?: RovingFocusGroupProps['orientation']\n /**\n * The direction of navigation between toolbar items.\n */\n dir?: RovingFocusGroupProps['dir']\n /**\n * Whether a tab is activated automatically or manually. Only supported in web.\n * @defaultValue automatic\n * */\n activationMode?: 'automatic' | 'manual'\n}\n\nexport const Tabs = withStaticProperties(\n React.forwardRef<HTMLDivElement, TabsProps>(\n (props: ScopedProps<TabsProps>, forwardedRef) => {\n const {\n __scopeTabs,\n value: valueProp,\n onValueChange,\n defaultValue,\n orientation = 'horizontal',\n dir,\n activationMode = 'automatic',\n size = '$true',\n ...tabsProps\n } = props\n const direction = useDirection(dir)\n const [value, setValue] = useControllableState({\n prop: valueProp,\n onChange: onValueChange,\n defaultProp: defaultValue ?? '',\n })\n\n return (\n <TabsProvider\n scope={__scopeTabs}\n baseId={useId()}\n value={value}\n onChange={setValue}\n orientation={orientation}\n dir={direction}\n activationMode={activationMode}\n size={size}\n >\n <TabsFrame\n direction={direction}\n // dir={direction}\n data-orientation={orientation}\n {...tabsProps}\n ref={forwardedRef}\n />\n </TabsProvider>\n )\n }\n ),\n {\n List: TabsList,\n Trigger: TabsTrigger,\n Content: TabsContent,\n }\n)\n\nTabs.displayName = TABS_NAME\n\n/* ---------------------------------------------------------------------------------------------- */\n\nfunction makeTriggerId(baseId: string, value: string) {\n return `${baseId}-trigger-${value}`\n}\n\nfunction makeContentId(baseId: string, value: string) {\n return `${baseId}-content-${value}`\n}\n\nexport type {\n TabsProps,\n TabsListProps,\n TabsTriggerProps,\n TabsContentProps,\n TabTriggerLayout,\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContextScope } from "@tamagui/create-context";
|
|
3
|
+
import { getButtonSized } from "@tamagui/get-button-sized";
|
|
4
|
+
import { Group, useGroupItem } from "@tamagui/group";
|
|
5
|
+
import { RovingFocusGroup, createRovingFocusGroupScope } from "@tamagui/roving-focus";
|
|
6
|
+
import { SizableStack, ThemeableStack } from "@tamagui/stacks";
|
|
7
|
+
import { useControllableState } from "@tamagui/use-controllable-state";
|
|
8
|
+
import { useDirection } from "@tamagui/use-direction";
|
|
9
|
+
import {
|
|
10
|
+
Theme,
|
|
11
|
+
composeEventHandlers,
|
|
12
|
+
composeRefs,
|
|
13
|
+
isWeb,
|
|
14
|
+
styled,
|
|
15
|
+
useId,
|
|
16
|
+
withStaticProperties
|
|
17
|
+
} from "@tamagui/web";
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
const TAB_LIST_NAME = "TabsList";
|
|
20
|
+
const TabsListFrame = styled(Group, {
|
|
21
|
+
name: TAB_LIST_NAME,
|
|
22
|
+
focusable: true
|
|
23
|
+
// defaultVariants: {
|
|
24
|
+
// flexGrow: 0,
|
|
25
|
+
// },
|
|
26
|
+
});
|
|
27
|
+
const TabsList = React.forwardRef(
|
|
28
|
+
(props, forwardedRef) => {
|
|
29
|
+
const { __scopeTabs, loop = true, children, ...listProps } = props;
|
|
30
|
+
const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);
|
|
31
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
32
|
+
return /* @__PURE__ */ jsx(
|
|
33
|
+
RovingFocusGroup,
|
|
34
|
+
{
|
|
35
|
+
asChild: true,
|
|
36
|
+
orientation: context.orientation,
|
|
37
|
+
dir: context.dir,
|
|
38
|
+
loop,
|
|
39
|
+
...rovingFocusGroupScope,
|
|
40
|
+
children: /* @__PURE__ */ jsx(
|
|
41
|
+
TabsListFrame,
|
|
42
|
+
{
|
|
43
|
+
role: "tablist",
|
|
44
|
+
"aria-orientation": context.orientation,
|
|
45
|
+
ref: forwardedRef,
|
|
46
|
+
axis: context.orientation,
|
|
47
|
+
...listProps,
|
|
48
|
+
children
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
TabsList.displayName = TAB_LIST_NAME;
|
|
56
|
+
const TRIGGER_NAME = "TabsTrigger";
|
|
57
|
+
const TabsTriggerFrame = styled(ThemeableStack, {
|
|
58
|
+
name: TRIGGER_NAME,
|
|
59
|
+
justifyContent: "center",
|
|
60
|
+
alignItems: "center",
|
|
61
|
+
flexWrap: "nowrap",
|
|
62
|
+
flexDirection: "row",
|
|
63
|
+
cursor: "pointer",
|
|
64
|
+
variants: {
|
|
65
|
+
size: {
|
|
66
|
+
"...size": getButtonSized
|
|
67
|
+
},
|
|
68
|
+
disabled: {
|
|
69
|
+
true: {
|
|
70
|
+
pointerEvents: "none"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
theme: {
|
|
74
|
+
Button: {
|
|
75
|
+
focusable: true,
|
|
76
|
+
hoverTheme: true,
|
|
77
|
+
pressTheme: true,
|
|
78
|
+
backgrounded: true,
|
|
79
|
+
borderWidth: 1,
|
|
80
|
+
borderColor: "transparent",
|
|
81
|
+
pressStyle: {
|
|
82
|
+
borderColor: "transparent"
|
|
83
|
+
},
|
|
84
|
+
hoverStyle: {
|
|
85
|
+
borderColor: "transparent"
|
|
86
|
+
},
|
|
87
|
+
focusStyle: {
|
|
88
|
+
borderColor: "$borderColorFocus"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const TabsTrigger = TabsTriggerFrame.extractable(
|
|
95
|
+
React.forwardRef(
|
|
96
|
+
(props, forwardedRef) => {
|
|
97
|
+
const {
|
|
98
|
+
__scopeTabs,
|
|
99
|
+
value,
|
|
100
|
+
disabled = false,
|
|
101
|
+
onInteraction,
|
|
102
|
+
...triggerProps
|
|
103
|
+
} = props;
|
|
104
|
+
const context = useTabsContext(TRIGGER_NAME, __scopeTabs);
|
|
105
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
106
|
+
const triggerId = makeTriggerId(context.baseId, value);
|
|
107
|
+
const contentId = makeContentId(context.baseId, value);
|
|
108
|
+
const isSelected = value === context.value;
|
|
109
|
+
const [layout, setLayout] = React.useState(null);
|
|
110
|
+
const triggerRef = React.useRef(null);
|
|
111
|
+
const groupItemProps = useGroupItem({ disabled });
|
|
112
|
+
React.useEffect(() => {
|
|
113
|
+
if (!triggerRef.current || !isWeb)
|
|
114
|
+
return;
|
|
115
|
+
function getTriggerSize() {
|
|
116
|
+
if (!triggerRef.current)
|
|
117
|
+
return;
|
|
118
|
+
setLayout({
|
|
119
|
+
width: triggerRef.current.offsetWidth,
|
|
120
|
+
height: triggerRef.current.offsetHeight,
|
|
121
|
+
x: triggerRef.current.offsetLeft,
|
|
122
|
+
y: triggerRef.current.offsetTop
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
getTriggerSize();
|
|
126
|
+
const observer = new ResizeObserver(getTriggerSize);
|
|
127
|
+
observer.observe(triggerRef.current);
|
|
128
|
+
return () => {
|
|
129
|
+
if (!triggerRef.current)
|
|
130
|
+
return;
|
|
131
|
+
observer.unobserve(triggerRef.current);
|
|
132
|
+
};
|
|
133
|
+
}, []);
|
|
134
|
+
React.useEffect(() => {
|
|
135
|
+
if (isSelected && layout) {
|
|
136
|
+
onInteraction == null ? void 0 : onInteraction("select", layout);
|
|
137
|
+
}
|
|
138
|
+
}, [isSelected, value, layout]);
|
|
139
|
+
return /* @__PURE__ */ jsx(Theme, { forceClassName: true, name: isSelected ? "active" : null, children: /* @__PURE__ */ jsx(
|
|
140
|
+
RovingFocusGroup.Item,
|
|
141
|
+
{
|
|
142
|
+
asChild: true,
|
|
143
|
+
...rovingFocusGroupScope,
|
|
144
|
+
focusable: !disabled,
|
|
145
|
+
active: isSelected,
|
|
146
|
+
children: /* @__PURE__ */ jsx(
|
|
147
|
+
TabsTriggerFrame,
|
|
148
|
+
{
|
|
149
|
+
onLayout: (event) => {
|
|
150
|
+
if (!isWeb) {
|
|
151
|
+
setLayout(event.nativeEvent.layout);
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
onHoverIn: composeEventHandlers(props.onHoverIn, () => {
|
|
155
|
+
if (layout) {
|
|
156
|
+
onInteraction == null ? void 0 : onInteraction("hover", layout);
|
|
157
|
+
}
|
|
158
|
+
}),
|
|
159
|
+
onHoverOut: composeEventHandlers(props.onHoverOut, () => {
|
|
160
|
+
onInteraction == null ? void 0 : onInteraction("hover", null);
|
|
161
|
+
}),
|
|
162
|
+
role: "tab",
|
|
163
|
+
"aria-selected": isSelected,
|
|
164
|
+
"aria-controls": contentId,
|
|
165
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
166
|
+
"data-disabled": disabled ? "" : void 0,
|
|
167
|
+
disabled,
|
|
168
|
+
id: triggerId,
|
|
169
|
+
size: context.size,
|
|
170
|
+
...triggerProps,
|
|
171
|
+
ref: composeRefs(forwardedRef, triggerRef),
|
|
172
|
+
onPress: composeEventHandlers(props.onPress ?? void 0, (event) => {
|
|
173
|
+
const webChecks = !isWeb || event.button === 0 && event.ctrlKey === false;
|
|
174
|
+
if (!disabled && !isSelected && webChecks) {
|
|
175
|
+
context.onChange(value);
|
|
176
|
+
} else {
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
}
|
|
179
|
+
}),
|
|
180
|
+
...isWeb && {
|
|
181
|
+
type: "button",
|
|
182
|
+
onKeyDown: composeEventHandlers(
|
|
183
|
+
props.onKeyDown,
|
|
184
|
+
(event) => {
|
|
185
|
+
if ([" ", "Enter"].includes(event.key)) {
|
|
186
|
+
context.onChange(value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
onFocus: composeEventHandlers(props.onFocus, (event) => {
|
|
191
|
+
if (layout) {
|
|
192
|
+
onInteraction == null ? void 0 : onInteraction("focus", layout);
|
|
193
|
+
}
|
|
194
|
+
const isAutomaticActivation = context.activationMode !== "manual";
|
|
195
|
+
if (!isSelected && !disabled && isAutomaticActivation) {
|
|
196
|
+
context.onChange(value);
|
|
197
|
+
}
|
|
198
|
+
}),
|
|
199
|
+
onBlur: composeEventHandlers(props.onFocus, () => {
|
|
200
|
+
onInteraction == null ? void 0 : onInteraction("focus", null);
|
|
201
|
+
})
|
|
202
|
+
},
|
|
203
|
+
...groupItemProps
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
) });
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
TabsTrigger.displayName = TRIGGER_NAME;
|
|
212
|
+
const CONTENT_NAME = "TabsContent";
|
|
213
|
+
const TabsContentFrame = styled(ThemeableStack, {
|
|
214
|
+
name: CONTENT_NAME
|
|
215
|
+
});
|
|
216
|
+
const TabsContent = React.forwardRef(
|
|
217
|
+
(props, forwardedRef) => {
|
|
218
|
+
const { __scopeTabs, value, forceMount, children, ...contentProps } = props;
|
|
219
|
+
const context = useTabsContext(CONTENT_NAME, __scopeTabs);
|
|
220
|
+
const isSelected = value === context.value;
|
|
221
|
+
const show = forceMount || isSelected;
|
|
222
|
+
const triggerId = makeTriggerId(context.baseId, value);
|
|
223
|
+
const contentId = makeContentId(context.baseId, value);
|
|
224
|
+
if (!show)
|
|
225
|
+
return null;
|
|
226
|
+
return /* @__PURE__ */ jsx(
|
|
227
|
+
TabsContentFrame,
|
|
228
|
+
{
|
|
229
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
230
|
+
"data-orientation": context.orientation,
|
|
231
|
+
role: "tabpanel",
|
|
232
|
+
"aria-labelledby": triggerId,
|
|
233
|
+
hidden: !show,
|
|
234
|
+
id: contentId,
|
|
235
|
+
tabIndex: 0,
|
|
236
|
+
...contentProps,
|
|
237
|
+
ref: forwardedRef,
|
|
238
|
+
children
|
|
239
|
+
},
|
|
240
|
+
value
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
TabsContent.displayName = CONTENT_NAME;
|
|
245
|
+
const TABS_NAME = "Tabs";
|
|
246
|
+
const [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [
|
|
247
|
+
createRovingFocusGroupScope
|
|
248
|
+
]);
|
|
249
|
+
const useRovingFocusGroupScope = createRovingFocusGroupScope();
|
|
250
|
+
const [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);
|
|
251
|
+
const TabsFrame = styled(SizableStack, {
|
|
252
|
+
name: TABS_NAME
|
|
253
|
+
});
|
|
254
|
+
const Tabs = withStaticProperties(
|
|
255
|
+
React.forwardRef(
|
|
256
|
+
(props, forwardedRef) => {
|
|
257
|
+
const {
|
|
258
|
+
__scopeTabs,
|
|
259
|
+
value: valueProp,
|
|
260
|
+
onValueChange,
|
|
261
|
+
defaultValue,
|
|
262
|
+
orientation = "horizontal",
|
|
263
|
+
dir,
|
|
264
|
+
activationMode = "automatic",
|
|
265
|
+
size = "$true",
|
|
266
|
+
...tabsProps
|
|
267
|
+
} = props;
|
|
268
|
+
const direction = useDirection(dir);
|
|
269
|
+
const [value, setValue] = useControllableState({
|
|
270
|
+
prop: valueProp,
|
|
271
|
+
onChange: onValueChange,
|
|
272
|
+
defaultProp: defaultValue ?? ""
|
|
273
|
+
});
|
|
274
|
+
return /* @__PURE__ */ jsx(
|
|
275
|
+
TabsProvider,
|
|
276
|
+
{
|
|
277
|
+
scope: __scopeTabs,
|
|
278
|
+
baseId: useId(),
|
|
279
|
+
value,
|
|
280
|
+
onChange: setValue,
|
|
281
|
+
orientation,
|
|
282
|
+
dir: direction,
|
|
283
|
+
activationMode,
|
|
284
|
+
size,
|
|
285
|
+
children: /* @__PURE__ */ jsx(
|
|
286
|
+
TabsFrame,
|
|
287
|
+
{
|
|
288
|
+
direction,
|
|
289
|
+
"data-orientation": orientation,
|
|
290
|
+
...tabsProps,
|
|
291
|
+
ref: forwardedRef
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
),
|
|
298
|
+
{
|
|
299
|
+
List: TabsList,
|
|
300
|
+
Trigger: TabsTrigger,
|
|
301
|
+
Content: TabsContent
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
Tabs.displayName = TABS_NAME;
|
|
305
|
+
function makeTriggerId(baseId, value) {
|
|
306
|
+
return `${baseId}-trigger-${value}`;
|
|
307
|
+
}
|
|
308
|
+
function makeContentId(baseId, value) {
|
|
309
|
+
return `${baseId}-content-${value}`;
|
|
310
|
+
}
|
|
311
|
+
export {
|
|
312
|
+
Tabs
|
|
313
|
+
};
|
|
314
|
+
//# sourceMappingURL=Tabs.mjs.map
|