@hanzogui/tabs 2.0.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/StyledContext.cjs +31 -0
  3. package/dist/cjs/StyledContext.native.js +34 -0
  4. package/dist/cjs/StyledContext.native.js.map +1 -0
  5. package/dist/cjs/Tabs.cjs +81 -0
  6. package/dist/cjs/Tabs.native.js +84 -0
  7. package/dist/cjs/Tabs.native.js.map +1 -0
  8. package/dist/cjs/createTabs.cjs +262 -0
  9. package/dist/cjs/createTabs.native.js +280 -0
  10. package/dist/cjs/createTabs.native.js.map +1 -0
  11. package/dist/cjs/index.cjs +35 -0
  12. package/dist/cjs/index.native.js +38 -0
  13. package/dist/cjs/index.native.js.map +1 -0
  14. package/dist/esm/StyledContext.mjs +7 -0
  15. package/dist/esm/StyledContext.mjs.map +1 -0
  16. package/dist/esm/StyledContext.native.js +7 -0
  17. package/dist/esm/StyledContext.native.js.map +1 -0
  18. package/dist/esm/Tabs.mjs +56 -0
  19. package/dist/esm/Tabs.mjs.map +1 -0
  20. package/dist/esm/Tabs.native.js +56 -0
  21. package/dist/esm/Tabs.native.js.map +1 -0
  22. package/dist/esm/createTabs.mjs +228 -0
  23. package/dist/esm/createTabs.mjs.map +1 -0
  24. package/dist/esm/createTabs.native.js +243 -0
  25. package/dist/esm/createTabs.native.js.map +1 -0
  26. package/dist/esm/index.js +11 -0
  27. package/dist/esm/index.js.map +1 -0
  28. package/dist/esm/index.mjs +11 -0
  29. package/dist/esm/index.mjs.map +1 -0
  30. package/dist/esm/index.native.js +11 -0
  31. package/dist/esm/index.native.js.map +1 -0
  32. package/dist/jsx/StyledContext.mjs +7 -0
  33. package/dist/jsx/StyledContext.mjs.map +1 -0
  34. package/dist/jsx/StyledContext.native.js +34 -0
  35. package/dist/jsx/StyledContext.native.js.map +1 -0
  36. package/dist/jsx/Tabs.mjs +56 -0
  37. package/dist/jsx/Tabs.mjs.map +1 -0
  38. package/dist/jsx/Tabs.native.js +84 -0
  39. package/dist/jsx/Tabs.native.js.map +1 -0
  40. package/dist/jsx/createTabs.mjs +228 -0
  41. package/dist/jsx/createTabs.mjs.map +1 -0
  42. package/dist/jsx/createTabs.native.js +280 -0
  43. package/dist/jsx/createTabs.native.js.map +1 -0
  44. package/dist/jsx/index.js +11 -0
  45. package/dist/jsx/index.js.map +1 -0
  46. package/dist/jsx/index.mjs +11 -0
  47. package/dist/jsx/index.mjs.map +1 -0
  48. package/dist/jsx/index.native.js +38 -0
  49. package/dist/jsx/index.native.js.map +1 -0
  50. package/package.json +65 -0
  51. package/src/StyledContext.tsx +19 -0
  52. package/src/Tabs.tsx +69 -0
  53. package/src/createTabs.tsx +433 -0
  54. package/src/index.ts +10 -0
  55. package/types/StyledContext.d.ts +19 -0
  56. package/types/Tabs.d.ts +38 -0
  57. package/types/createTabs.d.ts +527 -0
  58. package/types/index.d.ts +539 -0
