@tamagui/tabs 2.7.7 → 3.0.0-beta.643.1

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 (67) hide show
  1. package/dist/cjs/StyledContext.cjs +16 -20
  2. package/dist/cjs/StyledContext.native.cjs +31 -0
  3. package/dist/cjs/StyledContext.native.js +17 -20
  4. package/dist/cjs/StyledContext.native.js.map +1 -1
  5. package/dist/cjs/Tabs.cjs +227 -68
  6. package/dist/cjs/Tabs.native.cjs +250 -0
  7. package/dist/cjs/Tabs.native.js +233 -67
  8. package/dist/cjs/Tabs.native.js.map +1 -1
  9. package/dist/cjs/index.cjs +10 -27
  10. package/dist/cjs/index.native.cjs +22 -0
  11. package/dist/cjs/index.native.js +11 -27
  12. package/dist/cjs/index.native.js.map +1 -1
  13. package/dist/esm/StyledContext.mjs +3 -4
  14. package/dist/esm/StyledContext.mjs.map +1 -1
  15. package/dist/esm/StyledContext.native.js +3 -4
  16. package/dist/esm/StyledContext.native.js.map +1 -1
  17. package/dist/esm/Tabs.mjs +206 -55
  18. package/dist/esm/Tabs.mjs.map +1 -1
  19. package/dist/esm/Tabs.native.js +212 -55
  20. package/dist/esm/Tabs.native.js.map +1 -1
  21. package/dist/esm/index.js +3 -11
  22. package/dist/esm/index.mjs +3 -11
  23. package/dist/esm/index.native.js +3 -11
  24. package/dist/jsx/StyledContext.mjs +3 -4
  25. package/dist/jsx/StyledContext.mjs.map +1 -1
  26. package/dist/jsx/StyledContext.native.cjs +31 -0
  27. package/dist/jsx/StyledContext.native.js +17 -20
  28. package/dist/jsx/StyledContext.native.js.map +1 -1
  29. package/dist/jsx/Tabs.mjs +206 -55
  30. package/dist/jsx/Tabs.mjs.map +1 -1
  31. package/dist/jsx/Tabs.native.cjs +250 -0
  32. package/dist/jsx/Tabs.native.js +233 -67
  33. package/dist/jsx/Tabs.native.js.map +1 -1
  34. package/dist/jsx/index.js +3 -11
  35. package/dist/jsx/index.mjs +3 -11
  36. package/dist/jsx/index.native.cjs +22 -0
  37. package/dist/jsx/index.native.js +11 -27
  38. package/dist/jsx/index.native.js.map +1 -1
  39. package/package.json +11 -17
  40. package/src/StyledContext.tsx +3 -3
  41. package/src/Tabs.tsx +368 -55
  42. package/src/index.ts +1 -9
  43. package/types/StyledContext.d.ts +3 -3
  44. package/types/StyledContext.d.ts.map +1 -1
  45. package/types/Tabs.d.ts +398 -35
  46. package/types/Tabs.d.ts.map +1 -1
  47. package/types/index.d.ts +1 -537
  48. package/types/index.d.ts.map +1 -1
  49. package/dist/cjs/createTabs.cjs +0 -293
  50. package/dist/cjs/createTabs.native.js +0 -308
  51. package/dist/cjs/createTabs.native.js.map +0 -1
  52. package/dist/esm/createTabs.mjs +0 -257
  53. package/dist/esm/createTabs.mjs.map +0 -1
  54. package/dist/esm/createTabs.native.js +0 -269
  55. package/dist/esm/createTabs.native.js.map +0 -1
  56. package/dist/esm/index.js.map +0 -1
  57. package/dist/esm/index.mjs.map +0 -1
  58. package/dist/esm/index.native.js.map +0 -1
  59. package/dist/jsx/createTabs.mjs +0 -257
  60. package/dist/jsx/createTabs.mjs.map +0 -1
  61. package/dist/jsx/createTabs.native.js +0 -308
  62. package/dist/jsx/createTabs.native.js.map +0 -1
  63. package/dist/jsx/index.js.map +0 -1
  64. package/dist/jsx/index.mjs.map +0 -1
  65. package/src/createTabs.tsx +0 -438
  66. package/types/createTabs.d.ts +0 -527
  67. package/types/createTabs.d.ts.map +0 -1
