@ankhorage/zora 2.4.7 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1542 -3430
  3. package/dist/components/breadcrumbs/Breadcrumbs.d.ts +4 -0
  4. package/dist/components/breadcrumbs/Breadcrumbs.d.ts.map +1 -0
  5. package/dist/components/breadcrumbs/Breadcrumbs.js +77 -0
  6. package/dist/components/breadcrumbs/Breadcrumbs.js.map +1 -0
  7. package/dist/components/breadcrumbs/index.d.ts +3 -0
  8. package/dist/components/breadcrumbs/index.d.ts.map +1 -0
  9. package/dist/components/breadcrumbs/index.js +2 -0
  10. package/dist/components/breadcrumbs/index.js.map +1 -0
  11. package/dist/components/breadcrumbs/meta.d.ts +9 -0
  12. package/dist/components/breadcrumbs/meta.d.ts.map +1 -0
  13. package/dist/components/breadcrumbs/meta.js +9 -0
  14. package/dist/components/breadcrumbs/meta.js.map +1 -0
  15. package/dist/components/breadcrumbs/types.d.ts +19 -0
  16. package/dist/components/breadcrumbs/types.d.ts.map +1 -0
  17. package/dist/components/breadcrumbs/types.js +2 -0
  18. package/dist/components/breadcrumbs/types.js.map +1 -0
  19. package/dist/index.d.ts +4 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/metadata/bindableComponentMeta.d.ts +553 -0
  24. package/dist/metadata/bindableComponentMeta.d.ts.map +1 -0
  25. package/dist/metadata/bindableComponentMeta.js +473 -0
  26. package/dist/metadata/bindableComponentMeta.js.map +1 -0
  27. package/dist/metadata/componentMeta.d.ts.map +1 -1
  28. package/dist/metadata/componentMeta.js +2 -0
  29. package/dist/metadata/componentMeta.js.map +1 -1
  30. package/dist/metadata/index.d.ts +2 -0
  31. package/dist/metadata/index.d.ts.map +1 -1
  32. package/dist/metadata/index.js +1 -0
  33. package/dist/metadata/index.js.map +1 -1
  34. package/dist/theme/useZoraTheme.d.ts +1 -1
  35. package/package.json +11 -2
  36. package/src/components/breadcrumbs/Breadcrumbs.tsx +143 -0
  37. package/src/components/breadcrumbs/index.ts +2 -0
  38. package/src/components/breadcrumbs/meta.ts +10 -0
  39. package/src/components/breadcrumbs/types.ts +21 -0
  40. package/src/index.ts +4 -1
  41. package/src/metadata/bindableComponentMeta.test.ts +18 -0
  42. package/src/metadata/bindableComponentMeta.ts +476 -0
  43. package/src/metadata/componentMeta.ts +2 -0
  44. package/src/metadata/index.ts +2 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "2.4.7",
4
+ "version": "2.5.0",
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": {
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@ankhorage/color-theory": "^0.0.7",
47
- "@ankhorage/contracts": "^1.7.0",
47
+ "@ankhorage/contracts": "^1.12.0",
48
48
  "@ankhorage/surface": "^2.0.2"
49
49
  },
50
50
  "files": [
@@ -63,6 +63,13 @@
63
63
  "import": "./dist/index.js",
64
64
  "default": "./dist/index.js"
65
65
  },
66
+ "./metadata": {
67
+ "react-native": "./src/metadata/index.ts",
68
+ "browser": "./src/metadata/index.ts",
69
+ "types": "./dist/metadata/index.d.ts",
70
+ "import": "./dist/metadata/index.js",
71
+ "default": "./dist/metadata/index.js"
72
+ },
66
73
  "./package.json": "./package.json"
67
74
  },
68
75
  "publishConfig": {
@@ -72,6 +79,7 @@
72
79
  "build": "rm -rf dist tsconfig.tsbuildinfo && bun x tsc -p tsconfig.json",
73
80
  "changeset": "changeset",
74
81
  "changeset:status": "changeset status --since=origin/main",
82
+ "docs": "paradox",
75
83
  "knip": "ankhorage-knip",
76
84
  "lint": "ankhorage-eslint . --max-warnings=0",
77
85
  "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
@@ -84,6 +92,7 @@
84
92
  },