@@ -0,0 +1,11 @@
1
+ import { createTabs } from "./createTabs.mjs";
2
+ import { DefaultTabsContentFrame, DefaultTabsFrame, DefaultTabsTabFrame } from "./Tabs.mjs";
3
+ export * from "./createTabs.mjs";
4
+ export * from "./StyledContext.mjs";
5
+ const Tabs = createTabs({
6
+ ContentFrame: DefaultTabsContentFrame,
7
+ TabFrame: DefaultTabsTabFrame,
8
+ TabsFrame: DefaultTabsFrame
9
+ });
10
+ export { Tabs };
11
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createTabs","DefaultTabsContentFrame","DefaultTabsFrame","DefaultTabsTabFrame","Tabs","ContentFrame","TabFrame","TabsFrame"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,UAAA,QAAkB;AAC3B,SAASC,uBAAA,EAAyBC,gBAAA,EAAkBC,mBAAA,QAA2B;AAC/E,cAAc;AACd,cAAc;AAEP,MAAMC,IAAA,GAAOJ,UAAA,CAAW;EAC7BK,YAAA,EAAcJ,uBAAA;EACdK,QAAA,EAAUH,mBAAA;EACVI,SAAA,EAAWL;AACb,CAAC","ignoreList":[]}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: !0
11
+ });
12
+ },
13
+ __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
15
+ get: () => from[key],
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ return to;
19
+ },
20
+ __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
22
+ value: !0
23
+ }), mod);
24
+ var index_exports = {};
25
+ __export(index_exports, {
26
+ Tabs: () => Tabs
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+ var import_createTabs = require("./createTabs.native.js"),
30
+ import_Tabs = require("./Tabs.native.js");
31
+ __reExport(index_exports, require("./createTabs.native.js"), module.exports);
32
+ __reExport(index_exports, require("./StyledContext.native.js"), module.exports);
33
+ var Tabs = (0, import_createTabs.createTabs)({
34
+ ContentFrame: import_Tabs.DefaultTabsContentFrame,
35
+ TabFrame: import_Tabs.DefaultTabsTabFrame,
36
+ TabsFrame: import_Tabs.DefaultTabsFrame
37
+ });
38
+ //# sourceMappingURL=index.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA,YAAS;;AACT,IAAAA,SAAS,GAAAC,MAAA,CAAAC,cAAyB;AAClC,IAAAC,gBAAc,GAAAF,MAAA,CAAAG,wBAAA;AACd,IAAAC,iBAAc,GAAAJ,MAAA,CAAAK,mBAAA;AAEP,IAAAC,YAAa,GAAAN,MAAW,CAAAO,SAAA,CAAAC,cAAA;AAAA,IAC7BC,QAAA,GAAAA,CAAcC,MAAA,EAAAC,GAAA;IACd,SAAAC,IAAU,IAAAD,GAAA,EACVZ,SAAW,CAAAW,MAAA,EAAAE,IAAA;MAAAC,GAAA,EAAAF,GAAA,CAAAC,IAAA;MAAAE,UAAA;IAAA;EACb,CAAC;EAAAC,WAAA,GAAAA,CAAAC,EAAA,EAAAC,IAAA,EAAAC,MAAA,EAAAC,IAAA","ignoreList":[]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@hanzogui/tabs",
3
+ "version": "2.0.0",
4
+ "gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
5
+ "source": "src/index.ts",
6
+ "files": [
7
+ "src",
8
+ "types",
9
+ "dist"
10
+ ],
11
+ "type": "module",
12
+ "sideEffects": [
13
+ "*.css"
14
+ ],
15
+ "main": "dist/cjs",
16
+ "module": "dist/esm",
17
+ "types": "./types/index.d.ts",
18
+ "exports": {
19
+ "./package.json": "./package.json",
20
+ ".": {
21
+ "types": "./types/index.d.ts",
22
+ "react-native": "./dist/esm/index.native.js",
23
+ "browser": "./dist/esm/index.mjs",
24
+ "module": "./dist/esm/index.mjs",
25
+ "import": "./dist/esm/index.mjs",
26
+ "require": "./dist/cjs/index.cjs",
27
+ "default": "./dist/esm/index.mjs"
28
+ }
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "build": "hanzo-gui-build",
35
+ "watch": "hanzo-gui-build --watch",
36
+ "clean": "hanzo-gui-build clean",
37
+ "clean:build": "hanzo-gui-build clean:build"
38
+ },
39
+ "dependencies": {
40
+ "@hanzogui/compose-refs": "2.0.0-rc.29",
41
+ "@hanzogui/constants": "2.0.0-rc.29",
42
+ "@hanzogui/core": "2.0.0-rc.29",
43
+ "@hanzogui/create-context": "2.0.0-rc.29",
44
+ "@hanzogui/element": "2.0.0-rc.29",
45
+ "@hanzogui/get-button-sized": "2.0.0-rc.29",
46
+ "@hanzogui/group": "2.0.0",
47
+ "@hanzogui/helpers": "2.0.0-rc.29",
48
+ "@hanzogui/roving-focus": "2.0.0",
49
+ "@hanzogui/sizable-context": "2.0.0-rc.29",
50
+ "@hanzogui/stacks": "2.0.0",
51
+ "@hanzogui/use-controllable-state": "2.0.0-rc.29",
52
+ "@hanzogui/use-direction": "2.0.0",
53
+ "@hanzogui/web": "2.0.0-rc.29"
54
+ },
55
+ "devDependencies": {
56
+ "@hanzogui/build": "2.0.0-rc.29",
57
+ "react": ">=19",
58
+ "react-native": "0.83.2"
59
+ },
60
+ "peerDependencies": {
61
+ "react": ">=19",
62
+ "react-native": "*"
63
+ },
64
+ "module:jsx": "dist/jsx"
65
+ }
@@ -0,0 +1,19 @@
1
+ import type { SizeTokens } from '@hanzogui/core'
2
+ import { createStyledContext } from '@hanzogui/core'
3
+ import type { TabsProps } from './createTabs'
4
+
5
+ export type TabsContextValue = {
6
+ baseId: string
7
+ value?: string
8
+ onChange: (value: string) => void
9
+ orientation?: TabsProps['orientation']
10
+ dir?: TabsProps['dir']
11
+ activationMode?: TabsProps['activationMode']
12
+ size: SizeTokens
13
+ registerTrigger: () => void
14
+ unregisterTrigger: () => void
15
+ triggersCount: number
16
+ }
17
+
18
+ export const { Provider: TabsProvider, useStyledContext: useTabsContext } =
19
+ createStyledContext<TabsContextValue>()
package/src/Tabs.tsx ADDED
@@ -0,0 +1,69 @@
1
+ import { styled, View } from '@hanzogui/core'
2
+ import { getButtonSized } from '@hanzogui/get-button-sized'
3
+ import { SizableStack, ThemeableStack } from '@hanzogui/stacks'
4
+
5
+ const TABS_NAME = 'Tabs'
6
+ export const DefaultTabsFrame = styled(SizableStack, {
7
+ name: TABS_NAME,
8
+ })
9
+
10
+ const TRIGGER_NAME = 'TabsTrigger'
11
+
12
+ export const DefaultTabsTabFrame = styled(
13
+ View,
14
+ {
15
+ name: TRIGGER_NAME,
16
+ role: 'tab',
17
+
18
+ variants: {
19
+ size: {
20
+ '...size': getButtonSized,
21
+ },
22
+
23
+ disabled: {
24
+ true: {
25
+ pointerEvents: 'none',
26
+ },
27
+ },
28
+
29
+ unstyled: {
30
+ false: {
31
+ borderWidth: 0,
32
+ backgroundColor: '$background',
33
+ userSelect: 'none',
34
+ justifyContent: 'center',
35
+ alignItems: 'center',
36
+ flexWrap: 'nowrap',
37
+ flexDirection: 'row',
38
+ cursor: 'pointer',
39
+
40
+ pressStyle: {
41
+ backgroundColor: '$backgroundPress',
42
+ },
43
+
44
+ focusVisibleStyle: {
45
+ outlineColor: '$outlineColor',
46
+ outlineWidth: 2,
47
+ outlineStyle: 'solid',
48
+ zIndex: 10,
49
+ },
50
+ },
51
+ },
52
+ } as const,
53
+
54
+ defaultVariants: {
55
+ unstyled: process.env.HANZO_GUI_HEADLESS === '1',
56
+ },
57
+ },
58
+ {
59
+ accept: {
60
+ activeStyle: 'style',
61
+ } as const,
62
+ }
63
+ )
64
+
65
+ const CONTENT_NAME = 'TabsContent'
66
+
67
+ export const DefaultTabsContentFrame = styled(ThemeableStack, {
68
+ name: CONTENT_NAME,
69
+ })
@@ -0,0 +1,433 @@
1
+ import { composeRefs } from '@hanzogui/compose-refs'
2
+ import { isWeb } from '@hanzogui/constants'
3
+ import type { GroupProps } from '@hanzogui/group'
4
+ import { Group, useGroupItem } from '@hanzogui/group'
5
+ import { composeEventHandlers, withStaticProperties } from '@hanzogui/helpers'
6
+ import { RovingFocusGroup, type RovingFocusGroupProps } from '@hanzogui/roving-focus'
7
+ import { SizableContext } from '@hanzogui/sizable-context'
8
+ import { useControllableState } from '@hanzogui/use-controllable-state'
9
+ import { useDirection } from '@hanzogui/use-direction'
10
+ import type { GetProps, GuiElement, ViewProps } from '@hanzogui/web'
11
+ import { useEvent } from '@hanzogui/web'
12
+ import * as React from 'react'
13
+ import type { LayoutRectangle } from 'react-native'
14
+ import { TabsProvider, useTabsContext } from './StyledContext'
15
+ import { DefaultTabsContentFrame, DefaultTabsFrame, DefaultTabsTabFrame } from './Tabs'
16
+
17
+ type TabsComponent = (props: { direction: 'horizontal' | 'vertical' } & ViewProps) => any
18
+ type TabComponent = (props: { active?: boolean } & ViewProps) => any
19
+ type ContentComponent = (props: ViewProps) => any
20
+
21
+ export function createTabs<
22
+ C extends TabsComponent,
23
+ T extends TabComponent,
24
+ F extends ContentComponent,
25
+ >(createProps: { ContentFrame: F; TabFrame: T; TabsFrame: C }) {
26
+ const {
27
+ ContentFrame = DefaultTabsContentFrame,
28
+ TabFrame = DefaultTabsTabFrame,
29
+ TabsFrame = DefaultTabsFrame,
30
+ } = createProps as unknown as {
31
+ ContentFrame: typeof DefaultTabsContentFrame
32
+ TabFrame: typeof DefaultTabsTabFrame
33
+ TabsFrame: typeof DefaultTabsFrame
34
+ }
35
+
36
+ const TABS_CONTEXT = 'TabsContext'
37
+
38
+ const TAB_LIST_NAME = 'TabsList'
39
+
40
+ const TabsList = React.forwardRef<GuiElement, TabsListProps>(
41
+ (props: ScopedProps<TabsListProps>, forwardedRef) => {
42
+ const { __scopeTabs, loop = true, children, ...listProps } = props
43
+ const context = useTabsContext(__scopeTabs)
44
+
45
+ return (
46
+ <RovingFocusGroup
47
+ __scopeRovingFocusGroup={__scopeTabs || TABS_CONTEXT}
48
+ orientation={context.orientation}
49
+ dir={context.dir}
50
+ loop={loop}
51
+ asChild
52
+ >
53
+ <Group
54
+ role="tablist"
55
+ componentName={TAB_LIST_NAME}
56
+ aria-orientation={context.orientation}
57
+ ref={forwardedRef}
58
+ orientation={context.orientation}
59
+ {...listProps}
60
+ >
61
+ {children}
62
+ </Group>
63
+ </RovingFocusGroup>
64
+ )
65
+ }
66
+ )
67
+
68
+ TabsList.displayName = TAB_LIST_NAME
69
+
70
+ /* -------------------------------------------------------------------------------------------------
71
+ * TabsTrigger
72
+ * -----------------------------------------------------------------------------------------------*/
73
+
74
+ const TRIGGER_NAME = 'TabsTrigger'
75
+
76
+ /**
77
+ * @deprecated Use `TabLayout` instead
78
+ */
79
+
80
+ const TabsTrigger = TabFrame.styleable<ScopedProps<TabsTabProps>>(
81
+ (props, forwardedRef) => {
82
+ const {
83
+ __scopeTabs,
84
+ value,
85
+ disabled = false,
86
+ onInteraction,
87
+ activeStyle,
88
+ activeTheme,
89
+ unstyled = false,
90
+ ...triggerProps
91
+ } = props
92
+ const context = useTabsContext(__scopeTabs)
93
+ const triggerId = makeTriggerId(context.baseId, value)
94
+ const contentId = makeContentId(context.baseId, value)
95
+ const isSelected = value === context.value
96
+ const [layout, setLayout] = React.useState<TabLayout | null>(null)
97
+ const triggerRef = React.useRef<GuiElement>(null)
98
+ const groupItemProps = useGroupItem({ disabled: !!disabled })
99
+
100
+ React.useEffect(() => {
101
+ context.registerTrigger()
102
+ return () => context.unregisterTrigger()
103
+ }, [])
104
+
105
+ React.useEffect(() => {
106
+ if (!triggerRef.current || !isWeb) return
107
+
108
+ const el = triggerRef.current as unknown as HTMLElement
109
+
110
+ function getTriggerSize() {
111
+ if (!el) return
112
+ setLayout({
113
+ width: el.offsetWidth,
114
+ height: el.offsetHeight,
115
+ x: el.offsetLeft,
116
+ y: el.offsetTop,
117
+ })
118
+ }
119
+ getTriggerSize()
120
+
121
+ const observer = new ResizeObserver(getTriggerSize)
122
+ observer.observe(el)
123
+
124
+ return () => {
125
+ observer.unobserve(el)
126
+ }
127
+ }, [context.triggersCount])
128
+
129
+ React.useEffect(() => {
130
+ if (isSelected && layout) {
131
+ onInteraction?.('select', layout)
132
+ }
133
+ }, [isSelected, value, layout])
134
+
135
+ return (
136
+ <RovingFocusGroup.Item
137
+ __scopeRovingFocusGroup={__scopeTabs || TABS_CONTEXT}
138
+ asChild
139
+ focusable={!disabled}
140
+ active={isSelected}
141
+ >
142
+ <TabFrame
143
+ onMouseEnter={composeEventHandlers(props.onMouseEnter, () => {
144
+ if (layout) {
145
+ onInteraction?.('hover', layout)
146
+ }
147
+ })}
148
+ onMouseLeave={composeEventHandlers(props.onMouseLeave, () => {
149
+ onInteraction?.('hover', null)
150
+ })}
151
+ role="tab"
152
+ aria-selected={isSelected}
153
+ aria-controls={contentId}
154
+ data-state={isSelected ? 'active' : 'inactive'}
155
+ data-disabled={disabled ? '' : undefined}
156
+ id={triggerId}
157
+ theme={activeTheme ?? null}
158
+ unstyled={unstyled}
159
+ {...(!unstyled && {
160
+ size: context.size,
161
+ })}
162
+ {...(isSelected && {
163
+ ...(!unstyled &&
164
+ !activeStyle && {
165
+ backgroundColor: '$backgroundActive',
166
+ }),
167
+ ...(activeStyle as object),
168
+ })}
169
+ {...groupItemProps}
170
+ disabled={disabled ?? groupItemProps.disabled}
171
+ {...triggerProps}
172
+ ref={composeRefs(forwardedRef, triggerRef)}
173
+ onPress={composeEventHandlers(props.onPress ?? undefined, (event) => {
174
+ // only call handler if it's the left button (mousedown gets triggered by all mouse buttons)
175
+ // but not when the control key is pressed (avoiding MacOS right click)
176
+
177
+ const webChecks =
178
+ !isWeb ||
179
+ ((event as unknown as React.MouseEvent).button === 0 &&
180
+ (event as unknown as React.MouseEvent).ctrlKey === false)
181
+ if (!disabled && !isSelected && webChecks) {
182
+ context.onChange(value)
183
+ }
184
+ })}
185
+ {...(isWeb && {
186
+ onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
187
+ if ([' ', 'Enter'].includes(event.key)) {
188
+ context.onChange(value)
189
+ event.preventDefault()
190
+ }
191
+ }),
192
+ onFocus: composeEventHandlers(props.onFocus, (event) => {
193
+ if (layout) {
194
+ onInteraction?.('focus', layout)
195
+ }
196
+ // handle "automatic" activation if necessary
197
+ // ie. activate tab following focus
198
+ const isAutomaticActivation = context.activationMode !== 'manual'
199
+ if (!isSelected && !disabled && isAutomaticActivation) {
200
+ context.onChange(value)
201
+ }
202
+ }),
203
+ onBlur: composeEventHandlers(props.onBlur, () => {
204
+ onInteraction?.('focus', null)
205
+ }),
206
+ })}
207
+ />
208
+ </RovingFocusGroup.Item>
209
+ )
210
+ }
211
+ )
212
+
213
+ TabsTrigger.displayName = TRIGGER_NAME
214
+
215
+ /* -------------------------------------------------------------------------------------------------
216
+ * TabsContent
217
+ * -----------------------------------------------------------------------------------------------*/
218
+
219
+ const TabsContent = ContentFrame.styleable<TabsContentExtraProps>(function TabsContent(
220
+ props: ScopedProps<TabsContentProps>,
221
+ forwardedRef
222
+ ) {
223
+ const { __scopeTabs, value, forceMount, children, ...contentProps } = props
224
+ const context = useTabsContext(__scopeTabs)
225
+ const isSelected = value === context.value
226
+ const show = forceMount || isSelected
227
+
228
+ const triggerId = makeTriggerId(context.baseId, value)
229
+ const contentId = makeContentId(context.baseId, value)
230
+
231
+ if (!show) {
232
+ return null
233
+ }
234
+
235
+ return (
236
+ <ContentFrame
237
+ key={value}
238
+ data-state={isSelected ? 'active' : 'inactive'}
239
+ data-orientation={context.orientation}
240
+ role="tabpanel"
241
+ aria-labelledby={triggerId}
242
+ // @ts-ignore
243
+ hidden={!show}
244
+ id={contentId}
245
+ tabIndex={0}
246
+ {...contentProps}
247
+ ref={forwardedRef}
248
+ >
249
+ {children}
250
+ </ContentFrame>
251
+ )
252
+ })
253
+
254
+ /* -------------------------------------------------------------------------------------------------
255
+ * Tabs
256
+ * -----------------------------------------------------------------------------------------------*/
257
+
258
+ type ScopedProps<P> = P & { __scopeTabs?: string }
259
+ // const [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [
260
+ // createRovingFocusGroupScope,
261
+ // ])
262
+ // const useRovingFocusGroupScope = createRovingFocusGroupScope()
263
+
264
+ type RovingFocusGroupProps = React.ComponentPropsWithoutRef<typeof RovingFocusGroup>
265
+
266
+ const TabsComponent = TabsFrame.styleable<TabsExtraProps>(function Tabs(
267
+ props: ScopedProps<TabsProps>,
268
+ forwardedRef
269
+ ) {
270
+ const {
271
+ __scopeTabs,
272
+ value: valueProp,
273
+ onValueChange,
274
+ defaultValue,
275
+ orientation = 'horizontal',
276
+ dir,
277
+ activationMode = 'manual',
278
+ size = '$true',
279
+ ...tabsProps
280
+ } = props
281
+ const direction = useDirection(dir)
282
+ const [value, setValue] = useControllableState({
283
+ prop: valueProp,
284
+ onChange: onValueChange,
285
+ defaultProp: defaultValue ?? '',
286
+ })
287
+ const [triggersCount, setTriggersCount] = React.useState(0)
288
+ const registerTrigger = useEvent(() => setTriggersCount((v) => v + 1))
289
+ const unregisterTrigger = useEvent(() => setTriggersCount((v) => v - 1))
290
+
291
+ return (
292
+ <SizableContext.Provider size={size}>
293
+ <TabsProvider
294
+ scope={__scopeTabs}
295
+ baseId={React.useId()}
296
+ value={value}
297
+ onChange={setValue}
298
+ orientation={orientation}
299
+ dir={direction}
300
+ activationMode={activationMode}
301
+ size={size}
302
+ registerTrigger={registerTrigger}
303
+ triggersCount={triggersCount}
304
+ unregisterTrigger={unregisterTrigger}
305
+ >
306
+ <TabsFrame
307
+ direction={direction}
308
+ data-orientation={orientation}
309
+ {...tabsProps}
310
+ ref={forwardedRef}
311
+ />
312
+ </TabsProvider>
313
+ </SizableContext.Provider>
314
+ )
315
+ })
316
+
317
+ // make it so it can accept a generic
318
+ // this broke things outside our repo, but not sure why, all non style props were missing
319
+ // like onPress etc
320
+ // as <Tab = string>(
321
+ // props: TabsProps<Tab> & { ref?: React.Ref<GuiElement> }
322
+ // ) => React.JSX.Element
323
+
324
+ return withStaticProperties(TabsComponent, {
325
+ List: TabsList,
326
+ /**
327
+ * @deprecated Use Tabs.Tab instead
328
+ */
329
+ Trigger: TabsTrigger,
330
+ Tab: TabsTrigger,
331
+ Content: TabsContent,
332
+ })
333
+ }
334
+ /* ---------------------------------------------------------------------------------------------- */
335
+
336
+ function makeTriggerId(baseId: string, value: string) {
337
+ return `${baseId}-trigger-${value}`
338
+ }
339
+
340
+ function makeContentId(baseId: string, value: string) {
341
+ return `${baseId}-content-${value}`
342
+ }
343
+
344
+ // Types
345
+ type TabsFrameProps = GetProps<typeof DefaultTabsFrame>
346
+
347
+ type TabsExtraProps<Tab = string> = {
348
+ /** The value for the selected tab, if controlled */
349
+ value?: string
350
+ /** The value of the tab to select by default, if uncontrolled */
351
+ defaultValue?: Tab
352
+ /** A function called when a new tab is selected */
353
+ onValueChange?: (value: Tab) => void
354
+ /**
355
+ * The orientation the tabs are layed out.
356
+ * Mainly so arrow navigation is done accordingly (left & right vs. up & down)
357
+ * @defaultValue horizontal
358
+ */
359
+ orientation?: RovingFocusGroupProps['orientation']
360
+ /**
361
+ * The direction of navigation between toolbar items.
362
+ */
363
+ dir?: RovingFocusGroupProps['dir']
364
+ /**
365
+ * Whether a tab is activated automatically or manually. Only supported in web.
366
+ * @defaultValue automatic
367
+ * */
368
+ activationMode?: 'automatic' | 'manual'
369
+ }
370
+
371
+ type TabsProps<Tab = string> = TabsFrameProps & TabsExtraProps<Tab>
372
+
373
+ type TabsListFrameProps = GroupProps
374
+
375
+ type TabsListProps = TabsListFrameProps & {
376
+ /**
377
+ * Whether to loop over after reaching the end or start of the items
378
+ * @default true
379
+ */
380
+ loop?: boolean
381
+ }
382
+
383
+ type InteractionType = 'select' | 'focus' | 'hover'
384
+
385
+ type TabLayout = LayoutRectangle
386
+
387
+ type TabsTriggerFrameProps = GetProps<typeof DefaultTabsTabFrame>
388
+
389
+ /**
390
+ * @deprecated use `TabTabsProps` instead
391
+ */
392
+ type TabsTriggerProps = TabsTriggerFrameProps & {
393
+ /** The value for the tabs state to be changed to after activation of the trigger */
394
+ value: string
395
+
396
+ /** Used for making custom indicators when trigger interacted with */
397
+ onInteraction?: (type: InteractionType, layout: TabLayout | null) => void
398
+
399
+ /** Custom styles to apply when tab is active */
400
+ activeStyle?: TabsTriggerFrameProps
401
+
402
+ /** Theme to apply when tab is active (use null for no theme) */
403
+ activeTheme?: string | null
404
+ }
405
+
406
+ type TabsTabProps = TabsTriggerProps
407
+
408
+ type TabsTriggerLayout = LayoutRectangle
409
+
410
+ type TabsContentFrameProps = GetProps<typeof DefaultTabsContentFrame>
411
+
412
+ type TabsContentExtraProps = {
413
+ /** Will show the content when the value matches the state of Tabs root */
414
+ value: string
415
+
416
+ /**
417
+ * Used to force mounting when more control is needed. Useful when
418
+ * controlling animation with Gui animations.
419
+ */
420
+ forceMount?: boolean
421
+ }
422
+
423
+ type TabsContentProps = TabsContentFrameProps & TabsContentExtraProps
424
+
425
+ export type {
426
+ TabLayout,
427
+ TabsContentProps,
428
+ TabsListProps,
429
+ TabsProps,
430
+ TabsTabProps,
431
+ TabsTriggerLayout,
432
+ TabsTriggerProps,
433
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { createTabs } from './createTabs'
2
+ import { DefaultTabsContentFrame, DefaultTabsFrame, DefaultTabsTabFrame } from './Tabs'
3
+ export * from './createTabs'
4
+ export * from './StyledContext'
5
+
6
+ export const Tabs = createTabs({
7
+ ContentFrame: DefaultTabsContentFrame,
8
+ TabFrame: DefaultTabsTabFrame,
9
+ TabsFrame: DefaultTabsFrame,
10
+ })
@@ -0,0 +1,19 @@
1
+ import type { SizeTokens } from '@gui/core';
2
+ import type { TabsProps } from './createTabs';
3
+ export type TabsContextValue = {
4
+ baseId: string;
5
+ value?: string;
6
+ onChange: (value: string) => void;
7
+ orientation?: TabsProps['orientation'];
8
+ dir?: TabsProps['dir'];
9
+ activationMode?: TabsProps['activationMode'];
10
+ size: SizeTokens;
11
+ registerTrigger: () => void;
12
+ unregisterTrigger: () => void;
13
+ triggersCount: number;
14
+ };
15
+ export declare const TabsProvider: import("react").Provider<TabsContextValue> & import("react").ProviderExoticComponent<Partial<TabsContextValue> & {
16
+ children?: import("react").ReactNode;
17
+ scope?: string;
18
+ }>, useTabsContext: (scope?: string) => TabsContextValue;
19
+ //# sourceMappingURL=StyledContext.d.ts.map