@ankhorage/zora 14.0.1 → 15.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.
Files changed (218) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/dist/features/app-bar/adapters/inbound/AppBar.d.ts.map +1 -1
  4. package/dist/features/app-bar/adapters/inbound/AppBar.js +28 -27
  5. package/dist/features/app-bar/adapters/inbound/AppBar.js.map +1 -1
  6. package/dist/features/app-bar/public.d.ts +1 -1
  7. package/dist/features/app-bar/public.d.ts.map +1 -1
  8. package/dist/features/app-bar/public.js.map +1 -1
  9. package/dist/features/bottom-sheet/public.d.ts +2 -2
  10. package/dist/features/bottom-sheet/public.d.ts.map +1 -1
  11. package/dist/features/bottom-sheet/public.js +1 -1
  12. package/dist/features/bottom-sheet/public.js.map +1 -1
  13. package/dist/features/breadcrumbs/adapters/inbound/Breadcrumbs.d.ts +5 -0
  14. package/dist/features/breadcrumbs/adapters/inbound/Breadcrumbs.d.ts.map +1 -0
  15. package/dist/features/breadcrumbs/adapters/inbound/Breadcrumbs.js +81 -0
  16. package/dist/features/breadcrumbs/adapters/inbound/Breadcrumbs.js.map +1 -0
  17. package/dist/features/breadcrumbs/breadcrumbsMeta.d.ts +119 -0
  18. package/dist/features/breadcrumbs/breadcrumbsMeta.d.ts.map +1 -0
  19. package/dist/features/breadcrumbs/breadcrumbsMeta.js +94 -0
  20. package/dist/features/breadcrumbs/breadcrumbsMeta.js.map +1 -0
  21. package/dist/features/breadcrumbs/public.d.ts +3 -0
  22. package/dist/features/breadcrumbs/public.d.ts.map +1 -0
  23. package/dist/features/breadcrumbs/public.js +2 -0
  24. package/dist/features/breadcrumbs/public.js.map +1 -0
  25. package/dist/features/button/utils/resolveButtonThemeRecipe.d.ts +1 -1
  26. package/dist/features/data-table/adapters/inbound/DataTable.d.ts.map +1 -1
  27. package/dist/features/data-table/adapters/inbound/DataTable.js +4 -2
  28. package/dist/features/data-table/adapters/inbound/DataTable.js.map +1 -1
  29. package/dist/features/form/formFieldMeta.d.ts +1 -1
  30. package/dist/features/form/formFieldMeta.js +1 -1
  31. package/dist/features/form/formFieldMeta.js.map +1 -1
  32. package/dist/features/form/radio/utils/resolveRadioGroupThemeRecipe.d.ts +1 -1
  33. package/dist/features/form/select/adapters/inbound/Select.d.ts +2 -0
  34. package/dist/features/form/select/adapters/inbound/Select.d.ts.map +1 -0
  35. package/dist/features/form/select/adapters/inbound/Select.js +2 -0
  36. package/dist/features/form/select/adapters/inbound/Select.js.map +1 -0
  37. package/dist/features/form/select/adapters/inbound/Select.native.d.ts +5 -0
  38. package/dist/features/form/select/adapters/inbound/Select.native.d.ts.map +1 -0
  39. package/dist/features/form/select/adapters/inbound/Select.native.js +42 -0
  40. package/dist/features/form/select/adapters/inbound/Select.native.js.map +1 -0
  41. package/dist/features/form/select/adapters/inbound/Select.web.d.ts +5 -0
  42. package/dist/features/form/select/adapters/inbound/Select.web.d.ts.map +1 -0
  43. package/dist/features/form/select/adapters/inbound/Select.web.js +31 -0
  44. package/dist/features/form/select/adapters/inbound/Select.web.js.map +1 -0
  45. package/dist/features/form/select/composition/SelectField.d.ts +10 -0
  46. package/dist/features/form/select/composition/SelectField.d.ts.map +1 -0
  47. package/dist/features/form/select/composition/SelectField.js +8 -0
  48. package/dist/features/form/select/composition/SelectField.js.map +1 -0
  49. package/dist/features/form/select/composition/SelectOptionRow.d.ts +12 -0
  50. package/dist/features/form/select/composition/SelectOptionRow.d.ts.map +1 -0
  51. package/dist/features/form/select/composition/SelectOptionRow.js +9 -0
  52. package/dist/features/form/select/composition/SelectOptionRow.js.map +1 -0
  53. package/dist/features/form/select/composition/SelectTrigger.d.ts +10 -0
  54. package/dist/features/form/select/composition/SelectTrigger.d.ts.map +1 -0
  55. package/dist/features/form/select/composition/SelectTrigger.js +9 -0
  56. package/dist/features/form/select/composition/SelectTrigger.js.map +1 -0
  57. package/dist/features/form/select/composition/useSelectController.d.ts +8 -0
  58. package/dist/features/form/select/composition/useSelectController.d.ts.map +1 -0
  59. package/dist/features/form/select/composition/useSelectController.js +15 -0
  60. package/dist/features/form/select/composition/useSelectController.js.map +1 -0
  61. package/dist/features/form/select/public.d.ts +3 -0
  62. package/dist/features/form/select/public.d.ts.map +1 -0
  63. package/dist/features/form/select/public.js +2 -0
  64. package/dist/features/form/select/public.js.map +1 -0
  65. package/dist/features/form/select/selectMeta.d.ts +195 -0
  66. package/dist/features/form/select/selectMeta.d.ts.map +1 -0
  67. package/dist/features/form/select/selectMeta.js +152 -0
  68. package/dist/features/form/select/selectMeta.js.map +1 -0
  69. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.d.ts +5 -0
  70. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.d.ts.map +1 -0
  71. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.js +43 -0
  72. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.js.map +1 -0
  73. package/dist/features/popover-menu/popoverMenuMeta.d.ts +9 -0
  74. package/dist/features/popover-menu/popoverMenuMeta.d.ts.map +1 -0
  75. package/dist/features/popover-menu/popoverMenuMeta.js +9 -0
  76. package/dist/features/popover-menu/popoverMenuMeta.js.map +1 -0
  77. package/dist/features/popover-menu/public.d.ts +3 -0
  78. package/dist/features/popover-menu/public.d.ts.map +1 -0
  79. package/dist/features/popover-menu/public.js +2 -0
  80. package/dist/features/popover-menu/public.js.map +1 -0
  81. package/dist/features/typography/utils/resolveHeadingThemeRecipe.d.ts +1 -1
  82. package/dist/features/typography/utils/resolveTextThemeRecipe.d.ts +1 -1
  83. package/dist/index.d.ts +4 -7
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +3 -3
  86. package/dist/index.js.map +1 -1
  87. package/dist/metadata/bindableComponentMeta.d.ts +1 -2
  88. package/dist/metadata/bindableComponentMeta.d.ts.map +1 -1
  89. package/dist/metadata/bindableComponentMeta.js +1 -2
  90. package/dist/metadata/bindableComponentMeta.js.map +1 -1
  91. package/dist/metadata/componentMeta.d.ts.map +1 -1
  92. package/dist/metadata/componentMeta.js +4 -5
  93. package/dist/metadata/componentMeta.js.map +1 -1
  94. package/dist/patterns/theme-composer/ThemeComposer.js +1 -1
  95. package/dist/patterns/theme-composer/ThemeComposer.js.map +1 -1
  96. package/dist/registry.d.ts.map +1 -1
  97. package/dist/registry.js +4 -5
  98. package/dist/registry.js.map +1 -1
  99. package/dist/types/app-bar.d.ts +6 -3
  100. package/dist/types/app-bar.d.ts.map +1 -1
  101. package/dist/types/app-bar.js.map +1 -1
  102. package/dist/{components/breadcrumbs/types.d.ts → types/breadcrumbs.d.ts} +8 -6
  103. package/dist/types/breadcrumbs.d.ts.map +1 -0
  104. package/dist/types/breadcrumbs.js +2 -0
  105. package/dist/types/breadcrumbs.js.map +1 -0
  106. package/dist/types/data-table.d.ts +2 -2
  107. package/dist/types/data-table.d.ts.map +1 -1
  108. package/dist/types/data-table.js.map +1 -1
  109. package/dist/types/popover-menu.d.ts +24 -0
  110. package/dist/types/popover-menu.d.ts.map +1 -0
  111. package/dist/types/popover-menu.js +2 -0
  112. package/dist/types/popover-menu.js.map +1 -0
  113. package/dist/types/select.d.ts +22 -0
  114. package/dist/types/select.d.ts.map +1 -0
  115. package/dist/types/select.js +2 -0
  116. package/dist/types/select.js.map +1 -0
  117. package/package.json +2 -4
  118. package/src/features/app-bar/adapters/inbound/AppBar.tsx +50 -42
  119. package/src/features/app-bar/public.ts +1 -1
  120. package/src/features/bottom-sheet/public.test.ts +10 -1
  121. package/src/features/bottom-sheet/public.ts +9 -1
  122. package/src/features/breadcrumbs/adapters/inbound/Breadcrumbs.tsx +179 -0
  123. package/src/features/breadcrumbs/breadcrumbsMeta.ts +95 -0
  124. package/src/features/breadcrumbs/public.test.ts +22 -0
  125. package/src/features/breadcrumbs/public.ts +6 -0
  126. package/src/features/data-table/adapters/inbound/DataTable.tsx +9 -5
  127. package/src/features/form/formFieldMeta.ts +1 -1
  128. package/src/features/form/select/adapters/inbound/Select.native.tsx +78 -0
  129. package/src/features/form/select/adapters/inbound/Select.tsx +1 -0
  130. package/src/features/form/select/adapters/inbound/Select.web.tsx +63 -0
  131. package/src/features/form/select/composition/SelectField.tsx +27 -0
  132. package/src/features/form/select/composition/SelectOptionRow.tsx +36 -0
  133. package/src/features/form/select/composition/SelectTrigger.tsx +32 -0
  134. package/src/features/form/select/composition/useSelectController.ts +21 -0
  135. package/src/features/form/select/public.test.ts +25 -0
  136. package/src/features/form/select/public.ts +2 -0
  137. package/src/features/form/select/selectMeta.ts +154 -0
  138. package/src/features/popover-menu/adapters/inbound/PopoverMenu.tsx +60 -0
  139. package/src/{components/breadcrumbs/meta.ts → features/popover-menu/popoverMenuMeta.ts} +3 -3
  140. package/src/features/popover-menu/public.test.ts +11 -0
  141. package/src/features/popover-menu/public.ts +6 -0
  142. package/src/index.ts +14 -7
  143. package/src/interactionPolicy.test.ts +19 -21
  144. package/src/metadata/bindableComponentMeta.test.ts +8 -0
  145. package/src/metadata/bindableComponentMeta.ts +1 -2
  146. package/src/metadata/componentMeta.test.ts +3 -0
  147. package/src/metadata/componentMeta.ts +4 -5
  148. package/src/nativePeerContract.test.ts +5 -19
  149. package/src/patterns/theme-composer/ThemeComposer.tsx +1 -1
  150. package/src/platformAcceptance.test.ts +1 -1
  151. package/src/registry.ts +4 -5
  152. package/src/theme/themeScopeStructure.test.ts +8 -3
  153. package/src/types/app-bar.ts +6 -3
  154. package/src/{components/breadcrumbs/types.ts → types/breadcrumbs.ts} +8 -5
  155. package/src/types/data-table.ts +2 -2
  156. package/src/types/popover-menu.ts +31 -0
  157. package/src/{components/select/types.ts → types/select.ts} +11 -3
  158. package/dist/components/breadcrumbs/Breadcrumbs.d.ts +0 -7
  159. package/dist/components/breadcrumbs/Breadcrumbs.d.ts.map +0 -1
  160. package/dist/components/breadcrumbs/Breadcrumbs.js +0 -80
  161. package/dist/components/breadcrumbs/Breadcrumbs.js.map +0 -1
  162. package/dist/components/breadcrumbs/index.d.ts +0 -3
  163. package/dist/components/breadcrumbs/index.d.ts.map +0 -1
  164. package/dist/components/breadcrumbs/index.js +0 -2
  165. package/dist/components/breadcrumbs/index.js.map +0 -1
  166. package/dist/components/breadcrumbs/meta.d.ts +0 -9
  167. package/dist/components/breadcrumbs/meta.d.ts.map +0 -1
  168. package/dist/components/breadcrumbs/meta.js +0 -9
  169. package/dist/components/breadcrumbs/meta.js.map +0 -1
  170. package/dist/components/breadcrumbs/types.d.ts.map +0 -1
  171. package/dist/components/breadcrumbs/types.js +0 -2
  172. package/dist/components/breadcrumbs/types.js.map +0 -1
  173. package/dist/components/menu/DropdownMenu.d.ts +0 -7
  174. package/dist/components/menu/DropdownMenu.d.ts.map +0 -1
  175. package/dist/components/menu/DropdownMenu.js +0 -9
  176. package/dist/components/menu/DropdownMenu.js.map +0 -1
  177. package/dist/components/menu/Menu.d.ts +0 -7
  178. package/dist/components/menu/Menu.d.ts.map +0 -1
  179. package/dist/components/menu/Menu.js +0 -46
  180. package/dist/components/menu/Menu.js.map +0 -1
  181. package/dist/components/menu/index.d.ts +0 -4
  182. package/dist/components/menu/index.d.ts.map +0 -1
  183. package/dist/components/menu/index.js +0 -3
  184. package/dist/components/menu/index.js.map +0 -1
  185. package/dist/components/menu/meta.d.ts +0 -17
  186. package/dist/components/menu/meta.d.ts.map +0 -1
  187. package/dist/components/menu/meta.js +0 -17
  188. package/dist/components/menu/meta.js.map +0 -1
  189. package/dist/components/menu/types.d.ts +0 -22
  190. package/dist/components/menu/types.d.ts.map +0 -1
  191. package/dist/components/menu/types.js +0 -2
  192. package/dist/components/menu/types.js.map +0 -1
  193. package/dist/components/select/Select.d.ts +0 -7
  194. package/dist/components/select/Select.d.ts.map +0 -1
  195. package/dist/components/select/Select.js +0 -32
  196. package/dist/components/select/Select.js.map +0 -1
  197. package/dist/components/select/index.d.ts +0 -3
  198. package/dist/components/select/index.d.ts.map +0 -1
  199. package/dist/components/select/index.js +0 -3
  200. package/dist/components/select/index.js.map +0 -1
  201. package/dist/components/select/meta.d.ts +0 -9
  202. package/dist/components/select/meta.d.ts.map +0 -1
  203. package/dist/components/select/meta.js +0 -9
  204. package/dist/components/select/meta.js.map +0 -1
  205. package/dist/components/select/types.d.ts +0 -14
  206. package/dist/components/select/types.d.ts.map +0 -1
  207. package/dist/components/select/types.js +0 -2
  208. package/dist/components/select/types.js.map +0 -1
  209. package/src/components/breadcrumbs/Breadcrumbs.tsx +0 -147
  210. package/src/components/breadcrumbs/index.ts +0 -2
  211. package/src/components/menu/DropdownMenu.tsx +0 -11
  212. package/src/components/menu/Menu.tsx +0 -59
  213. package/src/components/menu/index.ts +0 -3
  214. package/src/components/menu/meta.ts +0 -19
  215. package/src/components/menu/types.ts +0 -31
  216. package/src/components/select/Select.tsx +0 -67
  217. package/src/components/select/index.ts +0 -2
  218. package/src/components/select/meta.ts +0 -10
