@ankhorage/zora 1.0.8 → 1.0.10
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 +12 -0
- package/README.md +41 -0
- package/dist/components/navigation-item/NavigationItem.d.ts +4 -0
- package/dist/components/navigation-item/NavigationItem.d.ts.map +1 -0
- package/dist/components/navigation-item/NavigationItem.js +18 -0
- package/dist/components/navigation-item/NavigationItem.js.map +1 -0
- package/dist/components/navigation-item/index.d.ts +3 -0
- package/dist/components/navigation-item/index.d.ts.map +1 -0
- package/dist/components/navigation-item/index.js +2 -0
- package/dist/components/navigation-item/index.js.map +1 -0
- package/dist/components/navigation-item/types.d.ts +23 -0
- package/dist/components/navigation-item/types.d.ts.map +1 -0
- package/dist/components/navigation-item/types.js +2 -0
- package/dist/components/navigation-item/types.js.map +1 -0
- package/dist/components/navigation-list/NavigationList.d.ts +4 -0
- package/dist/components/navigation-list/NavigationList.d.ts.map +1 -0
- package/dist/components/navigation-list/NavigationList.js +26 -0
- package/dist/components/navigation-list/NavigationList.js.map +1 -0
- package/dist/components/navigation-list/index.d.ts +3 -0
- package/dist/components/navigation-list/index.d.ts.map +1 -0
- package/dist/components/navigation-list/index.js +2 -0
- package/dist/components/navigation-list/index.js.map +1 -0
- package/dist/components/navigation-list/types.d.ts +15 -0
- package/dist/components/navigation-list/types.d.ts.map +1 -0
- package/dist/components/navigation-list/types.js +2 -0
- package/dist/components/navigation-list/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/resolveZoraNavigationItems.d.ts +64 -0
- package/dist/internal/resolveZoraNavigationItems.d.ts.map +1 -0
- package/dist/internal/resolveZoraNavigationItems.js +55 -0
- package/dist/internal/resolveZoraNavigationItems.js.map +1 -0
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.d.ts +4 -0
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.d.ts.map +1 -0
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.js +26 -0
- package/dist/patterns/zora-drawer-content/ZoraDrawerContent.js.map +1 -0
- package/dist/patterns/zora-drawer-content/index.d.ts +3 -0
- package/dist/patterns/zora-drawer-content/index.d.ts.map +1 -0
- package/dist/patterns/zora-drawer-content/index.js +2 -0
- package/dist/patterns/zora-drawer-content/index.js.map +1 -0
- package/dist/patterns/zora-drawer-content/types.d.ts +15 -0
- package/dist/patterns/zora-drawer-content/types.d.ts.map +1 -0
- package/dist/patterns/zora-drawer-content/types.js +2 -0
- package/dist/patterns/zora-drawer-content/types.js.map +1 -0
- package/dist/patterns/zora-tab-bar/ZoraTabBar.d.ts +4 -0
- package/dist/patterns/zora-tab-bar/ZoraTabBar.d.ts.map +1 -0
- package/dist/patterns/zora-tab-bar/ZoraTabBar.js +33 -0
- package/dist/patterns/zora-tab-bar/ZoraTabBar.js.map +1 -0
- package/dist/patterns/zora-tab-bar/index.d.ts +3 -0
- package/dist/patterns/zora-tab-bar/index.d.ts.map +1 -0
- package/dist/patterns/zora-tab-bar/index.js +2 -0
- package/dist/patterns/zora-tab-bar/index.js.map +1 -0
- package/dist/patterns/zora-tab-bar/types.d.ts +19 -0
- package/dist/patterns/zora-tab-bar/types.d.ts.map +1 -0
- package/dist/patterns/zora-tab-bar/types.js +2 -0
- package/dist/patterns/zora-tab-bar/types.js.map +1 -0
- package/package.json +2 -2
- package/src/audit.test.ts +29 -0
- package/src/components/navigation-item/NavigationItem.tsx +36 -0
- package/src/components/navigation-item/index.ts +6 -0
- package/src/components/navigation-item/types.ts +26 -0
- package/src/components/navigation-list/NavigationList.tsx +62 -0
- package/src/components/navigation-list/index.ts +2 -0
- package/src/components/navigation-list/types.ts +17 -0
- package/src/index.ts +12 -0
- package/src/internal/resolveZoraNavigationItems.test.ts +163 -0
- package/src/internal/resolveZoraNavigationItems.ts +156 -0
- package/src/navigationExports.test.ts +15 -0
- package/src/patterns/zora-drawer-content/ZoraDrawerContent.tsx +53 -0
- package/src/patterns/zora-drawer-content/index.ts +2 -0
- package/src/patterns/zora-drawer-content/types.ts +20 -0
- package/src/patterns/zora-tab-bar/ZoraTabBar.tsx +55 -0
- package/src/patterns/zora-tab-bar/index.ts +2 -0
- package/src/patterns/zora-tab-bar/types.ts +18 -0
- package/src/showcaseCoverage.test.ts +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/patterns/zora-drawer-content/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,KAAK,EACV,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,KAAK,EAAE,mBAAmB,CAAC;IAC3B,UAAU,EAAE,oBAAoB,CAAC;IACjC,WAAW,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/patterns/zora-drawer-content/types.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { ZoraNavigationRouteMap } from '../../components/navigation-list';\nimport type {\n ZoraDrawerNavigation,\n ZoraNavigationDescriptors,\n ZoraNavigationState,\n} from '../../internal/resolveZoraNavigationItems';\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport interface ZoraDrawerContentProps extends ZoraBaseProps {\n state: ZoraNavigationState;\n navigation: ZoraDrawerNavigation;\n descriptors?: ZoraNavigationDescriptors | undefined;\n routeMap?: ZoraNavigationRouteMap | undefined;\n compact?: boolean;\n header?: React.ReactNode;\n footer?: React.ReactNode;\n testID?: string;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ZoraTabBar.d.ts","sourceRoot":"","sources":["../../../src/patterns/zora-tab-bar/ZoraTabBar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA6C/C,eAAO,MAAM,UAAU,uDAAsC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TabBar as SurfaceTabBar } from '@ankhorage/surface';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Surface } from '../../foundation';
|
|
4
|
+
import { createTabBarItemPressHandler, resolveNavigationItems, } from '../../internal/resolveZoraNavigationItems';
|
|
5
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
6
|
+
function ZoraTabBarInner({ themeId: _themeId, mode: _mode, state, navigation, descriptors, routeMap, compact = false, chrome = 'raised', testID, }) {
|
|
7
|
+
const resolved = resolveNavigationItems({
|
|
8
|
+
state,
|
|
9
|
+
descriptors,
|
|
10
|
+
routeMap,
|
|
11
|
+
kind: 'tab',
|
|
12
|
+
});
|
|
13
|
+
const items = resolved.map((item) => ({
|
|
14
|
+
id: item.route.key,
|
|
15
|
+
label: item.label,
|
|
16
|
+
icon: item.metadata?.icon,
|
|
17
|
+
badge: item.metadata?.badge,
|
|
18
|
+
active: item.active,
|
|
19
|
+
disabled: item.disabled,
|
|
20
|
+
onPress: createTabBarItemPressHandler({ item, navigation }),
|
|
21
|
+
accessibilityLabel: item.metadata?.accessibilityLabel,
|
|
22
|
+
testID: item.metadata?.testID,
|
|
23
|
+
}));
|
|
24
|
+
const content = <SurfaceTabBar compact={compact} items={items} testID={testID}/>;
|
|
25
|
+
if (chrome === 'none') {
|
|
26
|
+
return content;
|
|
27
|
+
}
|
|
28
|
+
return (<Surface variant="raised" testID={testID ? `${testID}-chrome` : undefined}>
|
|
29
|
+
{content}
|
|
30
|
+
</Surface>);
|
|
31
|
+
}
|
|
32
|
+
export const ZoraTabBar = withZoraThemeScope(ZoraTabBarInner);
|
|
33
|
+
//# sourceMappingURL=ZoraTabBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ZoraTabBar.js","sourceRoot":"","sources":["../../../src/patterns/zora-tab-bar/ZoraTabBar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,SAAS,eAAe,CAAC,EACvB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,KAAK,EACX,KAAK,EACL,UAAU,EACV,WAAW,EACX,QAAQ,EACR,OAAO,GAAG,KAAK,EACf,MAAM,GAAG,QAAQ,EACjB,MAAM,GACU;IAChB,MAAM,QAAQ,GAAG,sBAAsB,CAAC;QACtC,KAAK;QACL,WAAW;QACX,QAAQ;QACR,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACpC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI;QACzB,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,4BAA4B,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC3D,kBAAkB,EAAE,IAAI,CAAC,QAAQ,EAAE,kBAAkB;QACrD,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM;KAC9B,CAAC,CAAC,CAAC;IAEJ,MAAM,OAAO,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAG,CAAC;IAElF,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,CACL,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CACxE;MAAA,CAAC,OAAO,CACV;IAAA,EAAE,OAAO,CAAC,CACX,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,kBAAkB,CAAC,eAAe,CAAC,CAAC","sourcesContent":["import { TabBar as SurfaceTabBar } from '@ankhorage/surface';\nimport React from 'react';\n\nimport { Surface } from '../../foundation';\nimport {\n createTabBarItemPressHandler,\n resolveNavigationItems,\n} from '../../internal/resolveZoraNavigationItems';\nimport { withZoraThemeScope } from '../../theme/withZoraThemeScope';\nimport type { ZoraTabBarProps } from './types';\n\nfunction ZoraTabBarInner({\n themeId: _themeId,\n mode: _mode,\n state,\n navigation,\n descriptors,\n routeMap,\n compact = false,\n chrome = 'raised',\n testID,\n}: ZoraTabBarProps) {\n const resolved = resolveNavigationItems({\n state,\n descriptors,\n routeMap,\n kind: 'tab',\n });\n\n const items = resolved.map((item) => ({\n id: item.route.key,\n label: item.label,\n icon: item.metadata?.icon,\n badge: item.metadata?.badge,\n active: item.active,\n disabled: item.disabled,\n onPress: createTabBarItemPressHandler({ item, navigation }),\n accessibilityLabel: item.metadata?.accessibilityLabel,\n testID: item.metadata?.testID,\n }));\n\n const content = <SurfaceTabBar compact={compact} items={items} testID={testID} />;\n\n if (chrome === 'none') {\n return content;\n }\n\n return (\n <Surface variant=\"raised\" testID={testID ? `${testID}-chrome` : undefined}>\n {content}\n </Surface>\n );\n}\n\nexport const ZoraTabBar = withZoraThemeScope(ZoraTabBarInner);\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/patterns/zora-tab-bar/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/patterns/zora-tab-bar/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export type { ZoraTabBarProps } from './types';\nexport { ZoraTabBar } from './ZoraTabBar';\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ZoraNavigationRouteMap } from '../../components/navigation-list';
|
|
2
|
+
import type { ZoraNavigationDescriptors, ZoraNavigationState, ZoraTabBarNavigation } from '../../internal/resolveZoraNavigationItems';
|
|
3
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
+
export interface ZoraTabBarProps extends ZoraBaseProps {
|
|
5
|
+
state: ZoraNavigationState;
|
|
6
|
+
navigation: ZoraTabBarNavigation;
|
|
7
|
+
descriptors?: ZoraNavigationDescriptors | undefined;
|
|
8
|
+
insets?: {
|
|
9
|
+
top?: number;
|
|
10
|
+
bottom?: number;
|
|
11
|
+
left?: number;
|
|
12
|
+
right?: number;
|
|
13
|
+
} | undefined;
|
|
14
|
+
routeMap?: ZoraNavigationRouteMap | undefined;
|
|
15
|
+
compact?: boolean;
|
|
16
|
+
chrome?: 'none' | 'raised';
|
|
17
|
+
testID?: string;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/patterns/zora-tab-bar/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,KAAK,EACV,yBAAyB,EACzB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,KAAK,EAAE,mBAAmB,CAAC;IAC3B,UAAU,EAAE,oBAAoB,CAAC;IACjC,WAAW,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACpD,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACtF,QAAQ,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/patterns/zora-tab-bar/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ZoraNavigationRouteMap } from '../../components/navigation-list';\nimport type {\n ZoraNavigationDescriptors,\n ZoraNavigationState,\n ZoraTabBarNavigation,\n} from '../../internal/resolveZoraNavigationItems';\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport interface ZoraTabBarProps extends ZoraBaseProps {\n state: ZoraNavigationState;\n navigation: ZoraTabBarNavigation;\n descriptors?: ZoraNavigationDescriptors | undefined;\n insets?: { top?: number; bottom?: number; left?: number; right?: number } | undefined;\n routeMap?: ZoraNavigationRouteMap | undefined;\n compact?: boolean;\n chrome?: 'none' | 'raised';\n testID?: string;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.10",
|
|
5
5
|
"description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/zora#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@ankhorage/color-theory": "^0.0.4",
|
|
47
47
|
"@ankhorage/contracts": "^1.1.1",
|
|
48
|
-
"@ankhorage/surface": "^1.0
|
|
48
|
+
"@ankhorage/surface": "^1.1.0"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"dist",
|
package/src/audit.test.ts
CHANGED
|
@@ -240,3 +240,32 @@ describe('Plan 5 audit — public exports carry no legacy types', () => {
|
|
|
240
240
|
expect(indexSource).not.toContain('AnkhTheme');
|
|
241
241
|
});
|
|
242
242
|
});
|
|
243
|
+
|
|
244
|
+
describe('navigation chrome policy', () => {
|
|
245
|
+
const NAVIGATION_PATHS = [
|
|
246
|
+
join(SRC_ROOT, 'components', 'navigation-item'),
|
|
247
|
+
join(SRC_ROOT, 'components', 'navigation-list'),
|
|
248
|
+
join(SRC_ROOT, 'patterns', 'zora-tab-bar'),
|
|
249
|
+
join(SRC_ROOT, 'patterns', 'zora-drawer-content'),
|
|
250
|
+
join(SRC_ROOT, 'internal', 'resolveZoraNavigationItems.ts'),
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
test('does not import expo-router, react-navigation, or Surface internals', () => {
|
|
254
|
+
const sources = NAVIGATION_PATHS.flatMap((path) => {
|
|
255
|
+
if (path.endsWith('.ts')) {
|
|
256
|
+
return [readFileSync(path, 'utf8')];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!path.includes(`${SRC_ROOT}/`)) {
|
|
260
|
+
return [];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return listFiles(path).map((filePath) => readFileSync(filePath, 'utf8'));
|
|
264
|
+
}).join('\n');
|
|
265
|
+
|
|
266
|
+
expect(sources).not.toMatch(/expo-router/);
|
|
267
|
+
expect(sources).not.toMatch(/@react-navigation\//);
|
|
268
|
+
expect(sources).not.toMatch(/@ankhorage\/surface\/src\//);
|
|
269
|
+
expect(sources).not.toMatch(/@ankhorage\/surface\/dist\//);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NavigationItem as SurfaceNavigationItem } from '@ankhorage/surface';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
5
|
+
import type { NavigationItemProps } from './types';
|
|
6
|
+
|
|
7
|
+
function NavigationItemInner({
|
|
8
|
+
themeId: _themeId,
|
|
9
|
+
mode: _mode,
|
|
10
|
+
route,
|
|
11
|
+
metadata,
|
|
12
|
+
active = false,
|
|
13
|
+
compact = false,
|
|
14
|
+
onPress,
|
|
15
|
+
testID,
|
|
16
|
+
}: NavigationItemProps) {
|
|
17
|
+
return (
|
|
18
|
+
<SurfaceNavigationItem
|
|
19
|
+
compact={compact}
|
|
20
|
+
item={{
|
|
21
|
+
id: route.key,
|
|
22
|
+
label: metadata?.label ?? route.name,
|
|
23
|
+
icon: metadata?.icon,
|
|
24
|
+
badge: metadata?.badge,
|
|
25
|
+
active,
|
|
26
|
+
disabled: metadata?.disabled,
|
|
27
|
+
onPress,
|
|
28
|
+
accessibilityLabel: metadata?.accessibilityLabel,
|
|
29
|
+
testID: metadata?.testID,
|
|
30
|
+
}}
|
|
31
|
+
testID={testID}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const NavigationItem = withZoraThemeScope(NavigationItemInner);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ButtonIconSpec } from '@ankhorage/surface';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
5
|
+
|
|
6
|
+
export interface ZoraNavigationRouteMetadata {
|
|
7
|
+
label?: React.ReactNode;
|
|
8
|
+
icon?: ButtonIconSpec;
|
|
9
|
+
badge?: React.ReactNode;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
testID?: string;
|
|
12
|
+
accessibilityLabel?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ZoraNavigationRouteState {
|
|
16
|
+
key: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface NavigationItemProps extends ZoraBaseProps {
|
|
21
|
+
route: ZoraNavigationRouteState;
|
|
22
|
+
metadata?: ZoraNavigationRouteMetadata;
|
|
23
|
+
active?: boolean;
|
|
24
|
+
compact?: boolean;
|
|
25
|
+
onPress?: () => void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
}: NavigationListProps) {
|
|
23
|
+
const items = routes.map((route) => {
|
|
24
|
+
const metadata = routeMap?.[route.name];
|
|
25
|
+
const active = activeRouteKey ? route.key === activeRouteKey : false;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
id: route.key,
|
|
29
|
+
label: metadata?.label ?? route.name,
|
|
30
|
+
icon: metadata?.icon,
|
|
31
|
+
badge: metadata?.badge,
|
|
32
|
+
active,
|
|
33
|
+
disabled: metadata?.disabled,
|
|
34
|
+
onPress: metadata?.disabled ? undefined : () => onRoutePress?.(route),
|
|
35
|
+
accessibilityLabel: metadata?.accessibilityLabel,
|
|
36
|
+
testID: metadata?.testID,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (header || footer) {
|
|
41
|
+
return (
|
|
42
|
+
<SurfaceDrawerNavigation
|
|
43
|
+
compact={compact}
|
|
44
|
+
footer={footer}
|
|
45
|
+
header={header}
|
|
46
|
+
items={items}
|
|
47
|
+
testID={testID}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<SurfaceNavigationList
|
|
54
|
+
compact={compact}
|
|
55
|
+
items={items}
|
|
56
|
+
orientation={orientation}
|
|
57
|
+
testID={testID}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const NavigationList = withZoraThemeScope(NavigationListInner);
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -66,6 +66,14 @@ export type { MetricCardProps } from './components/metric-card';
|
|
|
66
66
|
export { MetricCard } from './components/metric-card';
|
|
67
67
|
export type { ModalProps } from './components/modal';
|
|
68
68
|
export { Modal } from './components/modal';
|
|
69
|
+
export type {
|
|
70
|
+
NavigationItemProps,
|
|
71
|
+
ZoraNavigationRouteMetadata,
|
|
72
|
+
ZoraNavigationRouteState,
|
|
73
|
+
} from './components/navigation-item';
|
|
74
|
+
export { NavigationItem } from './components/navigation-item';
|
|
75
|
+
export type { NavigationListProps, ZoraNavigationRouteMap } from './components/navigation-list';
|
|
76
|
+
export { NavigationList } from './components/navigation-list';
|
|
69
77
|
export type { ProgressProps } from './components/progress';
|
|
70
78
|
export { Progress } from './components/progress';
|
|
71
79
|
export type { RadioGroupOption, RadioGroupProps, RadioProps } from './components/radio';
|
|
@@ -183,4 +191,8 @@ export type { TimelineItem, TimelineProps } from './patterns/timeline';
|
|
|
183
191
|
export { Timeline } from './patterns/timeline';
|
|
184
192
|
export type { TreeItemNode, TreeItemRenderProps, TreeViewProps } from './patterns/tree-view';
|
|
185
193
|
export { TreeItem, TreeView } from './patterns/tree-view';
|
|
194
|
+
export type { ZoraDrawerContentProps } from './patterns/zora-drawer-content';
|
|
195
|
+
export { ZoraDrawerContent } from './patterns/zora-drawer-content';
|
|
196
|
+
export type { ZoraTabBarProps } from './patterns/zora-tab-bar';
|
|
197
|
+
export { ZoraTabBar } from './patterns/zora-tab-bar';
|
|
186
198
|
export * from './theme';
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
|
|
65
|
+
describe('resolveNavigationItems', () => {
|
|
66
|
+
test('maps active index + routeMap disabled state without using descriptors for icons/badges', () => {
|
|
67
|
+
const state: ZoraNavigationState = {
|
|
68
|
+
index: 1,
|
|
69
|
+
routes: [
|
|
70
|
+
{ key: 'r1', name: 'home' },
|
|
71
|
+
{ key: 'r2', name: 'settings' },
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const items = resolveNavigationItems({
|
|
76
|
+
state,
|
|
77
|
+
kind: 'tab',
|
|
78
|
+
descriptors: {
|
|
79
|
+
r1: { options: { tabBarLabel: 'Home Opt', title: 'Home Title' } },
|
|
80
|
+
r2: { options: { tabBarLabel: 'Settings Opt' } },
|
|
81
|
+
},
|
|
82
|
+
routeMap: {
|
|
83
|
+
settings: { disabled: true, badge: React.createElement('span', null, '3') },
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(items).toHaveLength(2);
|
|
88
|
+
expect(items[0]).toMatchObject({ active: false, disabled: false });
|
|
89
|
+
expect(items[1]).toMatchObject({ active: true, disabled: true });
|
|
90
|
+
expect(items[1]?.metadata?.badge).toBeDefined();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('press handler helpers', () => {
|
|
95
|
+
test('tab press handler is undefined for disabled or active items', () => {
|
|
96
|
+
const handlerDisabled = createTabBarItemPressHandler({
|
|
97
|
+
item: {
|
|
98
|
+
route: { key: 'k1', name: 'home' },
|
|
99
|
+
metadata: { disabled: true },
|
|
100
|
+
label: 'Home',
|
|
101
|
+
active: false,
|
|
102
|
+
disabled: true,
|
|
103
|
+
},
|
|
104
|
+
navigation: { navigate: () => undefined },
|
|
105
|
+
});
|
|
106
|
+
expect(handlerDisabled).toBeUndefined();
|
|
107
|
+
|
|
108
|
+
const handlerActive = createTabBarItemPressHandler({
|
|
109
|
+
item: {
|
|
110
|
+
route: { key: 'k1', name: 'home' },
|
|
111
|
+
metadata: undefined,
|
|
112
|
+
label: 'Home',
|
|
113
|
+
active: true,
|
|
114
|
+
disabled: false,
|
|
115
|
+
},
|
|
116
|
+
navigation: { navigate: () => undefined },
|
|
117
|
+
});
|
|
118
|
+
expect(handlerActive).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('tab press handler respects preventDefault', () => {
|
|
122
|
+
let navigatedTo: string | null = null;
|
|
123
|
+
const handler = createTabBarItemPressHandler({
|
|
124
|
+
item: {
|
|
125
|
+
route: { key: 'k1', name: 'home' },
|
|
126
|
+
metadata: undefined,
|
|
127
|
+
label: 'Home',
|
|
128
|
+
active: false,
|
|
129
|
+
disabled: false,
|
|
130
|
+
},
|
|
131
|
+
navigation: {
|
|
132
|
+
emit: () => ({ defaultPrevented: true }),
|
|
133
|
+
navigate: (name) => {
|
|
134
|
+
navigatedTo = name;
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(handler).toBeDefined();
|
|
140
|
+
handler?.();
|
|
141
|
+
expect(navigatedTo).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('drawer press handler navigates and closes drawer', () => {
|
|
145
|
+
const calls: string[] = [];
|
|
146
|
+
const handler = createDrawerItemPressHandler({
|
|
147
|
+
item: {
|
|
148
|
+
route: { key: 'k1', name: 'home' },
|
|
149
|
+
metadata: undefined,
|
|
150
|
+
label: 'Home',
|
|
151
|
+
active: false,
|
|
152
|
+
disabled: false,
|
|
153
|
+
} satisfies ZoraResolvedNavigationItem,
|
|
154
|
+
navigation: {
|
|
155
|
+
navigate: (name) => calls.push(`navigate:${name}`),
|
|
156
|
+
closeDrawer: () => calls.push('close'),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
handler?.();
|
|
161
|
+
expect(calls).toEqual(['navigate:home', 'close']);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
export interface ZoraNavigationDescriptorOptions {
|
|
10
|
+
title?: string;
|
|
11
|
+
tabBarLabel?: string | React.ReactNode;
|
|
12
|
+
drawerLabel?: string | React.ReactNode;
|
|
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
|
+
export interface ZoraTabPressEvent {
|
|
29
|
+
type: 'tabPress';
|
|
30
|
+
target: string;
|
|
31
|
+
canPreventDefault: true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export 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 fallback =
|
|
75
|
+
kind === 'tab'
|
|
76
|
+
? (options?.tabBarLabel ?? options?.title)
|
|
77
|
+
: (options?.drawerLabel ?? options?.title);
|
|
78
|
+
|
|
79
|
+
if (fallback !== undefined) {
|
|
80
|
+
return { label: fallback, source: 'descriptor' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { label: route.name, source: 'name' };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function resolveNavigationItems({
|
|
87
|
+
state,
|
|
88
|
+
descriptors,
|
|
89
|
+
routeMap,
|
|
90
|
+
kind,
|
|
91
|
+
}: {
|
|
92
|
+
state: ZoraNavigationState;
|
|
93
|
+
descriptors?: ZoraNavigationDescriptors | undefined;
|
|
94
|
+
routeMap?: ZoraNavigationRouteMap | undefined;
|
|
95
|
+
kind: 'tab' | 'drawer';
|
|
96
|
+
}): readonly ZoraResolvedNavigationItem[] {
|
|
97
|
+
const activeRoute = state.routes[state.index];
|
|
98
|
+
|
|
99
|
+
return state.routes.map((route) => {
|
|
100
|
+
const metadata = routeMap?.[route.name];
|
|
101
|
+
const descriptor = descriptors?.[route.key];
|
|
102
|
+
const { label } = resolveRouteLabel({ route, metadata, descriptor, kind });
|
|
103
|
+
const disabled = Boolean(metadata?.disabled);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
route,
|
|
107
|
+
metadata,
|
|
108
|
+
label,
|
|
109
|
+
active: activeRoute ? activeRoute.key === route.key : false,
|
|
110
|
+
disabled,
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function createTabBarItemPressHandler({
|
|
116
|
+
item,
|
|
117
|
+
navigation,
|
|
118
|
+
}: {
|
|
119
|
+
item: ZoraResolvedNavigationItem;
|
|
120
|
+
navigation: ZoraTabBarNavigation;
|
|
121
|
+
}): (() => void) | undefined {
|
|
122
|
+
if (item.disabled || item.active) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return () => {
|
|
127
|
+
const result = navigation.emit?.({
|
|
128
|
+
type: 'tabPress',
|
|
129
|
+
target: item.route.key,
|
|
130
|
+
canPreventDefault: true,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (result?.defaultPrevented) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
navigation.navigate(item.route.name);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createDrawerItemPressHandler({
|
|
142
|
+
item,
|
|
143
|
+
navigation,
|
|
144
|
+
}: {
|
|
145
|
+
item: ZoraResolvedNavigationItem;
|
|
146
|
+
navigation: ZoraDrawerNavigation;
|
|
147
|
+
}): (() => void) | undefined {
|
|
148
|
+
if (item.disabled) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return () => {
|
|
153
|
+
navigation.navigate(item.route.name);
|
|
154
|
+
navigation.closeDrawer?.();
|
|
155
|
+
};
|
|
156
|
+
}
|