@@ -1,438 +0,0 @@
1
- import { composeRefs } from '@tamagui/compose-refs'
2
- import { isWeb } from '@tamagui/constants'
3
- import type { GroupProps } from '@tamagui/group'
4
- import { Group, useGroupItem } from '@tamagui/group'
5
- import { composeEventHandlers, withStaticProperties } from '@tamagui/helpers'
6
- import { RovingFocusGroup, type RovingFocusGroupProps } from '@tamagui/roving-focus'
7
- import { SizableContext } from '@tamagui/sizable-context'
8
- import { useControllableState } from '@tamagui/use-controllable-state'
9
- import { useDirection } from '@tamagui/use-direction'
10
- import type { GetProps, TamaguiElement, ViewProps } from '@tamagui/web'
11
- import { useEvent } from '@tamagui/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<TamaguiElement, 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<TamaguiElement>(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.disconnect()
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
- {...(!isWeb && {
144
- onLayout: (event) => {
145
- setLayout(event.nativeEvent.layout)
146
- },
147
- })}
148
- onMouseEnter={composeEventHandlers(props.onMouseEnter, () => {
149
- if (layout) {
150
- onInteraction?.('hover', layout)
151
- }
152
- })}
153
- onMouseLeave={composeEventHandlers(props.onMouseLeave, () => {
154
- onInteraction?.('hover', null)
155
- })}
156
- role="tab"
157
- aria-selected={isSelected}
158
- aria-controls={contentId}
159
- data-state={isSelected ? 'active' : 'inactive'}
160
- data-disabled={disabled ? '' : undefined}
161
- id={triggerId}
162
- theme={activeTheme ?? null}
163
- unstyled={unstyled}
164
- {...(!unstyled && {
165
- size: context.size,
166
- })}
167
- {...(isSelected && {
168
- ...(!unstyled &&
169
- !activeStyle && {
170
- backgroundColor: '$backgroundActive',
171
- }),
172
- ...(activeStyle as object),
173
- })}
174
- {...groupItemProps}
175
- disabled={disabled ?? groupItemProps.disabled}
176
- {...triggerProps}
177
- ref={composeRefs(forwardedRef, triggerRef)}
178
- onPress={composeEventHandlers(props.onPress ?? undefined, (event) => {
179
- // only call handler if it's the left button (mousedown gets triggered by all mouse buttons)
180
- // but not when the control key is pressed (avoiding MacOS right click)
181
-
182
- const webChecks =
183
- !isWeb ||
184
- ((event as unknown as React.MouseEvent).button === 0 &&
185
- (event as unknown as React.MouseEvent).ctrlKey === false)
186
- if (!disabled && !isSelected && webChecks) {
187
- context.onChange(value)
188
- }
189
- })}
190
- {...(isWeb && {
191
- onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
192
- if ([' ', 'Enter'].includes(event.key)) {
193
- context.onChange(value)
194
- event.preventDefault()
195
- }
196
- }),
197
- onFocus: composeEventHandlers(props.onFocus, (event) => {
198
- if (layout) {
199
- onInteraction?.('focus', layout)
200
- }
201
- // handle "automatic" activation if necessary
202
- // ie. activate tab following focus
203
- const isAutomaticActivation = context.activationMode !== 'manual'
204
- if (!isSelected && !disabled && isAutomaticActivation) {
205
- context.onChange(value)
206
- }
207
- }),
208
- onBlur: composeEventHandlers(props.onBlur, () => {
209
- onInteraction?.('focus', null)
210
- }),
211
- })}
212
- />
213
- </RovingFocusGroup.Item>
214
- )
215
- }
216
- )
217
-
218
- TabsTrigger.displayName = TRIGGER_NAME
219
-
220
- /* -------------------------------------------------------------------------------------------------
221
- * TabsContent
222
- * -----------------------------------------------------------------------------------------------*/
223
-
224
- const TabsContent = ContentFrame.styleable<TabsContentExtraProps>(function TabsContent(
225
- props: ScopedProps<TabsContentProps>,
226
- forwardedRef
227
- ) {
228
- const { __scopeTabs, value, forceMount, children, ...contentProps } = props
229
- const context = useTabsContext(__scopeTabs)
230
- const isSelected = value === context.value
231
- const show = forceMount || isSelected
232
-
233
- const triggerId = makeTriggerId(context.baseId, value)
234
- const contentId = makeContentId(context.baseId, value)
235
-
236
- if (!show) {
237
- return null
238
- }
239
-
240
- return (
241
- <ContentFrame
242
- key={value}
243
- data-state={isSelected ? 'active' : 'inactive'}
244
- data-orientation={context.orientation}
245
- role="tabpanel"
246
- aria-labelledby={triggerId}
247
- // @ts-ignore
248
- hidden={!show}
249
- id={contentId}
250
- tabIndex={0}
251
- {...contentProps}
252
- ref={forwardedRef}
253
- >
254
- {children}
255
- </ContentFrame>
256
- )
257
- })
258
-
259
- /* -------------------------------------------------------------------------------------------------
260
- * Tabs
261
- * -----------------------------------------------------------------------------------------------*/
262
-
263
- type ScopedProps<P> = P & { __scopeTabs?: string }
264
- // const [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [
265
- // createRovingFocusGroupScope,
266
- // ])
267
- // const useRovingFocusGroupScope = createRovingFocusGroupScope()
268
-
269
- type RovingFocusGroupProps = React.ComponentPropsWithoutRef<typeof RovingFocusGroup>
270
-
271
- const TabsComponent = TabsFrame.styleable<TabsExtraProps>(function Tabs(
272
- props: ScopedProps<TabsProps>,
273
- forwardedRef
274
- ) {
275
- const {
276
- __scopeTabs,
277
- value: valueProp,
278
- onValueChange,
279
- defaultValue,
280
- orientation = 'horizontal',
281
- dir,
282
- activationMode = 'manual',
283
- size = '$true',
284
- ...tabsProps
285
- } = props
286
- const direction = useDirection(dir)
287
- const [value, setValue] = useControllableState({
288
- prop: valueProp,
289
- onChange: onValueChange,
290
- defaultProp: defaultValue ?? '',
291
- })
292
- const [triggersCount, setTriggersCount] = React.useState(0)
293
- const registerTrigger = useEvent(() => setTriggersCount((v) => v + 1))
294
- const unregisterTrigger = useEvent(() => setTriggersCount((v) => v - 1))
295
-
296
- return (
297
- <SizableContext.Provider size={size}>
298
- <TabsProvider
299
- scope={__scopeTabs}
300
- baseId={React.useId()}
301
- value={value}
302
- onChange={setValue}
303
- orientation={orientation}
304
- dir={direction}
305
- activationMode={activationMode}
306
- size={size}
307
- registerTrigger={registerTrigger}
308
- triggersCount={triggersCount}
309
- unregisterTrigger={unregisterTrigger}
310
- >
311
- <TabsFrame
312
- direction={direction}
313
- data-orientation={orientation}
314
- {...tabsProps}
315
- ref={forwardedRef}
316
- />
317
- </TabsProvider>
318
- </SizableContext.Provider>
319
- )
320
- })
321
-
322
- // make it so it can accept a generic
323
- // this broke things outside our repo, but not sure why, all non style props were missing
324
- // like onPress etc
325
- // as <Tab = string>(
326
- // props: TabsProps<Tab> & { ref?: React.Ref<TamaguiElement> }
327
- // ) => React.JSX.Element
328
-
329
- return withStaticProperties(TabsComponent, {
330
- List: TabsList,
331
- /**
332
- * @deprecated Use Tabs.Tab instead
333
- */
334
- Trigger: TabsTrigger,
335
- Tab: TabsTrigger,
336
- Content: TabsContent,
337
- })
338
- }
339
- /* ---------------------------------------------------------------------------------------------- */
340
-
341
- function makeTriggerId(baseId: string, value: string) {
342
- return `${baseId}-trigger-${value}`
343
- }
344
-
345
- function makeContentId(baseId: string, value: string) {
346
- return `${baseId}-content-${value}`
347
- }
348
-
349
- // Types
350
- type TabsFrameProps = GetProps<typeof DefaultTabsFrame>
351
-
352
- type TabsExtraProps<Tab = string> = {
353
- /** The value for the selected tab, if controlled */
354
- value?: string
355
- /** The value of the tab to select by default, if uncontrolled */
356
- defaultValue?: Tab
357
- /** A function called when a new tab is selected */
358
- onValueChange?: (value: Tab) => void
359
- /**
360
- * The orientation the tabs are layed out.
361
- * Mainly so arrow navigation is done accordingly (left & right vs. up & down)
362
- * @defaultValue horizontal
363
- */
364
- orientation?: RovingFocusGroupProps['orientation']
365
- /**
366
- * The direction of navigation between toolbar items.
367
- */
368
- dir?: RovingFocusGroupProps['dir']
369
- /**
370
- * Whether a tab is activated automatically or manually. Only supported in web.
371
- * @defaultValue automatic
372
- * */
373
- activationMode?: 'automatic' | 'manual'
374
- }
375
-
376
- type TabsProps<Tab = string> = TabsFrameProps & TabsExtraProps<Tab>
377
-
378
- type TabsListFrameProps = GroupProps
379
-
380
- type TabsListProps = TabsListFrameProps & {
381
- /**
382
- * Whether to loop over after reaching the end or start of the items
383
- * @default true
384
- */
385
- loop?: boolean
386
- }
387
-
388
- type InteractionType = 'select' | 'focus' | 'hover'
389
-
390
- type TabLayout = LayoutRectangle
391
-
392
- type TabsTriggerFrameProps = GetProps<typeof DefaultTabsTabFrame>
393
-
394
- /**
395
- * @deprecated use `TabTabsProps` instead
396
- */
397
- type TabsTriggerProps = TabsTriggerFrameProps & {
398
- /** The value for the tabs state to be changed to after activation of the trigger */
399
- value: string
400
-
401
- /** Used for making custom indicators when trigger interacted with */
402
- onInteraction?: (type: InteractionType, layout: TabLayout | null) => void
403
-
404
- /** Custom styles to apply when tab is active */
405
- activeStyle?: TabsTriggerFrameProps
406
-
407
- /** Theme to apply when tab is active (use null for no theme) */
408
- activeTheme?: string | null
409
- }
410
-
411
- type TabsTabProps = TabsTriggerProps
412
-
413
- type TabsTriggerLayout = LayoutRectangle
414
-
415
- type TabsContentFrameProps = GetProps<typeof DefaultTabsContentFrame>
416
-
417
- type TabsContentExtraProps = {
418
- /** Will show the content when the value matches the state of Tabs root */
419
- value: string
420
-
421
- /**
422
- * Used to force mounting when more control is needed. Useful when
423
- * controlling animation with Tamagui animations.
424
- */
425
- forceMount?: boolean
426
- }
427
-
428
- type TabsContentProps = TabsContentFrameProps & TabsContentExtraProps
429
-
430
- export type {
431
- TabLayout,
432
- TabsContentProps,
433
- TabsListProps,
434
- TabsProps,
435
- TabsTabProps,
436
- TabsTriggerLayout,
437
- TabsTriggerProps,
438
- }