@@ -0,0 +1,154 @@
1
+ import type { ZoraComponentMeta } from '../../../metadata';
2
+
3
+ export const selectMeta = {
4
+ name: 'Select',
5
+ category: 'component',
6
+ description:
7
+ 'Single-value form selection with Popover presentation on web and BottomSheet presentation on native.',
8
+ directManifestNode: true,
9
+ allowedChildren: [],
10
+ blueprint: {
11
+ label: 'Select',
12
+ defaultProps: {
13
+ label: 'Option',
14
+ placeholder: 'Select',
15
+ defaultValue: 'option-b',
16
+ options: [
17
+ { value: 'option-a', label: 'First option' },
18
+ { value: 'option-b', label: 'Second option' },
19
+ { value: 'option-c', label: 'Third option' },
20
+ ],
21
+ disabled: false,
22
+ readOnly: false,
23
+ invalid: false,
24
+ },
25
+ },
26
+ bindings: {
27
+ props: {
28
+ value: {
29
+ label: 'Value',
30
+ description: 'Currently selected option value.',
31
+ value: { type: 'string' },
32
+ acceptsFallback: true,
33
+ acceptsTransforms: true,
34
+ },
35
+ disabled: {
36
+ label: 'Disabled',
37
+ description: 'Whether the select is disabled.',
38
+ value: { type: 'boolean' },
39
+ acceptsFallback: true,
40
+ acceptsTransforms: true,
41
+ },
42
+ },
43
+ events: {
44
+ valueChange: {
45
+ label: 'Value change',
46
+ description: 'Runs when the selected option value changes.',
47
+ payload: {
48
+ eventType: 'select.valueChange',
49
+ fields: [{ path: 'value', type: 'string', label: 'Value' }],
50
+ },
51
+ },
52
+ },
53
+ },
54
+ events: {
55
+ valueChange: {
56
+ label: 'Value change',
57
+ eventType: 'select.valueChange',
58
+ description: 'Emitted when the selected option value changes.',
59
+ payloadFields: [{ path: 'value', type: 'string', label: 'Value' }],
60
+ },
61
+ },
62
+ props: {
63
+ defaultValue: {
64
+ type: 'string',
65
+ category: 'State',
66
+ label: 'Initial value',
67
+ authoring: { authority: 'instance' },
68
+ },
69
+ value: {
70
+ type: 'string',
71
+ category: 'State',
72
+ label: 'Value',
73
+ authoring: { authority: 'instance' },
74
+ },
75
+ options: {
76
+ type: 'array',
77
+ category: 'Content',
78
+ label: 'Options',
79
+ itemSchema: [
80
+ {
81
+ key: 'value',
82
+ schema: { type: 'string', category: 'Content', label: 'Value' },
83
+ },
84
+ {
85
+ key: 'label',
86
+ schema: { type: 'string', category: 'Content', label: 'Label' },
87
+ },
88
+ {
89
+ key: 'disabled',
90
+ schema: {
91
+ type: 'boolean',
92
+ category: 'State',
93
+ label: 'Disabled',
94
+ default: false,
95
+ },
96
+ },
97
+ ],
98
+ authoring: { authority: 'instance' },
99
+ },
100
+ label: {
101
+ type: 'string',
102
+ category: 'Content',
103
+ label: 'Label',
104
+ authoring: { authority: 'instance' },
105
+ },
106
+ helperText: {
107
+ type: 'string',
108
+ category: 'Content',
109
+ label: 'Helper text',
110
+ authoring: { authority: 'instance' },
111
+ },
112
+ errorText: {
113
+ type: 'string',
114
+ category: 'Content',
115
+ label: 'Error text',
116
+ authoring: { authority: 'instance' },
117
+ },
118
+ placeholder: {
119
+ type: 'string',
120
+ category: 'Content',
121
+ label: 'Placeholder',
122
+ default: 'Select',
123
+ authoring: { authority: 'instance' },
124
+ },
125
+ required: {
126
+ type: 'boolean',
127
+ category: 'State',
128
+ label: 'Required',
129
+ default: false,
130
+ authoring: { authority: 'instance' },
131
+ },
132
+ disabled: {
133
+ type: 'boolean',
134
+ category: 'State',
135
+ label: 'Disabled',
136
+ default: false,
137
+ authoring: { authority: 'instance' },
138
+ },
139
+ readOnly: {
140
+ type: 'boolean',
141
+ category: 'State',
142
+ label: 'Read only',
143
+ default: false,
144
+ authoring: { authority: 'instance' },
145
+ },
146
+ invalid: {
147
+ type: 'boolean',
148
+ category: 'State',
149
+ label: 'Invalid',
150
+ default: false,
151
+ authoring: { authority: 'instance' },
152
+ },
153
+ },
154
+ } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,60 @@
1
+ import {
2
+ PopoverMenu as SurfacePopoverMenu,
3
+ type PopoverMenuAction as SurfacePopoverMenuAction,
4
+ } from '@ankhorage/surface';
5
+ import React from 'react';
6
+
7
+ import { resolveIconSize } from '../../../../internal/recipes';
8
+ import { useZoraTheme } from '../../../../theme/useZoraTheme';
9
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
10
+ import type { PopoverMenuAction, PopoverMenuProps } from '../../../../types/popover-menu';
11
+ import { Icon } from '../../../icon/public';
12
+ import { Box } from '../../../layout/public';
13
+
14
+ /*** Presents a ZORA-styled action list through the Surface PopoverMenu capability. */
15
+ export const PopoverMenu = withZoraThemeScope(PopoverMenuInner);
16
+
17
+ /*** Maps ZORA action presentation into the Surface action-menu boundary. */
18
+ function PopoverMenuInner({
19
+ themeId: _themeId,
20
+ mode: _mode,
21
+ actions,
22
+ onDismiss,
23
+ ...props
24
+ }: PopoverMenuProps) {
25
+ const { theme } = useZoraTheme();
26
+ const iconColor = theme.semantics.content.muted;
27
+ const surfaceActions = React.useMemo(
28
+ () => actions.map((action) => toSurfaceAction(action, iconColor)),
29
+ [actions, iconColor],
30
+ );
31
+
32
+ return <SurfacePopoverMenu {...props} actions={surfaceActions} dismiss={onDismiss} />;
33
+ }
34
+
35
+ /*** Maps one ZORA PopoverMenu action to the Surface action contract. */
36
+ function toSurfaceAction(action: PopoverMenuAction, iconColor: string): SurfacePopoverMenuAction {
37
+ return {
38
+ activate: action.onPress,
39
+ description: action.description,
40
+ disabled: action.disabled,
41
+ id: action.id,
42
+ intent: action.intent,
43
+ leading: renderActionLeading(action, iconColor),
44
+ selected: action.selected,
45
+ title: action.title,
46
+ trailing: renderActionTrailing(action),
47
+ };
48
+ }
49
+
50
+ /*** Resolves explicit leading content or the configured action icon. */
51
+ function renderActionLeading(action: PopoverMenuAction, iconColor: string): React.ReactNode {
52
+ if (action.leading !== undefined) return action.leading;
53
+ if (action.icon === undefined) return undefined;
54
+ return <Icon {...action.icon} color={iconColor} size={resolveIconSize('s')} />;
55
+ }
56
+
57
+ /*** Wraps optional trailing action content in the ZORA layout boundary. */
58
+ function renderActionTrailing(action: PopoverMenuAction): React.ReactNode {
59
+ return action.trailing === undefined ? undefined : <Box>{action.trailing}</Box>;
60
+ }
@@ -1,10 +1,10 @@
1
1
  import type { ZoraComponentMeta } from '../../metadata';
