@ankhorage/surface 5.0.0 → 5.0.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 (141) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -2
  3. package/dist/{components/modal → features/modal/adapters/inbound}/Modal.d.ts +2 -1
  4. package/dist/features/modal/adapters/inbound/Modal.d.ts.map +1 -0
  5. package/dist/features/modal/adapters/inbound/Modal.js +51 -0
  6. package/dist/features/modal/adapters/inbound/Modal.js.map +1 -0
  7. package/dist/features/modal/public.d.ts +3 -0
  8. package/dist/features/modal/public.d.ts.map +1 -0
  9. package/dist/features/modal/public.js +2 -0
  10. package/dist/features/modal/public.js.map +1 -0
  11. package/dist/features/tabs/adapters/inbound/Tab.d.ts +5 -0
  12. package/dist/features/tabs/adapters/inbound/Tab.d.ts.map +1 -0
  13. package/dist/features/tabs/adapters/inbound/Tab.js +33 -0
  14. package/dist/features/tabs/adapters/inbound/Tab.js.map +1 -0
  15. package/dist/features/tabs/adapters/inbound/TabList.d.ts +5 -0
  16. package/dist/features/tabs/adapters/inbound/TabList.d.ts.map +1 -0
  17. package/dist/features/tabs/adapters/inbound/TabList.js +42 -0
  18. package/dist/features/tabs/adapters/inbound/TabList.js.map +1 -0
  19. package/dist/features/tabs/adapters/inbound/TabPanel.d.ts +5 -0
  20. package/dist/features/tabs/adapters/inbound/TabPanel.d.ts.map +1 -0
  21. package/dist/features/tabs/adapters/inbound/TabPanel.js +14 -0
  22. package/dist/features/tabs/adapters/inbound/TabPanel.js.map +1 -0
  23. package/dist/features/tabs/adapters/inbound/Tabs.d.ts +5 -0
  24. package/dist/features/tabs/adapters/inbound/Tabs.d.ts.map +1 -0
  25. package/dist/features/tabs/adapters/inbound/Tabs.js +12 -0
  26. package/dist/features/tabs/adapters/inbound/Tabs.js.map +1 -0
  27. package/dist/features/tabs/composition/TabsContext.d.ts +4 -0
  28. package/dist/features/tabs/composition/TabsContext.d.ts.map +1 -0
  29. package/dist/features/tabs/composition/TabsContext.js +3 -0
  30. package/dist/features/tabs/composition/TabsContext.js.map +1 -0
  31. package/dist/features/tabs/composition/useTabRegistration.d.ts +7 -0
  32. package/dist/features/tabs/composition/useTabRegistration.d.ts.map +1 -0
  33. package/dist/features/tabs/composition/useTabRegistration.js +20 -0
  34. package/dist/features/tabs/composition/useTabRegistration.js.map +1 -0
  35. package/dist/features/tabs/composition/useTabsContext.d.ts +4 -0
  36. package/dist/features/tabs/composition/useTabsContext.d.ts.map +1 -0
  37. package/dist/{components/tabs/context.js → features/tabs/composition/useTabsContext.js} +4 -4
  38. package/dist/features/tabs/composition/useTabsContext.js.map +1 -0
  39. package/dist/features/tabs/composition/useTabsController.d.ts +4 -0
  40. package/dist/features/tabs/composition/useTabsController.d.ts.map +1 -0
  41. package/dist/features/tabs/composition/useTabsController.js +43 -0
  42. package/dist/features/tabs/composition/useTabsController.js.map +1 -0
  43. package/dist/features/tabs/public.d.ts +6 -0
  44. package/dist/features/tabs/public.d.ts.map +1 -0
  45. package/dist/features/tabs/public.js +5 -0
  46. package/dist/features/tabs/public.js.map +1 -0
  47. package/dist/features/tabs/utils/resolveNextTabValue.d.ts +4 -0
  48. package/dist/features/tabs/utils/resolveNextTabValue.d.ts.map +1 -0
  49. package/dist/{components/tabs/navigation.js → features/tabs/utils/resolveNextTabValue.js} +10 -13
  50. package/dist/features/tabs/utils/resolveNextTabValue.js.map +1 -0
  51. package/dist/features/tabs/utils/resolveTabElementId.d.ts +3 -0
  52. package/dist/features/tabs/utils/resolveTabElementId.d.ts.map +1 -0
  53. package/dist/features/tabs/utils/resolveTabElementId.js +10 -0
  54. package/dist/features/tabs/utils/resolveTabElementId.js.map +1 -0
  55. package/dist/index.d.ts +4 -4
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +2 -2
  58. package/dist/index.js.map +1 -1
  59. package/dist/{components/modal/types.d.ts → types/modal.d.ts} +2 -2
  60. package/dist/types/modal.d.ts.map +1 -0
  61. package/dist/types/modal.js +2 -0
  62. package/dist/types/modal.js.map +1 -0
  63. package/dist/types/tabs.d.ts +43 -0
  64. package/dist/types/tabs.d.ts.map +1 -0
  65. package/dist/types/tabs.js +2 -0
  66. package/dist/types/tabs.js.map +1 -0
  67. package/package.json +1 -1
  68. package/src/features/modal/adapters/inbound/Modal.tsx +74 -0
  69. package/src/features/modal/public.ts +2 -0
  70. package/src/features/tabs/adapters/inbound/Tab.tsx +67 -0
  71. package/src/features/tabs/adapters/inbound/TabList.tsx +50 -0
  72. package/src/{components/tabs → features/tabs/adapters/inbound}/TabPanel.tsx +6 -10
  73. package/src/features/tabs/adapters/inbound/Tabs.tsx +17 -0
  74. package/src/features/tabs/composition/TabsContext.tsx +5 -0
  75. package/src/features/tabs/composition/useTabRegistration.ts +27 -0
  76. package/src/features/tabs/composition/useTabsContext.ts +13 -0
  77. package/src/features/tabs/composition/useTabsController.ts +55 -0
  78. package/src/features/tabs/public.ts +5 -0
  79. package/src/{components/tabs/navigation.test.ts → features/tabs/utils/resolveNextTabValue.test.ts} +2 -2
  80. package/src/features/tabs/utils/resolveNextTabValue.ts +24 -0
  81. package/src/features/tabs/utils/resolveTabElementId.test.ts +19 -0
  82. package/src/features/tabs/utils/resolveTabElementId.ts +14 -0
  83. package/src/index.test.ts +4 -2
  84. package/src/index.ts +4 -4
  85. package/src/{components/modal/types.ts → types/modal.ts} +1 -1
  86. package/src/types/tabs.ts +51 -0
  87. package/dist/components/modal/Modal.d.ts.map +0 -1
  88. package/dist/components/modal/Modal.js +0 -59
  89. package/dist/components/modal/Modal.js.map +0 -1
  90. package/dist/components/modal/index.d.ts +0 -3
  91. package/dist/components/modal/index.d.ts.map +0 -1
  92. package/dist/components/modal/index.js +0 -2
  93. package/dist/components/modal/index.js.map +0 -1
  94. package/dist/components/modal/types.d.ts.map +0 -1
  95. package/dist/components/modal/types.js +0 -2
  96. package/dist/components/modal/types.js.map +0 -1
  97. package/dist/components/tabs/Tab.d.ts +0 -4
  98. package/dist/components/tabs/Tab.d.ts.map +0 -1
  99. package/dist/components/tabs/Tab.js +0 -48
  100. package/dist/components/tabs/Tab.js.map +0 -1
  101. package/dist/components/tabs/TabList.d.ts +0 -4
  102. package/dist/components/tabs/TabList.d.ts.map +0 -1
  103. package/dist/components/tabs/TabList.js +0 -37
  104. package/dist/components/tabs/TabList.js.map +0 -1
  105. package/dist/components/tabs/TabPanel.d.ts +0 -4
  106. package/dist/components/tabs/TabPanel.d.ts.map +0 -1
  107. package/dist/components/tabs/TabPanel.js +0 -16
  108. package/dist/components/tabs/TabPanel.js.map +0 -1
  109. package/dist/components/tabs/Tabs.d.ts +0 -4
  110. package/dist/components/tabs/Tabs.d.ts.map +0 -1
  111. package/dist/components/tabs/Tabs.js +0 -54
  112. package/dist/components/tabs/Tabs.js.map +0 -1
  113. package/dist/components/tabs/a11y.d.ts +0 -3
  114. package/dist/components/tabs/a11y.d.ts.map +0 -1
  115. package/dist/components/tabs/a11y.js +0 -13
  116. package/dist/components/tabs/a11y.js.map +0 -1
  117. package/dist/components/tabs/context.d.ts +0 -20
  118. package/dist/components/tabs/context.d.ts.map +0 -1
  119. package/dist/components/tabs/context.js.map +0 -1
  120. package/dist/components/tabs/index.d.ts +0 -6
  121. package/dist/components/tabs/index.d.ts.map +0 -1
  122. package/dist/components/tabs/index.js +0 -5
  123. package/dist/components/tabs/index.js.map +0 -1
  124. package/dist/components/tabs/navigation.d.ts +0 -3
  125. package/dist/components/tabs/navigation.d.ts.map +0 -1
  126. package/dist/components/tabs/navigation.js.map +0 -1
  127. package/dist/components/tabs/types.d.ts +0 -26
  128. package/dist/components/tabs/types.d.ts.map +0 -1
  129. package/dist/components/tabs/types.js +0 -2
  130. package/dist/components/tabs/types.js.map +0 -1
  131. package/src/components/modal/Modal.tsx +0 -96
  132. package/src/components/modal/index.ts +0 -2
  133. package/src/components/tabs/Tab.tsx +0 -89
  134. package/src/components/tabs/TabList.tsx +0 -51
  135. package/src/components/tabs/Tabs.tsx +0 -67
  136. package/src/components/tabs/a11y.test.ts +0 -15
  137. package/src/components/tabs/a11y.ts +0 -15
  138. package/src/components/tabs/context.tsx +0 -31
  139. package/src/components/tabs/index.ts +0 -5
  140. package/src/components/tabs/navigation.ts +0 -32
  141. package/src/components/tabs/types.ts +0 -30
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/tabs/types.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { InteractionPolicy } from '../../interactionPolicy';\n\nexport interface TabsProps {\n children?: React.ReactNode;\n value?: string;\n defaultValue?: string;\n onValueChange?: ((value: string) => void) | undefined;\n testID?: string;\n}\n\nexport interface TabListProps {\n children?: React.ReactNode;\n testID?: string;\n}\n\nexport interface TabProps {\n value: string;\n children?: React.ReactNode;\n disabled?: boolean;\n interactionPolicy?: InteractionPolicy;\n testID?: string;\n}\n\nexport interface TabPanelProps {\n value: string;\n children?: React.ReactNode;\n testID?: string;\n}\n"]}
@@ -1,96 +0,0 @@
1
- import React from 'react';
2
- import { Pressable, View } from 'react-native';
3
-
4
- import { Surface } from '../../features/surface/public';
5
- import { FocusScope } from '../../internal/focus/FocusScope';
6
- import { Portal } from '../../internal/overlay/Portal';
7
- import { resolvePointerEvents } from '../../internal/resolvePointerEvents';
8
- import { resolveOverlayAnimation } from '../../internal/resolvers';
9
- import { Center } from '../../layout';
10
- import { useTheme } from '../../theme/ThemeContext';
11
- import type { ModalProps } from './types';
12
-
13
- const boxNonePointerEvents = resolvePointerEvents('box-none');
14
-
15
- export function Modal({
16
- visible,
17
- onDismiss,
18
- children,
19
- closeOnBackdrop = true,
20
- interactionPolicy = 'enabled',
21
- testID,
22
- }: ModalProps) {
23
- const { theme } = useTheme();
24
- const animation = resolveOverlayAnimation('modal');
25
- const passive = interactionPolicy === 'passive';
26
-
27
- if (!visible) {
28
- return null;
29
- }
30
-
31
- return (
32
- <Portal layer="modal" visible={visible}>
33
- <View
34
- {...boxNonePointerEvents.props}
35
- style={[
36
- boxNonePointerEvents.style,
37
- {
38
- bottom: 0,
39
- left: 0,
40
- position: 'absolute',
41
- right: 0,
42
- top: 0,
43
- },
44
- ]}
45
- >
46
- <Pressable
47
- onPress={passive ? undefined : closeOnBackdrop ? onDismiss : undefined}
48
- style={{
49
- backgroundColor: theme.semantics.neutral.text,
50
- bottom: 0,
51
- left: 0,
52
- opacity: animation.backdropOpacity,
53
- position: 'absolute',
54
- right: 0,
55
- top: 0,
56
- }}
57
- testID={testID ? `${testID}-backdrop` : undefined}
58
- />
59
- <FocusScope
60
- active={visible}
61
- onEscape={passive ? undefined : onDismiss}
62
- testID={testID ? `${testID}-focus` : undefined}
63
- >
64
- <Center
65
- p="l"
66
- style={{
67
- flex: 1,
68
- }}
69
- >
70
- <View
71
- accessible
72
- accessibilityViewIsModal
73
- style={{
74
- maxWidth: 560,
75
- width: '100%',
76
- }}
77
- >
78
- <Surface
79
- p="l"
80
- style={{
81
- shadowOpacity: 0.14,
82
- shadowRadius: 18,
83
- shadowOffset: { width: 0, height: 8 },
84
- }}
85
- testID={testID}
86
- variant="raised"
87
- >
88
- {children}
89
- </Surface>
90
- </View>
91
- </Center>
92
- </FocusScope>
93
- </View>
94
- </Portal>
95
- );
96
- }
@@ -1,2 +0,0 @@
1
- export { Modal } from './Modal';
2
- export type { ModalProps } from './types';
@@ -1,89 +0,0 @@
1
- import React from 'react';
2
- import { Pressable } from 'react-native';
3
-
4
- import { Box } from '../../features/layout/public';
5
- import { Text } from '../../features/typography/public';
6
- import { useTheme } from '../../theme/ThemeContext';
7
- import { useTabsContext } from './context';
8
- import type { TabProps } from './types';
9
-
10
- export function Tab({
11
- value,
12
- children,
13
- disabled = false,
14
- interactionPolicy = 'enabled',
15
- testID,
16
- }: TabProps) {
17
- const { theme } = useTheme();
18
- const {
19
- activeValue,
20
- getPanelId,
21
- getTabId,
22
- registerTab,
23
- setActiveValue,
24
- setFocusedValue,
25
- unregisterTab,
26
- } = useTabsContext();
27
- const pressableRef = React.useRef<React.ElementRef<typeof Pressable> | null>(null);
28
- const selected = activeValue === value;
29
- const tabId = getTabId(value);
30
- const panelId = getPanelId(value);
31
- const passive = interactionPolicy === 'passive';
32
-
33
- React.useEffect(() => {
34
- registerTab({
35
- disabled,
36
- focus: () => {
37
- const focusable = pressableRef.current as unknown as {
38
- focus?: (() => void) | undefined;
39
- } | null;
40
- focusable?.focus?.();
41
- },
42
- value,
43
- });
44
-
45
- return () => {
46
- unregisterTab(value);
47
- };
48
- }, [disabled, registerTab, unregisterTab, value]);
49
-
50
- return (
51
- <Pressable
52
- accessibilityLabel={undefined}
53
- accessibilityRole="tab"
54
- accessibilityState={{ disabled, selected }}
55
- nativeID={tabId}
56
- aria-controls={panelId}
57
- disabled={disabled}
58
- onFocus={() => setFocusedValue(value)}
59
- onBlur={() => setFocusedValue(undefined)}
60
- onPress={
61
- passive
62
- ? undefined
63
- : () => {
64
- if (!disabled) {
65
- setActiveValue(value);
66
- }
67
- }
68
- }
69
- ref={pressableRef}
70
- testID={testID}
71
- >
72
- <Box
73
- px="m"
74
- py="s"
75
- style={{
76
- borderBottomColor: selected
77
- ? theme.semantics.action.primary.base
78
- : theme.semantics.border.default,
79
- borderBottomWidth: 2,
80
- opacity: disabled ? 0.64 : 1,
81
- }}
82
- >
83
- <Text color={selected ? 'primary' : undefined} variant="label" weight="medium">
84
- {children}
85
- </Text>
86
- </Box>
87
- </Pressable>
88
- );
89
- }
@@ -1,51 +0,0 @@
1
- import React from 'react';
2
-
3
- import { Box } from '../../features/layout/public';
4
- import { useFocusManager } from '../../internal/focus/useFocusManager';
5
- import { useTabsContext } from './context';
6
- import { resolveNextTabValue } from './navigation';
7
- import type { TabListProps } from './types';
8
-
9
- export function TabList({ children, testID }: TabListProps) {
10
- const { bindKeydown } = useFocusManager();
11
- const { focusedValue, setActiveValue, tabs } = useTabsContext();
12
-
13
- React.useEffect(() => {
14
- if (!focusedValue) {
15
- return undefined;
16
- }
17
-
18
- return bindKeydown((event) => {
19
- const nextValue =
20
- event.key === 'ArrowLeft' ||
21
- event.key === 'ArrowRight' ||
22
- event.key === 'ArrowUp' ||
23
- event.key === 'ArrowDown' ||
24
- event.key === 'Home' ||
25
- event.key === 'End'
26
- ? resolveNextTabValue(tabs, focusedValue, event.key)
27
- : undefined;
28
-
29
- if (!nextValue) {
30
- return;
31
- }
32
-
33
- event.preventDefault();
34
- const nextTab = tabs.find((tab) => tab.value === nextValue);
35
- nextTab?.focus();
36
- setActiveValue(nextValue);
37
- });
38
- }, [bindKeydown, focusedValue, setActiveValue, tabs]);
39
-
40
- return (
41
- <Box
42
- accessibilityRole="tablist"
43
- style={{
44
- flexDirection: 'row',
45
- }}
46
- testID={testID}
47
- >
48
- {children}
49
- </Box>
50
- );
51
- }
@@ -1,67 +0,0 @@
1
- import React from 'react';
2
-
3
- import { Box } from '../../features/layout/public';
4
- import { useControllableState } from '../../internal/useControllableState';
5
- import { getTabId as resolveTabId, getTabPanelId as resolveTabPanelId } from './a11y';
6
- import { type TabRegistration, TabsContext } from './context';
7
- import type { TabsProps } from './types';
8
-
9
- export function Tabs({ children, value, defaultValue, onValueChange, testID }: TabsProps) {
10
- const [activeValue, setActiveValue] = useControllableState<string | undefined>({
11
- value,
12
- defaultValue,
13
- onChange: (nextValue) => {
14
- if (nextValue !== undefined) {
15
- onValueChange?.(nextValue);
16
- }
17
- },
18
- });
19
- const [focusedValue, setFocusedValue] = React.useState<string | undefined>(undefined);
20
- const [tabs, setTabs] = React.useState<TabRegistration[]>([]);
21
-
22
- const registerTab = React.useCallback((tab: TabRegistration) => {
23
- setTabs((current) => {
24
- return current.some((entry) => entry.value === tab.value)
25
- ? current.map((entry) => (entry.value === tab.value ? tab : entry))
26
- : [...current, tab];
27
- });
28
- }, []);
29
-
30
- const unregisterTab = React.useCallback((valueToRemove: string) => {
31
- setTabs((current) => current.filter((entry) => entry.value !== valueToRemove));
32
- }, []);
33
-
34
- React.useEffect(() => {
35
- if (activeValue !== undefined) {
36
- return;
37
- }
38
-
39
- const firstEnabledTab = tabs.find((entry) => !entry.disabled);
40
- if (firstEnabledTab) {
41
- setActiveValue(firstEnabledTab.value);
42
- }
43
- }, [activeValue, setActiveValue, tabs]);
44
-
45
- const contextValue = React.useMemo(
46
- () => ({
47
- activeValue,
48
- focusedValue,
49
- getPanelId: (tabValue: string) => resolveTabPanelId(testID, tabValue),
50
- getTabId: (tabValue: string) => resolveTabId(testID, tabValue),
51
- registerTab,
52
- setActiveValue: (nextValue: string) => {
53
- setActiveValue(nextValue);
54
- },
55
- setFocusedValue,
56
- tabs,
57
- unregisterTab,
58
- }),
59
- [activeValue, focusedValue, registerTab, setActiveValue, tabs, testID, unregisterTab],
60
- );
61
-
62
- return (
63
- <TabsContext value={contextValue}>
64
- <Box testID={testID}>{children}</Box>
65
- </TabsContext>
66
- );
67
- }
@@ -1,15 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
-
3
- import { getTabId, getTabPanelId } from './a11y';
4
-
5
- describe('tabs accessibility ids', () => {
6
- it('creates stable ids from tab values', () => {
7
- expect(getTabId(undefined, 'Account Settings')).toBe('tabs-tab-Account-Settings');
8
- expect(getTabPanelId(undefined, 'Account Settings')).toBe('tabs-panel-Account-Settings');
9
- });
10
-
11
- it('namespaces ids by tabs testID when provided', () => {
12
- expect(getTabId('settings', 'billing')).toBe('settings-tabs-tab-billing');
13
- expect(getTabPanelId('settings', 'billing')).toBe('settings-tabs-panel-billing');
14
- });
15
- });
@@ -1,15 +0,0 @@
1
- function sanitizeTabValue(value: string): string {
2
- return value.trim().replace(/[^a-zA-Z0-9_-]+/g, '-');
3
- }
4
-
5
- function createTabsIdBase(testID: string | undefined): string {
6
- return testID ? `${testID}-tabs` : 'tabs';
7
- }
8
-
9
- export function getTabId(testID: string | undefined, value: string): string {
10
- return `${createTabsIdBase(testID)}-tab-${sanitizeTabValue(value)}`;
11
- }
12
-
13
- export function getTabPanelId(testID: string | undefined, value: string): string {
14
- return `${createTabsIdBase(testID)}-panel-${sanitizeTabValue(value)}`;
15
- }
@@ -1,31 +0,0 @@
1
- import React from 'react';
2
-
3
- export interface TabRegistration {
4
- disabled: boolean;
5
- focus: () => void;
6
- value: string;
7
- }
8
-
9
- export interface TabsContextValue {
10
- activeValue: string | undefined;
11
- focusedValue: string | undefined;
12
- getPanelId: (value: string) => string;
13
- getTabId: (value: string) => string;
14
- registerTab: (tab: TabRegistration) => void;
15
- setActiveValue: (value: string) => void;
16
- setFocusedValue: (value: string | undefined) => void;
17
- tabs: TabRegistration[];
18
- unregisterTab: (value: string) => void;
19
- }
20
-
21
- export const TabsContext = React.createContext<TabsContextValue | null>(null);
22
-
23
- export function useTabsContext(): TabsContextValue {
24
- const value = React.use(TabsContext);
25
-
26
- if (!value) {
27
- throw new Error('Tabs components must be used within <Tabs>.');
28
- }
29
-
30
- return value;
31
- }
@@ -1,5 +0,0 @@
1
- export { Tab } from './Tab';
2
- export { TabList } from './TabList';
3
- export { TabPanel } from './TabPanel';
4
- export { Tabs } from './Tabs';
5
- export type { TabListProps, TabPanelProps, TabProps, TabsProps } from './types';
@@ -1,32 +0,0 @@
1
- import type { TabRegistration } from './context';
2
-
3
- export function resolveNextTabValue(
4
- tabs: TabRegistration[],
5
- currentValue: string | undefined,
6
- key: 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown' | 'Home' | 'End',
7
- ): string | undefined {
8
- const enabledTabs = tabs.filter((tab) => !tab.disabled);
9
- if (enabledTabs.length === 0) {
10
- return undefined;
11
- }
12
-
13
- if (key === 'Home') {
14
- return enabledTabs[0]?.value;
15
- }
16
-
17
- if (key === 'End') {
18
- return enabledTabs[enabledTabs.length - 1]?.value;
19
- }
20
-
21
- const currentIndex = enabledTabs.findIndex((tab) => tab.value === currentValue);
22
- if (currentIndex === -1) {
23
- return enabledTabs[0]?.value;
24
- }
25
-
26
- const nextIndex =
27
- key === 'ArrowLeft' || key === 'ArrowUp'
28
- ? (currentIndex - 1 + enabledTabs.length) % enabledTabs.length
29
- : (currentIndex + 1) % enabledTabs.length;
30
-
31
- return enabledTabs[nextIndex]?.value;
32
- }
@@ -1,30 +0,0 @@
1
- import type React from 'react';
2
-
3
- import type { InteractionPolicy } from '../../interactionPolicy';
4
-
5
- export interface TabsProps {
6
- children?: React.ReactNode;
7
- value?: string;
8
- defaultValue?: string;
9
- onValueChange?: ((value: string) => void) | undefined;
10
- testID?: string;
11
- }
12
-
13
- export interface TabListProps {
14
- children?: React.ReactNode;
15
- testID?: string;
16
- }
17
-
18
- export interface TabProps {
19
- value: string;
20
- children?: React.ReactNode;
21
- disabled?: boolean;
22
- interactionPolicy?: InteractionPolicy;
23
- testID?: string;
24
- }
25
-
26
- export interface TabPanelProps {
27
- value: string;
28
- children?: React.ReactNode;
29
- testID?: string;
30
- }