@ankhorage/zora 4.4.1 → 5.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.
- package/CHANGELOG.md +13 -0
- package/README.md +1 -1
- package/dist/components/radio/RadioGroup.d.ts +1 -3
- package/dist/components/radio/RadioGroup.d.ts.map +1 -1
- package/dist/components/radio/RadioGroup.js +52 -40
- package/dist/components/radio/RadioGroup.js.map +1 -1
- package/dist/components/radio/RadioOptionControl.d.ts +15 -0
- package/dist/components/radio/RadioOptionControl.d.ts.map +1 -0
- package/dist/components/radio/RadioOptionControl.js +105 -0
- package/dist/components/radio/RadioOptionControl.js.map +1 -0
- package/dist/components/radio/handleRadioOptionKeyDown.d.ts +13 -0
- package/dist/components/radio/handleRadioOptionKeyDown.d.ts.map +1 -0
- package/dist/components/radio/handleRadioOptionKeyDown.js +22 -0
- package/dist/components/radio/handleRadioOptionKeyDown.js.map +1 -0
- package/dist/components/radio/meta.d.ts +38 -3
- package/dist/components/radio/meta.d.ts.map +1 -1
- package/dist/components/radio/meta.js +28 -3
- package/dist/components/radio/meta.js.map +1 -1
- package/dist/components/radio/resolveRadioOptionColors.d.ts +10 -0
- package/dist/components/radio/resolveRadioOptionColors.d.ts.map +1 -0
- package/dist/components/radio/resolveRadioOptionColors.js +27 -0
- package/dist/components/radio/resolveRadioOptionColors.js.map +1 -0
- package/dist/components/radio/types.d.ts +12 -2
- package/dist/components/radio/types.d.ts.map +1 -1
- package/dist/components/radio/types.js.map +1 -1
- package/dist/constants/keyboard.d.ts +3 -0
- package/dist/constants/keyboard.d.ts.map +1 -0
- package/dist/constants/keyboard.js +8 -0
- package/dist/constants/keyboard.js.map +1 -0
- package/dist/index.d.ts +0 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +1 -1
- package/dist/metadata/componentMeta.d.ts.map +1 -1
- package/dist/metadata/componentMeta.js +0 -12
- package/dist/metadata/componentMeta.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +0 -12
- package/dist/registry.js.map +1 -1
- package/package.json +1 -1
- package/src/audit.test.ts +0 -29
- package/src/components/radio/RadioGroup.test.ts +13 -9
- package/src/components/radio/RadioGroup.tsx +85 -111
- package/src/components/radio/RadioOptionControl.tsx +192 -0
- package/src/components/radio/handleRadioOptionKeyDown.test.ts +47 -0
- package/src/components/radio/handleRadioOptionKeyDown.ts +37 -0
- package/src/components/radio/meta.ts +28 -3
- package/src/components/radio/resolveRadioOptionColors.test.ts +29 -0
- package/src/components/radio/resolveRadioOptionColors.ts +37 -0
- package/src/components/radio/types.ts +10 -2
- package/src/constants/keyboard.ts +7 -0
- package/src/index.ts +0 -23
- package/src/interactionPolicy.test.ts +0 -17
- package/src/metadata/componentMeta.test.ts +1 -0
- package/src/metadata/componentMeta.ts +0 -12
- package/src/registry.ts +0 -12
- package/src/showcaseCoverage.test.ts +0 -6
- package/src/theme/themeScopeStructure.test.ts +0 -4
- package/dist/components/drawer/Drawer.d.ts +0 -7
- package/dist/components/drawer/Drawer.d.ts.map +0 -1
- package/dist/components/drawer/Drawer.js +0 -36
- package/dist/components/drawer/Drawer.js.map +0 -1
- package/dist/components/drawer/index.d.ts +0 -3
- package/dist/components/drawer/index.d.ts.map +0 -1
- package/dist/components/drawer/index.js +0 -2
- package/dist/components/drawer/index.js.map +0 -1
- package/dist/components/drawer/meta.d.ts +0 -9
- package/dist/components/drawer/meta.d.ts.map +0 -1
- package/dist/components/drawer/meta.js +0 -9
- package/dist/components/drawer/meta.js.map +0 -1
- package/dist/components/drawer/types.d.ts +0 -10
- package/dist/components/drawer/types.d.ts.map +0 -1
- package/dist/components/drawer/types.js +0 -2
- package/dist/components/drawer/types.js.map +0 -1
- package/dist/components/navigation-item/NavigationItem.d.ts +0 -7
- package/dist/components/navigation-item/NavigationItem.d.ts.map +0 -1
- package/dist/components/navigation-item/NavigationItem.js +0 -21
- package/dist/components/navigation-item/NavigationItem.js.map +0 -1
- package/dist/components/navigation-item/index.d.ts +0 -3
- package/dist/components/navigation-item/index.d.ts.map +0 -1
- package/dist/components/navigation-item/index.js +0 -2
- package/dist/components/navigation-item/index.js.map +0 -1
- package/dist/components/navigation-item/meta.d.ts +0 -9
- package/dist/components/navigation-item/meta.d.ts.map +0 -1
- package/dist/components/navigation-item/meta.js +0 -9
- package/dist/components/navigation-item/meta.js.map +0 -1
- package/dist/components/navigation-item/types.d.ts +0 -23
- package/dist/components/navigation-item/types.d.ts.map +0 -1
- package/dist/components/navigation-item/types.js +0 -2
- package/dist/components/navigation-item/types.js.map +0 -1
- package/dist/components/navigation-list/NavigationList.d.ts +0 -7
- package/dist/components/navigation-list/NavigationList.d.ts.map +0 -1
- package/dist/components/navigation-list/NavigationList.js +0 -29
- package/dist/components/navigation-list/NavigationList.js.map +0 -1
- package/dist/components/navigation-list/index.d.ts +0 -3
- package/dist/components/navigation-list/index.d.ts.map +0 -1
- package/dist/components/navigation-list/index.js +0 -2
- package/dist/components/navigation-list/index.js.map +0 -1
- package/dist/components/navigation-list/meta.d.ts +0 -9
- package/dist/components/navigation-list/meta.d.ts.map +0 -1
- package/dist/components/navigation-list/meta.js +0 -9
- package/dist/components/navigation-list/meta.js.map +0 -1
- package/dist/components/navigation-list/types.d.ts +0 -15
- package/dist/components/navigation-list/types.d.ts.map +0 -1
- package/dist/components/navigation-list/types.js +0 -2
- package/dist/components/navigation-list/types.js.map +0 -1
- package/dist/internal/resolveZoraNavigationItems.d.ts +0 -65
- package/dist/internal/resolveZoraNavigationItems.d.ts.map +0 -1
- package/dist/internal/resolveZoraNavigationItems.js +0 -56
- package/dist/internal/resolveZoraNavigationItems.js.map +0 -1
- package/dist/patterns/responsive-panel/ResponsivePanel.d.ts +0 -16
- package/dist/patterns/responsive-panel/ResponsivePanel.d.ts.map +0 -1
- package/dist/patterns/responsive-panel/ResponsivePanel.js +0 -101
- package/dist/patterns/responsive-panel/ResponsivePanel.js.map +0 -1
- package/dist/patterns/responsive-panel/index.d.ts +0 -3
- package/dist/patterns/responsive-panel/index.d.ts.map +0 -1
- package/dist/patterns/responsive-panel/index.js +0 -3
- package/dist/patterns/responsive-panel/index.js.map +0 -1
- package/dist/patterns/responsive-panel/meta.d.ts +0 -9
- package/dist/patterns/responsive-panel/meta.d.ts.map +0 -1
- package/dist/patterns/responsive-panel/meta.js +0 -9
- package/dist/patterns/responsive-panel/meta.js.map +0 -1
- package/dist/patterns/responsive-panel/types.d.ts +0 -23
- package/dist/patterns/responsive-panel/types.d.ts.map +0 -1
- package/dist/patterns/responsive-panel/types.js +0 -2
- package/dist/patterns/responsive-panel/types.js.map +0 -1
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.d.ts +0 -9
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.d.ts.map +0 -1
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.js +0 -31
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.js.map +0 -1
- package/dist/patterns/zora-drawer-content/index.d.ts +0 -3
- package/dist/patterns/zora-drawer-content/index.d.ts.map +0 -1
- package/dist/patterns/zora-drawer-content/index.js +0 -2
- package/dist/patterns/zora-drawer-content/index.js.map +0 -1
- package/dist/patterns/zora-drawer-content/meta.d.ts +0 -9
- package/dist/patterns/zora-drawer-content/meta.d.ts.map +0 -1
- package/dist/patterns/zora-drawer-content/meta.js +0 -9
- package/dist/patterns/zora-drawer-content/meta.js.map +0 -1
- package/dist/patterns/zora-drawer-content/types.d.ts +0 -15
- package/dist/patterns/zora-drawer-content/types.d.ts.map +0 -1
- package/dist/patterns/zora-drawer-content/types.js +0 -2
- package/dist/patterns/zora-drawer-content/types.js.map +0 -1
- package/dist/patterns/zora-tab-bar/ZoraTabBar.d.ts +0 -9
- package/dist/patterns/zora-tab-bar/ZoraTabBar.d.ts.map +0 -1
- package/dist/patterns/zora-tab-bar/ZoraTabBar.js +0 -89
- package/dist/patterns/zora-tab-bar/ZoraTabBar.js.map +0 -1
- package/dist/patterns/zora-tab-bar/index.d.ts +0 -3
- package/dist/patterns/zora-tab-bar/index.d.ts.map +0 -1
- package/dist/patterns/zora-tab-bar/index.js +0 -2
- package/dist/patterns/zora-tab-bar/index.js.map +0 -1
- package/dist/patterns/zora-tab-bar/meta.d.ts +0 -9
- package/dist/patterns/zora-tab-bar/meta.d.ts.map +0 -1
- package/dist/patterns/zora-tab-bar/meta.js +0 -9
- package/dist/patterns/zora-tab-bar/meta.js.map +0 -1
- package/dist/patterns/zora-tab-bar/types.d.ts +0 -19
- package/dist/patterns/zora-tab-bar/types.d.ts.map +0 -1
- package/dist/patterns/zora-tab-bar/types.js +0 -2
- package/dist/patterns/zora-tab-bar/types.js.map +0 -1
- package/src/components/drawer/Drawer.tsx +0 -59
- package/src/components/drawer/index.ts +0 -2
- package/src/components/drawer/meta.ts +0 -10
- package/src/components/drawer/types.ts +0 -17
- package/src/components/navigation-item/NavigationItem.tsx +0 -41
- package/src/components/navigation-item/index.ts +0 -6
- package/src/components/navigation-item/meta.ts +0 -10
- package/src/components/navigation-item/types.ts +0 -26
- package/src/components/navigation-list/NavigationList.tsx +0 -68
- package/src/components/navigation-list/index.ts +0 -2
- package/src/components/navigation-list/meta.ts +0 -10
- package/src/components/navigation-list/types.ts +0 -17
- package/src/internal/resolveZoraNavigationItems.test.ts +0 -174
- package/src/internal/resolveZoraNavigationItems.ts +0 -157
- package/src/navigationExports.test.ts +0 -15
- package/src/patterns/responsive-panel/ResponsivePanel.tsx +0 -172
- package/src/patterns/responsive-panel/index.ts +0 -2
- package/src/patterns/responsive-panel/meta.ts +0 -10
- package/src/patterns/responsive-panel/types.ts +0 -25
- package/src/patterns/zora-drawer-content/ZoraDrawerContent.tsx +0 -60
- package/src/patterns/zora-drawer-content/index.ts +0 -2
- package/src/patterns/zora-drawer-content/meta.ts +0 -10
- package/src/patterns/zora-drawer-content/types.ts +0 -20
- package/src/patterns/zora-tab-bar/ZoraTabBar.tsx +0 -157
- package/src/patterns/zora-tab-bar/index.ts +0 -2
- package/src/patterns/zora-tab-bar/meta.ts +0 -10
- package/src/patterns/zora-tab-bar/types.ts +0 -18
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DrawerNavigation as SurfaceDrawerNavigation,
|
|
3
|
-
NavigationList as SurfaceNavigationList,
|
|
4
|
-
} from '@ankhorage/surface';
|
|
5
|
-
import React from 'react';
|
|
6
|
-
|
|
7
|
-
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
8
|
-
import type { NavigationListProps } from './types';
|
|
9
|
-
|
|
10
|
-
function NavigationListInner({
|
|
11
|
-
themeId: _themeId,
|
|
12
|
-
mode: _mode,
|
|
13
|
-
routes,
|
|
14
|
-
routeMap,
|
|
15
|
-
activeRouteKey,
|
|
16
|
-
orientation = 'vertical',
|
|
17
|
-
compact = false,
|
|
18
|
-
onRoutePress,
|
|
19
|
-
header,
|
|
20
|
-
footer,
|
|
21
|
-
testID,
|
|
22
|
-
interactionPolicy,
|
|
23
|
-
}: NavigationListProps) {
|
|
24
|
-
const items = routes.map((route) => {
|
|
25
|
-
const metadata = routeMap?.[route.name];
|
|
26
|
-
const active = activeRouteKey ? route.key === activeRouteKey : false;
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
id: route.key,
|
|
30
|
-
label: metadata?.label ?? route.name,
|
|
31
|
-
icon: metadata?.icon,
|
|
32
|
-
badge: metadata?.badge,
|
|
33
|
-
active,
|
|
34
|
-
disabled: metadata?.disabled,
|
|
35
|
-
onPress: metadata?.disabled ? undefined : () => onRoutePress?.(route),
|
|
36
|
-
accessibilityLabel: metadata?.accessibilityLabel,
|
|
37
|
-
testID: metadata?.testID,
|
|
38
|
-
};
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (header || footer) {
|
|
42
|
-
return (
|
|
43
|
-
<SurfaceDrawerNavigation
|
|
44
|
-
compact={compact}
|
|
45
|
-
footer={footer}
|
|
46
|
-
header={header}
|
|
47
|
-
interactionPolicy={interactionPolicy}
|
|
48
|
-
items={items}
|
|
49
|
-
testID={testID}
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<SurfaceNavigationList
|
|
56
|
-
compact={compact}
|
|
57
|
-
interactionPolicy={interactionPolicy}
|
|
58
|
-
items={items}
|
|
59
|
-
orientation={orientation}
|
|
60
|
-
testID={testID}
|
|
61
|
-
/>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/***
|
|
66
|
-
* Renders a navigation list from route metadata with active state mapping.
|
|
67
|
-
*/
|
|
68
|
-
export const NavigationList = withZoraThemeScope(NavigationListInner);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ZoraComponentMeta } from '../../metadata';
|
|
2
|
-
|
|
3
|
-
export const navigationListMeta = {
|
|
4
|
-
name: 'NavigationList',
|
|
5
|
-
category: 'component',
|
|
6
|
-
directManifestNode: false,
|
|
7
|
-
allowedChildren: [],
|
|
8
|
-
note: 'Navigation chrome component; not represented as a manifest node in v1.',
|
|
9
|
-
props: {},
|
|
10
|
-
} as const satisfies ZoraComponentMeta;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type React from 'react';
|
|
2
|
-
|
|
3
|
-
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
-
import type { ZoraNavigationRouteMetadata, ZoraNavigationRouteState } from '../navigation-item';
|
|
5
|
-
|
|
6
|
-
export type ZoraNavigationRouteMap = Partial<Record<string, ZoraNavigationRouteMetadata>>;
|
|
7
|
-
|
|
8
|
-
export interface NavigationListProps extends ZoraBaseProps {
|
|
9
|
-
routes: readonly ZoraNavigationRouteState[];
|
|
10
|
-
routeMap?: ZoraNavigationRouteMap;
|
|
11
|
-
activeRouteKey?: string;
|
|
12
|
-
orientation?: 'vertical' | 'horizontal';
|
|
13
|
-
compact?: boolean;
|
|
14
|
-
onRoutePress?: (route: ZoraNavigationRouteState) => void;
|
|
15
|
-
header?: React.ReactNode;
|
|
16
|
-
footer?: React.ReactNode;
|
|
17
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
createDrawerItemPressHandler,
|
|
6
|
-
createTabBarItemPressHandler,
|
|
7
|
-
resolveNavigationItems,
|
|
8
|
-
resolveRouteLabel,
|
|
9
|
-
type ZoraNavigationDescriptor,
|
|
10
|
-
type ZoraNavigationState,
|
|
11
|
-
type ZoraResolvedNavigationItem,
|
|
12
|
-
type ZoraRouteLabelSource,
|
|
13
|
-
} from './resolveZoraNavigationItems';
|
|
14
|
-
|
|
15
|
-
describe('resolveRouteLabel', () => {
|
|
16
|
-
test('prefers routeMap label over descriptor label', () => {
|
|
17
|
-
const descriptor: ZoraNavigationDescriptor = {
|
|
18
|
-
options: {
|
|
19
|
-
title: 'Home Title',
|
|
20
|
-
tabBarLabel: 'Home Tab',
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const result = resolveRouteLabel({
|
|
25
|
-
kind: 'tab',
|
|
26
|
-
route: { key: 'k1', name: 'home' },
|
|
27
|
-
metadata: { label: 'Home Map' },
|
|
28
|
-
descriptor,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const expectedSource: ZoraRouteLabelSource = 'routeMap';
|
|
32
|
-
expect(result).toEqual({ label: 'Home Map', source: expectedSource });
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('uses descriptor label/title as fallback for label only', () => {
|
|
36
|
-
const descriptor: ZoraNavigationDescriptor = {
|
|
37
|
-
options: {
|
|
38
|
-
title: 'Settings Title',
|
|
39
|
-
tabBarLabel: undefined,
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const result = resolveRouteLabel({
|
|
44
|
-
kind: 'tab',
|
|
45
|
-
route: { key: 'k2', name: 'settings' },
|
|
46
|
-
metadata: undefined,
|
|
47
|
-
descriptor,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
expect(result).toEqual({ label: 'Settings Title', source: 'descriptor' });
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('falls back to route name if nothing else provided', () => {
|
|
54
|
-
const result = resolveRouteLabel({
|
|
55
|
-
kind: 'drawer',
|
|
56
|
-
route: { key: 'k3', name: 'profile' },
|
|
57
|
-
metadata: undefined,
|
|
58
|
-
descriptor: undefined,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(result).toEqual({ label: 'profile', source: 'name' });
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('uses descriptor title instead of invoking a callable Expo tab label', () => {
|
|
65
|
-
const result = resolveRouteLabel({
|
|
66
|
-
route: { key: 'k4', name: 'home' },
|
|
67
|
-
metadata: undefined,
|
|
68
|
-
descriptor: { options: { tabBarLabel: () => 'callable', title: 'Home title' } },
|
|
69
|
-
kind: 'tab',
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(result).toEqual({ label: 'Home title', source: 'descriptor' });
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('resolveNavigationItems', () => {
|
|
77
|
-
test('maps active index + routeMap disabled state without using descriptors for icons/badges', () => {
|
|
78
|
-
const state: ZoraNavigationState = {
|
|
79
|
-
index: 1,
|
|
80
|
-
routes: [
|
|
81
|
-
{ key: 'r1', name: 'home' },
|
|
82
|
-
{ key: 'r2', name: 'settings' },
|
|
83
|
-
],
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const items = resolveNavigationItems({
|
|
87
|
-
state,
|
|
88
|
-
kind: 'tab',
|
|
89
|
-
descriptors: {
|
|
90
|
-
r1: { options: { tabBarLabel: 'Home Opt', title: 'Home Title' } },
|
|
91
|
-
r2: { options: { tabBarLabel: 'Settings Opt' } },
|
|
92
|
-
},
|
|
93
|
-
routeMap: {
|
|
94
|
-
settings: { disabled: true, badge: React.createElement('span', null, '3') },
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
expect(items).toHaveLength(2);
|
|
99
|
-
expect(items[0]).toMatchObject({ active: false, disabled: false });
|
|
100
|
-
expect(items[1]).toMatchObject({ active: true, disabled: true });
|
|
101
|
-
expect(items[1]?.metadata?.badge).toBeDefined();
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('press handler helpers', () => {
|
|
106
|
-
test('tab press handler is undefined for disabled or active items', () => {
|
|
107
|
-
const handlerDisabled = createTabBarItemPressHandler({
|
|
108
|
-
item: {
|
|
109
|
-
route: { key: 'k1', name: 'home' },
|
|
110
|
-
metadata: { disabled: true },
|
|
111
|
-
label: 'Home',
|
|
112
|
-
active: false,
|
|
113
|
-
disabled: true,
|
|
114
|
-
},
|
|
115
|
-
navigation: { navigate: () => undefined },
|
|
116
|
-
});
|
|
117
|
-
expect(handlerDisabled).toBeUndefined();
|
|
118
|
-
|
|
119
|
-
const handlerActive = createTabBarItemPressHandler({
|
|
120
|
-
item: {
|
|
121
|
-
route: { key: 'k1', name: 'home' },
|
|
122
|
-
metadata: undefined,
|
|
123
|
-
label: 'Home',
|
|
124
|
-
active: true,
|
|
125
|
-
disabled: false,
|
|
126
|
-
},
|
|
127
|
-
navigation: { navigate: () => undefined },
|
|
128
|
-
});
|
|
129
|
-
expect(handlerActive).toBeUndefined();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('tab press handler respects preventDefault', () => {
|
|
133
|
-
let navigatedTo: string | null = null;
|
|
134
|
-
const handler = createTabBarItemPressHandler({
|
|
135
|
-
item: {
|
|
136
|
-
route: { key: 'k1', name: 'home' },
|
|
137
|
-
metadata: undefined,
|
|
138
|
-
label: 'Home',
|
|
139
|
-
active: false,
|
|
140
|
-
disabled: false,
|
|
141
|
-
},
|
|
142
|
-
navigation: {
|
|
143
|
-
emit: () => ({ defaultPrevented: true }),
|
|
144
|
-
navigate: (name) => {
|
|
145
|
-
navigatedTo = name;
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
expect(handler).toBeDefined();
|
|
151
|
-
handler?.();
|
|
152
|
-
expect(navigatedTo).toBeNull();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('drawer press handler navigates and closes drawer', () => {
|
|
156
|
-
const calls: string[] = [];
|
|
157
|
-
const handler = createDrawerItemPressHandler({
|
|
158
|
-
item: {
|
|
159
|
-
route: { key: 'k1', name: 'home' },
|
|
160
|
-
metadata: undefined,
|
|
161
|
-
label: 'Home',
|
|
162
|
-
active: false,
|
|
163
|
-
disabled: false,
|
|
164
|
-
} satisfies ZoraResolvedNavigationItem,
|
|
165
|
-
navigation: {
|
|
166
|
-
navigate: (name) => calls.push(`navigate:${name}`),
|
|
167
|
-
closeDrawer: () => calls.push('close'),
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
handler?.();
|
|
172
|
-
expect(calls).toEqual(['navigate:home', 'close']);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import type React from 'react';
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
ZoraNavigationRouteMetadata,
|
|
5
|
-
ZoraNavigationRouteState,
|
|
6
|
-
} from '../components/navigation-item';
|
|
7
|
-
import type { ZoraNavigationRouteMap } from '../components/navigation-list';
|
|
8
|
-
|
|
9
|
-
interface ZoraNavigationDescriptorOptions {
|
|
10
|
-
title?: string;
|
|
11
|
-
tabBarLabel?: unknown;
|
|
12
|
-
drawerLabel?: unknown;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface ZoraNavigationDescriptor {
|
|
16
|
-
options?: ZoraNavigationDescriptorOptions;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type ZoraNavigationDescriptors = Readonly<
|
|
20
|
-
Record<string, ZoraNavigationDescriptor | undefined>
|
|
21
|
-
>;
|
|
22
|
-
|
|
23
|
-
export interface ZoraNavigationState {
|
|
24
|
-
index: number;
|
|
25
|
-
routes: readonly ZoraNavigationRouteState[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface ZoraTabPressEvent {
|
|
29
|
-
type: 'tabPress';
|
|
30
|
-
target: string;
|
|
31
|
-
canPreventDefault: true;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface ZoraTabPressEventResult {
|
|
35
|
-
defaultPrevented: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface ZoraTabBarNavigation {
|
|
39
|
-
emit?: (event: ZoraTabPressEvent) => ZoraTabPressEventResult;
|
|
40
|
-
navigate: (name: string) => void;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface ZoraDrawerNavigation {
|
|
44
|
-
navigate: (name: string) => void;
|
|
45
|
-
closeDrawer?: () => void;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ZoraResolvedNavigationItem {
|
|
49
|
-
route: ZoraNavigationRouteState;
|
|
50
|
-
metadata?: ZoraNavigationRouteMetadata;
|
|
51
|
-
label: React.ReactNode;
|
|
52
|
-
active: boolean;
|
|
53
|
-
disabled: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export type ZoraRouteLabelSource = 'routeMap' | 'descriptor' | 'name';
|
|
57
|
-
|
|
58
|
-
export function resolveRouteLabel({
|
|
59
|
-
route,
|
|
60
|
-
metadata,
|
|
61
|
-
descriptor,
|
|
62
|
-
kind,
|
|
63
|
-
}: {
|
|
64
|
-
route: ZoraNavigationRouteState;
|
|
65
|
-
metadata: ZoraNavigationRouteMetadata | undefined;
|
|
66
|
-
descriptor: ZoraNavigationDescriptor | undefined;
|
|
67
|
-
kind: 'tab' | 'drawer';
|
|
68
|
-
}): { label: React.ReactNode; source: ZoraRouteLabelSource } {
|
|
69
|
-
if (metadata?.label !== undefined) {
|
|
70
|
-
return { label: metadata.label, source: 'routeMap' };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const options = descriptor?.options;
|
|
74
|
-
const configuredLabel = kind === 'tab' ? options?.tabBarLabel : options?.drawerLabel;
|
|
75
|
-
const fallback =
|
|
76
|
-
configuredLabel === undefined || typeof configuredLabel === 'function'
|
|
77
|
-
? options?.title
|
|
78
|
-
: configuredLabel;
|
|
79
|
-
|
|
80
|
-
if (fallback !== undefined) {
|
|
81
|
-
return { label: fallback as React.ReactNode, source: 'descriptor' };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return { label: route.name, source: 'name' };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function resolveNavigationItems({
|
|
88
|
-
state,
|
|
89
|
-
descriptors,
|
|
90
|
-
routeMap,
|
|
91
|
-
kind,
|
|
92
|
-
}: {
|
|
93
|
-
state: ZoraNavigationState;
|
|
94
|
-
descriptors?: ZoraNavigationDescriptors | undefined;
|
|
95
|
-
routeMap?: ZoraNavigationRouteMap | undefined;
|
|
96
|
-
kind: 'tab' | 'drawer';
|
|
97
|
-
}): readonly ZoraResolvedNavigationItem[] {
|
|
98
|
-
const activeRoute = state.routes[state.index];
|
|
99
|
-
|
|
100
|
-
return state.routes.map((route) => {
|
|
101
|
-
const metadata = routeMap?.[route.name];
|
|
102
|
-
const descriptor = descriptors?.[route.key];
|
|
103
|
-
const { label } = resolveRouteLabel({ route, metadata, descriptor, kind });
|
|
104
|
-
const disabled = Boolean(metadata?.disabled);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
route,
|
|
108
|
-
metadata,
|
|
109
|
-
label,
|
|
110
|
-
active: activeRoute ? activeRoute.key === route.key : false,
|
|
111
|
-
disabled,
|
|
112
|
-
};
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function createTabBarItemPressHandler({
|
|
117
|
-
item,
|
|
118
|
-
navigation,
|
|
119
|
-
}: {
|
|
120
|
-
item: ZoraResolvedNavigationItem;
|
|
121
|
-
navigation: ZoraTabBarNavigation;
|
|
122
|
-
}): (() => void) | undefined {
|
|
123
|
-
if (item.disabled || item.active) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return () => {
|
|
128
|
-
const result = navigation.emit?.({
|
|
129
|
-
type: 'tabPress',
|
|
130
|
-
target: item.route.key,
|
|
131
|
-
canPreventDefault: true,
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (result?.defaultPrevented) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
navigation.navigate(item.route.name);
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function createDrawerItemPressHandler({
|
|
143
|
-
item,
|
|
144
|
-
navigation,
|
|
145
|
-
}: {
|
|
146
|
-
item: ZoraResolvedNavigationItem;
|
|
147
|
-
navigation: ZoraDrawerNavigation;
|
|
148
|
-
}): (() => void) | undefined {
|
|
149
|
-
if (item.disabled) {
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return () => {
|
|
154
|
-
navigation.navigate(item.route.name);
|
|
155
|
-
navigation.closeDrawer?.();
|
|
156
|
-
};
|
|
157
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { describe, expect, test } from 'bun:test';
|
|
5
|
-
|
|
6
|
-
describe('navigation exports', () => {
|
|
7
|
-
test('exports navigation chrome from src/index.ts', () => {
|
|
8
|
-
const source = readFileSync(join(process.cwd(), 'src', 'index.ts'), 'utf8');
|
|
9
|
-
|
|
10
|
-
expect(source).toContain('export { NavigationItem }');
|
|
11
|
-
expect(source).toContain('export { NavigationList }');
|
|
12
|
-
expect(source).toContain('export { ZoraTabBar }');
|
|
13
|
-
expect(source).toContain('export { ZoraDrawerContent }');
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ScrollView, StyleSheet, View } from 'react-native';
|
|
3
|
-
|
|
4
|
-
import { Drawer } from '../../components/drawer';
|
|
5
|
-
import { Modal } from '../../components/modal';
|
|
6
|
-
import type { ZoraContentWidth } from '../../internal/recipes';
|
|
7
|
-
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
8
|
-
import { Panel } from '../panel';
|
|
9
|
-
import type { ResponsivePanelProps, ResponsivePanelScroll, ResponsivePanelSize } from './types';
|
|
10
|
-
|
|
11
|
-
function resolvePanelWidth(size: ResponsivePanelSize): number {
|
|
12
|
-
switch (size) {
|
|
13
|
-
case 'compact':
|
|
14
|
-
return 300;
|
|
15
|
-
case 'wide':
|
|
16
|
-
return 420;
|
|
17
|
-
case 'extraWide':
|
|
18
|
-
return 560;
|
|
19
|
-
case 'default':
|
|
20
|
-
default:
|
|
21
|
-
return 360;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveModalWidth(size: ResponsivePanelSize | undefined): ZoraContentWidth | undefined {
|
|
26
|
-
switch (size) {
|
|
27
|
-
case 'compact':
|
|
28
|
-
return 'narrow';
|
|
29
|
-
case 'wide':
|
|
30
|
-
case 'extraWide':
|
|
31
|
-
return 'wide';
|
|
32
|
-
case 'default':
|
|
33
|
-
return 'default';
|
|
34
|
-
default:
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function renderPanelBody(children: React.ReactNode, scroll: ResponsivePanelScroll) {
|
|
40
|
-
if (children == null) return null;
|
|
41
|
-
|
|
42
|
-
if (scroll === 'content') {
|
|
43
|
-
return (
|
|
44
|
-
<ScrollView
|
|
45
|
-
contentContainerStyle={styles.scrollContent}
|
|
46
|
-
keyboardShouldPersistTaps="handled"
|
|
47
|
-
showsVerticalScrollIndicator
|
|
48
|
-
style={styles.scrollBody}
|
|
49
|
-
>
|
|
50
|
-
{children}
|
|
51
|
-
</ScrollView>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return children;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function ResponsivePanelInner({
|
|
59
|
-
themeId: _themeId,
|
|
60
|
-
mode: _mode,
|
|
61
|
-
title,
|
|
62
|
-
description,
|
|
63
|
-
actions,
|
|
64
|
-
footer,
|
|
65
|
-
children,
|
|
66
|
-
open,
|
|
67
|
-
onOpenChange,
|
|
68
|
-
side = 'right',
|
|
69
|
-
desktopMode = 'inline',
|
|
70
|
-
mobileMode = 'drawer',
|
|
71
|
-
compact = true,
|
|
72
|
-
size,
|
|
73
|
-
scroll = 'none',
|
|
74
|
-
testID,
|
|
75
|
-
interactionPolicy,
|
|
76
|
-
}: ResponsivePanelProps) {
|
|
77
|
-
if (!open) return null;
|
|
78
|
-
|
|
79
|
-
const passive = interactionPolicy === 'passive';
|
|
80
|
-
const body = renderPanelBody(children, scroll);
|
|
81
|
-
|
|
82
|
-
// For now, we assume desktopMode determines the rendering.
|
|
83
|
-
// In a real app, this would react to window size.
|
|
84
|
-
if (desktopMode === 'floating') {
|
|
85
|
-
if (mobileMode === 'modal') {
|
|
86
|
-
const modalWidth = resolveModalWidth(size);
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<Modal
|
|
90
|
-
description={description}
|
|
91
|
-
footer={footer}
|
|
92
|
-
interactionPolicy={interactionPolicy}
|
|
93
|
-
onDismiss={() => {
|
|
94
|
-
if (passive) return;
|
|
95
|
-
|
|
96
|
-
onOpenChange(false);
|
|
97
|
-
}}
|
|
98
|
-
testID={testID}
|
|
99
|
-
title={title}
|
|
100
|
-
visible={open}
|
|
101
|
-
width={modalWidth}
|
|
102
|
-
>
|
|
103
|
-
{body}
|
|
104
|
-
</Modal>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<Drawer
|
|
110
|
-
description={description}
|
|
111
|
-
footer={footer}
|
|
112
|
-
interactionPolicy={interactionPolicy}
|
|
113
|
-
onDismiss={() => {
|
|
114
|
-
if (passive) return;
|
|
115
|
-
|
|
116
|
-
onOpenChange(false);
|
|
117
|
-
}}
|
|
118
|
-
position={side}
|
|
119
|
-
testID={testID}
|
|
120
|
-
title={title}
|
|
121
|
-
visible={open}
|
|
122
|
-
>
|
|
123
|
-
{body}
|
|
124
|
-
</Drawer>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const panel = (
|
|
129
|
-
<Panel
|
|
130
|
-
actions={actions}
|
|
131
|
-
compact={compact}
|
|
132
|
-
description={description}
|
|
133
|
-
footer={footer}
|
|
134
|
-
interactionPolicy={interactionPolicy}
|
|
135
|
-
testID={testID}
|
|
136
|
-
title={title}
|
|
137
|
-
>
|
|
138
|
-
{body}
|
|
139
|
-
</Panel>
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (size === undefined) return panel;
|
|
143
|
-
|
|
144
|
-
return <View style={[styles.inlineSizedPanel, { width: resolvePanelWidth(size) }]}>{panel}</View>;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const styles = StyleSheet.create({
|
|
148
|
-
inlineSizedPanel: {
|
|
149
|
-
maxWidth: '100%',
|
|
150
|
-
},
|
|
151
|
-
scrollBody: {
|
|
152
|
-
flexGrow: 0,
|
|
153
|
-
maxHeight: '100%',
|
|
154
|
-
},
|
|
155
|
-
scrollContent: {
|
|
156
|
-
flexGrow: 1,
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
/***
|
|
161
|
-
* Adaptive secondary surface that can render as an inline panel, drawer, or modal.
|
|
162
|
-
*
|
|
163
|
-
* Use `ResponsivePanel` for tool panes and admin/detail flows that need the same
|
|
164
|
-
* content to work across compact mobile screens and wider desktop layouts.
|
|
165
|
-
*
|
|
166
|
-
|
|
167
|
-
* @example Scrollable wide panel
|
|
168
|
-
* ```tsx
|
|
169
|
-
* <ResponsivePanel open title="APIs" size="wide" scroll="content">...</ResponsivePanel>
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
export const ResponsivePanel = withZoraThemeScope(ResponsivePanelInner);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ZoraComponentMeta } from '../../metadata';
|
|
2
|
-
|
|
3
|
-
export const responsivePanelMeta = {
|
|
4
|
-
name: 'ResponsivePanel',
|
|
5
|
-
category: 'pattern',
|
|
6
|
-
directManifestNode: false,
|
|
7
|
-
allowedChildren: [],
|
|
8
|
-
note: 'Responsive panel pattern; not represented as a manifest node in v1.',
|
|
9
|
-
props: {},
|
|
10
|
-
} as const satisfies ZoraComponentMeta;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type React from 'react';
|
|
2
|
-
|
|
3
|
-
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
-
|
|
5
|
-
export type ResponsivePanelSide = 'left' | 'right';
|
|
6
|
-
export type ResponsivePanelDesktopMode = 'inline' | 'floating';
|
|
7
|
-
export type ResponsivePanelMobileMode = 'drawer' | 'modal';
|
|
8
|
-
export type ResponsivePanelSize = 'compact' | 'default' | 'wide' | 'extraWide';
|
|
9
|
-
export type ResponsivePanelScroll = 'none' | 'content';
|
|
10
|
-
|
|
11
|
-
export interface ResponsivePanelProps extends ZoraBaseProps {
|
|
12
|
-
title?: React.ReactNode;
|
|
13
|
-
description?: React.ReactNode;
|
|
14
|
-
actions?: React.ReactNode;
|
|
15
|
-
footer?: React.ReactNode;
|
|
16
|
-
children?: React.ReactNode;
|
|
17
|
-
open: boolean;
|
|
18
|
-
onOpenChange: (open: boolean) => void;
|
|
19
|
-
side?: ResponsivePanelSide;
|
|
20
|
-
desktopMode?: ResponsivePanelDesktopMode;
|
|
21
|
-
mobileMode?: ResponsivePanelMobileMode;
|
|
22
|
-
compact?: boolean;
|
|
23
|
-
size?: ResponsivePanelSize;
|
|
24
|
-
scroll?: ResponsivePanelScroll;
|
|
25
|
-
}
|