85
93
  "devDependencies": {
86
94
  "@ankhorage/devtools": "^1.0.6",
95
+ "@ankhorage/paradox": "^0.1.7",
87
96
  "@changesets/cli": "^2.31.0",
88
97
  "@expo/vector-icons": "^15.1.1",
89
98
  "@react-native-picker/picker": "^2.11.4",
@@ -0,0 +1,143 @@
1
+ import React from 'react';
2
+
3
+ import { Inline } from '../../foundation';
4
+ import { resolveIconSize } from '../../internal/recipes';
5
+ import { useZoraTheme } from '../../theme/useZoraTheme';
6
+ import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
7
+ import { Button } from '../button';
8
+ import { Icon } from '../icon';
9
+ import { Text } from '../text';
10
+ import type { BreadcrumbItem, BreadcrumbsProps } from './types';
11
+
12
+ type BreadcrumbRenderItem =
13
+ | {
14
+ item: BreadcrumbItem;
15
+ type: 'item';
16
+ }
17
+ | {
18
+ type: 'ellipsis';
19
+ };
20
+
21
+ function normalizeMaxItems(value: number | undefined): number | undefined {
22
+ if (value === undefined || !Number.isFinite(value)) {
23
+ return undefined;
24
+ }
25
+
26
+ return Math.max(0, Math.trunc(value));
27
+ }
28
+
29
+ function createRenderItems(
30
+ items: readonly BreadcrumbItem[],
31
+ maxItems: number | undefined,
32
+ ): readonly BreadcrumbRenderItem[] {
33
+ if (maxItems === undefined || maxItems === 0 || items.length <= maxItems || maxItems < 3) {
34
+ return items.map((item) => ({ item, type: 'item' }));
35
+ }
36
+
37
+ const [firstItem] = items;
38
+ if (firstItem === undefined) {
39
+ return [];
40
+ }
41
+
42
+ const trailingCount = maxItems - 2;
43
+ const trailingItems = items.slice(items.length - trailingCount);
44
+
45
+ return [
46
+ { item: firstItem, type: 'item' },
47
+ { type: 'ellipsis' },
48
+ ...trailingItems.map((item) => ({ item, type: 'item' }) satisfies BreadcrumbRenderItem),
49
+ ];
50
+ }
51
+
52
+ function renderSeparator(separator: React.ReactNode) {
53
+ return (
54
+ <Text emphasis="muted" variant="bodySmall">
55
+ {separator}
56
+ </Text>
57
+ );
58
+ }
59
+
60
+ function BreadcrumbLabel({ item, compact }: { item: BreadcrumbItem; compact: boolean }) {
61
+ const { theme } = useZoraTheme();
62
+
63
+ return (
64
+ <Inline align="center" gap="xs" wrap="nowrap">
65
+ {item.icon ? (
66
+ <Icon
67
+ color={theme.semantics.content.muted}
68
+ name={item.icon.name}
69
+ provider={item.icon.provider}
70
+ size={resolveIconSize(compact ? 's' : 'm')}
71
+ />
72
+ ) : null}
73
+ <Text emphasis="muted" variant={compact ? 'caption' : 'bodySmall'}>
74
+ {item.label}
75
+ </Text>
76
+ </Inline>
77
+ );
78
+ }
79
+
80
+ function BreadcrumbsInner({
81
+ themeId: _themeId,
82
+ mode: _mode,
83
+ items,
84
+ separator = '/',
85
+ maxItems,
86
+ compact = false,
87
+ disabled = false,
88
+ testID,
89
+ }: BreadcrumbsProps) {
90
+ const normalizedMaxItems = normalizeMaxItems(maxItems);
91
+ const renderItems = createRenderItems(items, normalizedMaxItems);
92
+ const currentItemId = items.at(-1)?.id;
93
+
94
+ if (items.length === 0) {
95
+ return null;
96
+ }
97
+
98
+ return (
99
+ <Inline align="center" gap={compact ? 'xs' : 's'} testID={testID} wrap="wrap">
100
+ {renderItems.map((renderItem, index) => {
101
+ const isLastRenderedItem = index === renderItems.length - 1;
102
+ const showSeparator = !isLastRenderedItem;
103
+
104
+ if (renderItem.type === 'ellipsis') {
105
+ return (
106
+ <React.Fragment key="ellipsis">
107
+ <Text emphasis="muted" variant={compact ? 'caption' : 'bodySmall'}>
108
+
109
+ </Text>
110
+ {showSeparator ? renderSeparator(separator) : null}
111
+ </React.Fragment>
112
+ );
113
+ }
114
+
115
+ const { item } = renderItem;
116
+ const current = item.id === currentItemId;
117
+ const interactive = !current && !disabled && !item.disabled && Boolean(item.onPress);
118
+
119
+ return (
120
+ <React.Fragment key={item.id}>
121
+ {interactive ? (
122
+ <Button
123
+ color="neutral"
124
+ leadingIcon={item.icon}
125
+ onPress={item.onPress}
126
+ size={compact ? 's' : 'm'}
127
+ testID={testID ? `${testID}-item-${item.id}` : undefined}
128
+ variant="ghost"
129
+ >
130
+ {item.label}
131
+ </Button>
132
+ ) : (
133
+ <BreadcrumbLabel item={item} compact={compact} />
134
+ )}
135
+ {showSeparator ? renderSeparator(separator) : null}
136
+ </React.Fragment>
137
+ );
138
+ })}
139
+ </Inline>
140
+ );
141
+ }
142
+
143
+ export const Breadcrumbs = withZoraThemeScope(BreadcrumbsInner);
@@ -0,0 +1,2 @@
1
+ export { Breadcrumbs } from './Breadcrumbs';
2
+ export type { BreadcrumbItem, BreadcrumbsProps } from './types';
@@ -0,0 +1,10 @@
1
+ import type { ZoraComponentMeta } from '../../metadata';
2
+
3
+ export const breadcrumbsMeta = {
4
+ name: 'Breadcrumbs',
5
+ category: 'component',
6
+ directManifestNode: false,
7
+ allowedChildren: [],
8
+ note: 'Code-facing route hierarchy component; not represented as a manifest node in v1.',
9
+ props: {},
10
+ } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,21 @@
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 BreadcrumbItem {
7
+ id: string;
8
+ label: React.ReactNode;
9
+ icon?: ButtonIconSpec;
10
+ onPress?: () => void;
11
+ disabled?: boolean;
12
+ }
13
+
14
+ export interface BreadcrumbsProps extends ZoraBaseProps {
15
+ items: readonly BreadcrumbItem[];
16
+ separator?: React.ReactNode;
17
+ maxItems?: number;
18
+ compact?: boolean;
19
+ disabled?: boolean;
20
+ testID?: string;
21
+ }
package/src/index.ts CHANGED
@@ -8,6 +8,8 @@ export type { AvatarGroupItem, AvatarGroupProps } from './components/avatar-grou
8
8
  export { AvatarGroup } from './components/avatar-group';
9
9
  export type { BadgeProps } from './components/badge';
10
10
  export { Badge } from './components/badge';
11
+ export type { BreadcrumbItem, BreadcrumbsProps } from './components/breadcrumbs';
12
+ export { Breadcrumbs } from './components/breadcrumbs';
11
13
  export type { ButtonProps } from './components/button';
12
14
  export { Button } from './components/button';
13
15
  export type {
@@ -192,6 +194,7 @@ export { SidebarLayout } from './layout/sidebar-layout';
192
194
  export type { TopbarLayoutProps } from './layout/topbar-layout';
193
195
  export { TopbarLayout } from './layout/topbar-layout';
194
196
  export type {
197
+ ZoraBindableComponentType,
195
198
  ZoraComponentBlueprint,
196
199
  ZoraComponentCategory,
197
200
  ZoraComponentEventMeta,
@@ -207,7 +210,7 @@ export type {
207
210
  ZoraComponentPropValue,
208
211
  ZoraComponentSlotMeta,
209
212
  } from './metadata';
210
- export { ZORA_COMPONENT_META } from './metadata';
213
+ export { ZORA_BINDABLE_COMPONENT_META, ZORA_COMPONENT_META } from './metadata';
211
214
  export type {
212
215
  AuthFormBaseProps,
213
216
  AuthIdentifierKind,
@@ -0,0 +1,18 @@
1
+ import type { UiComponentMetaRegistry } from '@ankhorage/contracts';
2
+ import { describe, expect, it } from 'bun:test';
3
+
4
+ import { ZORA_BINDABLE_COMPONENT_META } from './bindableComponentMeta';
5
+
6
+ describe('ZORA bindable component metadata', () => {
7
+ it('matches the shared UI metadata registry shape', () => {
8
+ const registry: UiComponentMetaRegistry = ZORA_BINDABLE_COMPONENT_META;
9
+
10
+ expect(registry.Text?.bindings?.props?.text?.value.type).toBe('string');
11
+ expect(registry.Heading?.bindings?.props?.text?.value.type).toBe('string');
12
+ expect(registry.Button?.bindings?.props?.children?.value.type).toBe('string');
13
+ expect(registry.Input?.bindings?.props?.value?.value.type).toBe('string');
14
+ expect(registry.Select?.bindings?.props?.value?.value.type).toBe('string');
15
+ expect(registry.Image?.bindings?.props?.source?.value.type).toBe('imageAsset');
16
+ expect(registry.DataTable?.bindings?.props?.rows?.value.type).toBe('array');
17
+ });
18
+ });