2
2
 
3
- export const breadcrumbsMeta = {
4
- name: 'Breadcrumbs',
3
+ export const popoverMenuMeta = {
4
+ name: 'PopoverMenu',
5
5
  category: 'component',
6
6
  directManifestNode: false,
7
7
  allowedChildren: [],
8
- note: 'Code-facing route hierarchy component; not represented as a manifest node in v1.',
8
+ note: 'Code-facing anchored action menu; the trigger is a render prop and is not serialized as a direct manifest node.',
9
9
  props: {},
10
10
  } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,11 @@
1
+ import { expect, test } from 'bun:test';
2
+
3
+ test('PopoverMenu composes the released Surface PopoverMenu capability', async () => {
4
+ const source = await Bun.file(
5
+ 'src/features/popover-menu/adapters/inbound/PopoverMenu.tsx',
6
+ ).text();
7
+
8
+ expect(source).toContain('PopoverMenu as SurfacePopoverMenu');
9
+ expect(source).toContain('<SurfacePopoverMenu');
10
+ expect(source).not.toContain('DropdownMenu');
11
+ });
@@ -0,0 +1,6 @@
1
+ export type {
2
+ PopoverMenuAction,
3
+ PopoverMenuActionIntent,
4
+ PopoverMenuProps,
5
+ } from '../../types/popover-menu';
6
+ export { PopoverMenu } from './adapters/inbound/PopoverMenu';
package/src/index.ts CHANGED
@@ -1,7 +1,3 @@
1
- export type { BreadcrumbItem, BreadcrumbsProps } from './components/breadcrumbs';
2
- export { Breadcrumbs } from './components/breadcrumbs';
3
- export type { DropdownMenuProps, MenuAction, MenuActionIntent, MenuProps } from './components/menu';
4
- export { DropdownMenu, Menu } from './components/menu';
5
1
  export type { ModalProps } from './components/modal';
6
2
  export { Modal } from './components/modal';
7
3
  export type { PaginationProps } from './components/pagination';
@@ -10,8 +6,6 @@ export type { RatingProps } from './components/rating';
10
6
  export { Rating } from './components/rating';
11
7
  export type { SearchBarProps } from './components/search-bar';
12
8
  export { SearchBar } from './components/search-bar';
13
- export type { SelectOption, SelectProps } from './components/select';
14
- export { Select } from './components/select';
15
9
  export type { TabItem, TabsProps, TabsVariant } from './components/tabs';
16
10
  export { Tabs } from './components/tabs';
17
11
  export type { ToolbarActionProps, ToolbarPosition, ToolbarProps } from './components/toolbar';
@@ -31,7 +25,7 @@ export {
31
25
  ActivityIndicator,
32
26
  type ActivityIndicatorProps,
33
27
  } from './features/activity-indicator/public';
34
- export type { AppBarMode, AppBarOverflowAction, AppBarProps } from './features/app-bar/public';
28
+ export type { AppBarMode, AppBarOverflowMenu, AppBarProps } from './features/app-bar/public';
35
29
  export { AppBar } from './features/app-bar/public';
36
30
  export type {
37
31
  AuthFormBaseProps,
@@ -70,6 +64,12 @@ export type { BadgeProps } from './features/badge/public';
70
64
  export { Badge } from './features/badge/public';
71
65
  export type { BottomSheetProps } from './features/bottom-sheet/public';
72
66
  export { BottomSheet } from './features/bottom-sheet/public';
67
+ export {
68
+ type BreadcrumbItem,
69
+ type BreadcrumbPressEvent,
70
+ Breadcrumbs,
71
+ type BreadcrumbsProps,
72
+ } from './features/breadcrumbs/public';
73
73
  export type { ButtonProps } from './features/button/public';
74
74
  export type {
75
75
  ButtonGroupAlign,
@@ -161,6 +161,7 @@ export {
161
161
  type RadioGroupProps,
162
162
  type RadioProps,
163
163
  } from './features/form/radio/public';
164
+ export { Select, type SelectOption, type SelectProps } from './features/form/select/public';
164
165
  export {
165
166
  TextInput,
166
167
  type TextInputProps,
@@ -219,6 +220,12 @@ export type {
219
220
  } from './features/list/public';
220
221
  export { FlatList, SectionList } from './features/list/public';
221
222
  export { MissingElement, type MissingElementProps } from './features/missing-element/public';
223
+ export {
224
+ PopoverMenu,
225
+ type PopoverMenuAction,
226
+ type PopoverMenuActionIntent,
227
+ type PopoverMenuProps,
228
+ } from './features/popover-menu/public';
222
229
  export {
223
230
  Progress,
224
231
  type ProgressProps,
@@ -2,7 +2,7 @@
2
2
  * Interaction policy regression guards.
3
3
  *
4
4
  * These tests verify that internally owned interactions respect the passive
5
- * policy returned by the canonical Surface type. They use static source
5
+ * policy returned by the canonical Surface type. They use static source
6
6
  * inspection so they run fast and do not require a renderer.
7
7
  */
8
8
 
@@ -18,10 +18,6 @@ function readSource(relativePath: string): string {
18
18
  return readFileSync(join(SRC_ROOT, relativePath), 'utf8');
19
19
  }
20
20
 
21
- function readComponent(...segments: string[]): string {
22
- return readSource(join('components', ...segments));
23
- }
24
-
25
21
  function readFeature(...segments: string[]): string {
26
22
  return readSource(join('features', ...segments));
27
23
  }
@@ -42,13 +38,13 @@ describe('InteractionPolicy declaration', () => {
42
38
  const files = [
43
39
  readSource('theme/ZoraBaseProps.ts'),
44
40
  readSource('types/app-bar.ts'),
45
- readComponent('breadcrumbs', 'types.ts'),
41
+ readSource('types/breadcrumbs.ts'),
46
42
  readSource('types/empty-state.ts'),
47
43
  readSource('types/hero.ts'),
48
44
  readSource('types/product-card.ts'),
49
45
  readPattern('reader', 'types.ts'),
50
46
  readPattern('scanner', 'types.ts'),
51
- readComponent('select', 'types.ts'),
47
+ readSource('types/select.ts'),
52
48
  readPattern('tree-view', 'types.ts'),
53
49
  ];
54
50
 
@@ -78,26 +74,27 @@ describe('Surface wrapper propagation', () => {
78
74
  });
79
75
 
80
76
  describe('AppBar', () => {
81
- test('forwards interactionPolicy to internal IconButtons', () => {
77
+ test('forwards interactionPolicy to selection and overflow controls', () => {
82
78
  const source = readFeature('app-bar', 'adapters', 'inbound', 'AppBar.tsx');
83
79
 
84
80
  expect(source).not.toMatch(/interactionPolicy:\s*_interactionPolicy/);
85
81
  expect(source).toMatch(
86
82
  /<IconButton[\s\S]*?interactionPolicy=\{interactionPolicy\}[\s\S]*?onPress=\{mode\.onCancel\}/,
87
83
  );
84
+ expect(source).toMatch(/<PopoverMenu[\s\S]*?interactionPolicy=\{interactionPolicy\}/);
88
85
  expect(source).toMatch(
89
- /<IconButton[\s\S]*?interactionPolicy=\{interactionPolicy\}[\s\S]*?onPress=\{overflow\.onPress\}/,
86
+ /trigger=\{\(\{ toggle \}\) => \([\s\S]*?<IconButton[\s\S]*?interactionPolicy=\{interactionPolicy\}[\s\S]*?onPress=\{toggle\}/,
90
87
  );
91
88
  });
92
89
  });
93
90
 
94
91
  describe('Breadcrumbs', () => {
95
- test('forwards interactionPolicy to internal Buttons', () => {
96
- const source = readComponent('breadcrumbs', 'Breadcrumbs.tsx');
92
+ test('forwards interactionPolicy and emits item ids from internal Buttons', () => {
93
+ const source = readFeature('breadcrumbs', 'adapters', 'inbound', 'Breadcrumbs.tsx');
97
94
 
98
95
  expect(source).not.toMatch(/interactionPolicy:\s*_interactionPolicy/);
99
96
  expect(source).toMatch(
100
- /<Button[\s\S]*?interactionPolicy=\{interactionPolicy\}[\s\S]*?onPress=\{item\.onPress\}/,
97
+ /<Button[\s\S]*?interactionPolicy=\{interactionPolicy\}[\s\S]*?onPress=\{\(\) => onItemPress\(\{ id: item\.id \}\)\}/,
101
98
  );
102
99
  });
103
100
  });
@@ -233,19 +230,20 @@ describe('BarcodeScannerView', () => {
233
230
  });
234
231
 
235
232
  describe('Select', () => {
236
- test('keeps Picker enabled controlled only by real disabled prop', () => {
237
- const source = readComponent('select', 'Select.tsx');
233
+ test('web forwards interactionPolicy to Popover and option controls', () => {
234
+ const webSource = readFeature('form', 'select', 'adapters', 'inbound', 'Select.web.tsx');
235
+ const optionSource = readFeature('form', 'select', 'composition', 'SelectOptionRow.tsx');
238
236
 
239
- expect(source).toMatch(/enabled=\{!disabled\}/);
237
+ expect(webSource).toContain('interactionPolicy={props.interactionPolicy}');
238
+ expect(optionSource).toContain('interactionPolicy={interactionPolicy}');
240
239
  });
241
240
 
242
- test('prevents pointer/touch interaction with Picker subtree while passive', () => {
243
- const source = readComponent('select', 'Select.tsx');
241
+ test('native suppresses opening while passive', () => {
242
+ const nativeSource = readFeature('form', 'select', 'adapters', 'inbound', 'Select.native.tsx');
244
243
 
245
- expect(source).toMatch(/pointerEvents:\s*passive \? 'none' : 'auto'/);
246
- expect(source).toMatch(
247
- /onValueChange=\{\s*\(\s*itemValue\s*\)\s*=>\s*\{\s*if\s*\(\s*!passive\s*\)/,
248
- );
244
+ expect(nativeSource).toContain("props.interactionPolicy === 'passive'");
245
+ expect(nativeSource).toContain("contentMode: 'direct'");
246
+ expect(nativeSource).toContain('<BottomSheetFlatList');
249
247
  });
250
248
  });
251
249
 
@@ -17,6 +17,14 @@ describe('ZORA bindable component metadata', () => {
17
17
  expect(registry.DataTable?.bindings?.props?.rows?.value.type).toBe('array');
18
18
  });
19
19
 
20
+ it('keeps Select direct-manifest ownership aligned across metadata registries', () => {
21
+ const select = ZORA_BINDABLE_COMPONENT_META.Select;
22
+
23
+ expect(select.directManifestNode).toBe(true);
24
+ expect(select.directManifestNode).toBe(ZORA_COMPONENT_META.Select.directManifestNode);
25
+ expect(select.bindings.events.valueChange.payload.eventType).toBe('select.valueChange');
26
+ });
27
+
20
28
  it('shares canonical Image authoring metadata while preserving dynamic image bindings', () => {
21
29
  const image = ZORA_BINDABLE_COMPONENT_META.Image;
22
30
 
@@ -190,7 +190,7 @@ export const ZORA_BINDABLE_COMPONENT_META = {
190
190
  name: 'Select',
191
191
  category: 'component',
192
192
  description: 'Captures one selected string value.',
193
- directManifestNode: false,
193
+ directManifestNode: true,
194
194
  allowedChildren: [],
195
195
  bindings: {
196
196
  props: {
@@ -228,7 +228,6 @@ export const ZORA_BINDABLE_COMPONENT_META = {
228
228
  payloadFields: [{ path: 'value', type: 'string', label: 'Value' }],
229
229
  },
230
230
  },
231
- note: 'Form control component; not represented as a manifest node in v1.',
232
231
  props: {
233
232
  value: {
234
233
  type: 'string',
@@ -106,6 +106,7 @@ describe('ZORA_COMPONENT_META invariants', () => {
106
106
  'Checkbox',
107
107
  'CheckboxGroup',
108
108
  'RadioGroup',
109
+ 'Select',
109
110
  'TextInput',
110
111
  ]);
111
112
  for (const [name, meta] of Object.entries(ZORA_COMPONENT_META)) {
@@ -146,6 +147,7 @@ describe('ZORA_COMPONENT_META invariants', () => {
146
147
  'Avatar',
147
148
  'AvatarGroup',
148
149
  'Badge',
150
+ 'Breadcrumbs',
149
151
  'Chip',
150
152
  'ChipGroup',
151
153
  'DataTable',
@@ -167,6 +169,7 @@ describe('ZORA_COMPONENT_META invariants', () => {
167
169
  'CheckboxGroup',
168
170
  'Radio',
169
171
  'RadioGroup',
172
+ 'Select',
170
173
  'ThemeModeToggle',
171
174
  'TextInput',
172
175
  'Text',
@@ -1,10 +1,7 @@
1
- import { breadcrumbsMeta } from '../components/breadcrumbs/meta';
2
- import { dropdownMenuMeta, menuMeta } from '../components/menu/meta';
3
1
  import { modalMeta } from '../components/modal/meta';
4
2
  import { paginationMeta } from '../components/pagination/meta';
5
3
  import { ratingMeta } from '../components/rating/meta';
6
4
  import { searchBarMeta } from '../components/search-bar/meta';
7
- import { selectMeta } from '../components/select/meta';
8
5
  import { tabsMeta } from '../components/tabs/meta';
9
6
  import { toolbarActionMeta, toolbarMeta } from '../components/toolbar/meta';
10
7
  import { accordionItemMeta } from '../features/accordion/accordionItemMeta';
@@ -21,6 +18,7 @@ import { avatarGroupMeta } from '../features/avatar/avatarGroupMeta';
21
18
  import { avatarMeta } from '../features/avatar/avatarMeta';
22
19
  import { badgeMeta } from '../features/badge/badgeMeta';
23
20
  import { bottomSheetMeta } from '../features/bottom-sheet/bottomSheetMeta';
21
+ import { breadcrumbsMeta } from '../features/breadcrumbs/breadcrumbsMeta';
24
22
  import { buttonGroupMeta } from '../features/button/buttonGroupMeta';
25
23
  import { buttonMeta } from '../features/button/buttonMeta';
26
24
  import { iconButtonMeta } from '../features/button/iconButtonMeta';
@@ -40,6 +38,7 @@ import { formErrorMeta } from '../features/form/formErrorMeta';
40
38
  import { formFieldMeta } from '../features/form/formFieldMeta';
41
39
  import { formMeta } from '../features/form/formMeta';
42
40
  import { radioGroupMeta, radioMeta } from '../features/form/radio/radioMeta';
41
+ import { selectMeta } from '../features/form/select/selectMeta';
43
42
  import { textInputMeta } from '../features/form/text-input/textInputMeta';
44
43
  import { gradientMeta } from '../features/gradient/gradientMeta';
45
44
  import { heroMeta } from '../features/hero/heroMeta';
@@ -55,6 +54,7 @@ import { stackMeta } from '../features/layout/stackMeta';
55
54
  import { flatListMeta } from '../features/list/flatListMeta';
56
55
  import { sectionListMeta } from '../features/list/sectionListMeta';
57
56
  import { missingElementMeta } from '../features/missing-element/missingElementMeta';
57
+ import { popoverMenuMeta } from '../features/popover-menu/popoverMenuMeta';
58
58
  import { progressMeta, progressRingMeta } from '../features/progress/progressMeta';
59
59
  import {
60
60
  skeletonCardMeta,
@@ -128,7 +128,6 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
128
128
  ChipGroup: chipGroupMeta,
129
129
  DataTable: dataTableMeta,
130
130
  DatePicker: datePickerMeta,
131
- DropdownMenu: dropdownMenuMeta,
132
131
  Form: formMeta,
133
132
  FormActions: formActionsMeta,
134
133
  FormError: formErrorMeta,
@@ -141,10 +140,10 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
141
140
  KeyboardAvoidingView: keyboardAvoidingViewMeta,
142
141
  TextInput: textInputMeta,
143
142
  MediaCard: mediaCardMeta,
144
- Menu: menuMeta,
145
143
  MetricCard: metricCardMeta,
146
144
  Modal: modalMeta,
147
145
  Pagination: paginationMeta,
146
+ PopoverMenu: popoverMenuMeta,
148
147
  Progress: progressMeta,
149
148
  ProgressRing: progressRingMeta,
150
149
  Radio: radioMeta,
@@ -6,30 +6,16 @@ function isRecord(value: unknown): value is Record<string, unknown> {
6
6
  return typeof value === 'object' && value !== null && !Array.isArray(value);
7
7
  }
8
8
 
9
- function findRecordValue(record: Record<string, unknown>, key: string): unknown {
10
- for (const [entryKey, entryValue] of Object.entries(record)) {
11
- if (entryKey === key) {
12
- return entryValue;
13
- }
14
- }
15
-
16
- return undefined;
17
- }
18
-
19
- function isOptionalPeer(metadata: unknown, packageName: string): boolean {
20
- if (!isRecord(metadata)) return false;
21
- const peerMetadata = findRecordValue(metadata, packageName);
22
- return isRecord(peerMetadata) && peerMetadata.optional === true;
23
- }
24
-
25
- test('root-entry native imports are required portable peers', async () => {
9
+ test('root-entry native imports expose only required portable peers', async () => {
26
10
  const packageJson = JSON.parse(
27
11
  await readFile(new URL('../package.json', import.meta.url), 'utf8'),
28
12
  ) as unknown;
29
13
  if (!isRecord(packageJson)) throw new Error('Expected package.json to contain an object.');
30
14
 
31
- const { peerDependencies, peerDependenciesMeta } = packageJson;
32
- expect(isOptionalPeer(peerDependenciesMeta, '@react-native-picker/picker')).toBe(false);
15
+ const { peerDependencies } = packageJson;
16
+ expect(
17
+ isRecord(peerDependencies) && peerDependencies['@react-native-picker/picker'],
18
+ ).toBeUndefined();
33
19
  expect(isRecord(peerDependencies) && peerDependencies['expo-linear-gradient']).toBeUndefined();
34
20
  expect(isRecord(peerDependencies) && peerDependencies['expo-font']).toBeUndefined();
35
21
  expect(isRecord(peerDependencies) && peerDependencies['@expo/vector-icons']).toBeUndefined();
@@ -3,11 +3,11 @@ import type { AppCategory } from '@ankhorage/contracts';
3
3
  import { APP_CATEGORIES } from '@ankhorage/contracts';
4
4
  import React from 'react';
5
5
 
6
- import { Select } from '../../components/select';
7
6
  import { Tabs } from '../../components/tabs';
8
7
  import { Badge } from '../../features/badge/public';
9
8
  import { Button } from '../../features/button/public';
10
9
  import { Card } from '../../features/card/public';
10
+ import { Select } from '../../features/form/select/public';
11
11
  import { TextInput } from '../../features/form/text-input/public';
12
12
  import { Box, Stack } from '../../features/layout/public';
13
13
  import { Heading } from '../../features/typography/public';
@@ -1,6 +1,6 @@
1
1
  import { expect, test } from 'bun:test';
2
2
 
3
- test('passes the isolated Surface 4 and RN Web 0.21 acceptance suite', () => {
3
+ test('passes the isolated Surface 5 and RN Web 0.21 acceptance suite', () => {
4
4
  const subprocess = Bun.spawnSync({
5
5
  cmd: [process.execPath, 'test', 'test-fixtures/platformAcceptance.test.tsx'],
6
6
  stderr: 'pipe',
package/src/registry.ts CHANGED
@@ -1,13 +1,10 @@
1
1
  import type { InteractionPolicyProps } from '@ankhorage/surface';
2
2
  import type React from 'react';
3
3
 
4
- import { Breadcrumbs } from './components/breadcrumbs';
5
- import { DropdownMenu, Menu } from './components/menu';
6
4
  import { Modal } from './components/modal';
7
5
  import { Pagination } from './components/pagination';
8
6
  import { Rating } from './components/rating';
9
7
  import { SearchBar } from './components/search-bar';
10
- import { Select } from './components/select';
11
8
  import { Tabs } from './components/tabs';
12
9
  import { Toolbar, ToolbarAction } from './components/toolbar';
13
10
  import { Accordion, AccordionItem } from './features/accordion/public';
@@ -25,6 +22,7 @@ import { Avatar } from './features/avatar/public';
25
22
  import { AvatarGroup } from './features/avatar/public';
26
23
  import { Badge } from './features/badge/public';
27
24
  import { BottomSheet } from './features/bottom-sheet/public';
25
+ import { Breadcrumbs } from './features/breadcrumbs/public';
28
26
  import { Button } from './features/button/public';
29
27
  import { ButtonGroup } from './features/button/public';
30
28
  import { IconButton } from './features/button/public';
@@ -41,6 +39,7 @@ import { EmptyState } from './features/empty-state/public';
41
39
  import { Checkbox, CheckboxGroup } from './features/form/checkbox/public';
42
40
  import { Form, FormActions, FormError, FormField } from './features/form/public';
43
41
  import { Radio, RadioGroup } from './features/form/radio/public';
42
+ import { Select } from './features/form/select/public';
44
43
  import { TextInput } from './features/form/text-input/public';
45
44
  import { Gradient } from './features/gradient/public';
46
45
  import { Hero } from './features/hero/public';
@@ -51,6 +50,7 @@ import { Box, Container, Divider, Grid, Stack } from './features/layout/public';
51
50
  import { ContentRail } from './features/layout/public';
52
51
  import { FlatList, SectionList } from './features/list/public';
53
52
  import { MissingElement } from './features/missing-element/public';
53
+ import { PopoverMenu } from './features/popover-menu/public';
54
54
  import { Progress, ProgressRing } from './features/progress/public';
55
55
  import { Skeleton, SkeletonCard, SkeletonList, SkeletonText } from './features/skeleton/public';
56
56
  import { Surface } from './features/surface/public';
@@ -130,7 +130,6 @@ const _ZORA_COMPONENT_REGISTRY = {
130
130
  ChipGroup,
131
131
  DataTable,
132
132
  DatePicker,
133
- DropdownMenu,
134
133
  Form,
135
134
  FormActions,
136
135
  FormError,
@@ -143,10 +142,10 @@ const _ZORA_COMPONENT_REGISTRY = {
143
142
  KeyboardAvoidingView,
144
143
  TextInput,
145
144
  MediaCard,
146
- Menu,
147
145
  MetricCard,
148
146
  Modal,
149
147
  Pagination,
148
+ PopoverMenu,
150
149
  Progress,
151
150
  ProgressRing,
152
151
  Radio,