@ankhorage/surface 4.4.1 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/dist/features/bottom-sheet/BottomSheetProvider.js +8 -6
- package/dist/features/bottom-sheet/BottomSheetProvider.js.map +1 -1
- package/dist/features/bottom-sheet/public.d.ts +2 -1
- package/dist/features/bottom-sheet/public.d.ts.map +1 -1
- package/dist/features/bottom-sheet/public.js +1 -0
- package/dist/features/bottom-sheet/public.js.map +1 -1
- package/dist/{components/modal → features/modal/adapters/inbound}/Modal.d.ts +2 -1
- package/dist/features/modal/adapters/inbound/Modal.d.ts.map +1 -0
- package/dist/features/modal/adapters/inbound/Modal.js +51 -0
- package/dist/features/modal/adapters/inbound/Modal.js.map +1 -0
- package/dist/features/modal/public.d.ts +3 -0
- package/dist/features/modal/public.d.ts.map +1 -0
- package/dist/features/modal/public.js +2 -0
- package/dist/features/modal/public.js.map +1 -0
- package/dist/features/popover-menu/adapters/inbound/PopoverMenu.d.ts +5 -0
- package/dist/features/popover-menu/adapters/inbound/PopoverMenu.d.ts.map +1 -0
- package/dist/features/popover-menu/adapters/inbound/PopoverMenu.js +85 -0
- package/dist/features/popover-menu/adapters/inbound/PopoverMenu.js.map +1 -0
- package/dist/features/popover-menu/composition/usePopoverMenuController.d.ts +19 -0
- package/dist/features/popover-menu/composition/usePopoverMenuController.d.ts.map +1 -0
- package/dist/features/popover-menu/composition/usePopoverMenuController.js +35 -0
- package/dist/features/popover-menu/composition/usePopoverMenuController.js.map +1 -0
- package/dist/features/popover-menu/public.d.ts +3 -0
- package/dist/features/popover-menu/public.d.ts.map +1 -0
- package/dist/features/popover-menu/public.js +2 -0
- package/dist/features/popover-menu/public.js.map +1 -0
- package/dist/features/popover-menu/utils/resolveNextMenuIndex.d.ts +5 -0
- package/dist/features/popover-menu/utils/resolveNextMenuIndex.d.ts.map +1 -0
- package/dist/features/popover-menu/utils/resolveNextMenuIndex.js +18 -0
- package/dist/features/popover-menu/utils/resolveNextMenuIndex.js.map +1 -0
- package/dist/features/tabs/adapters/inbound/Tab.d.ts +5 -0
- package/dist/features/tabs/adapters/inbound/Tab.d.ts.map +1 -0
- package/dist/features/tabs/adapters/inbound/Tab.js +33 -0
- package/dist/features/tabs/adapters/inbound/Tab.js.map +1 -0
- package/dist/features/tabs/adapters/inbound/TabList.d.ts +5 -0
- package/dist/features/tabs/adapters/inbound/TabList.d.ts.map +1 -0
- package/dist/features/tabs/adapters/inbound/TabList.js +42 -0
- package/dist/features/tabs/adapters/inbound/TabList.js.map +1 -0
- package/dist/features/tabs/adapters/inbound/TabPanel.d.ts +5 -0
- package/dist/features/tabs/adapters/inbound/TabPanel.d.ts.map +1 -0
- package/dist/features/tabs/adapters/inbound/TabPanel.js +14 -0
- package/dist/features/tabs/adapters/inbound/TabPanel.js.map +1 -0
- package/dist/features/tabs/adapters/inbound/Tabs.d.ts +5 -0
- package/dist/features/tabs/adapters/inbound/Tabs.d.ts.map +1 -0
- package/dist/features/tabs/adapters/inbound/Tabs.js +12 -0
- package/dist/features/tabs/adapters/inbound/Tabs.js.map +1 -0
- package/dist/features/tabs/composition/TabsContext.d.ts +4 -0
- package/dist/features/tabs/composition/TabsContext.d.ts.map +1 -0
- package/dist/features/tabs/composition/TabsContext.js +3 -0
- package/dist/features/tabs/composition/TabsContext.js.map +1 -0
- package/dist/features/tabs/composition/useTabRegistration.d.ts +7 -0
- package/dist/features/tabs/composition/useTabRegistration.d.ts.map +1 -0
- package/dist/features/tabs/composition/useTabRegistration.js +20 -0
- package/dist/features/tabs/composition/useTabRegistration.js.map +1 -0
- package/dist/features/tabs/composition/useTabsContext.d.ts +4 -0
- package/dist/features/tabs/composition/useTabsContext.d.ts.map +1 -0
- package/dist/{components/tabs/context.js → features/tabs/composition/useTabsContext.js} +4 -4
- package/dist/features/tabs/composition/useTabsContext.js.map +1 -0
- package/dist/features/tabs/composition/useTabsController.d.ts +4 -0
- package/dist/features/tabs/composition/useTabsController.d.ts.map +1 -0
- package/dist/features/tabs/composition/useTabsController.js +43 -0
- package/dist/features/tabs/composition/useTabsController.js.map +1 -0
- package/dist/features/tabs/public.d.ts +6 -0
- package/dist/features/tabs/public.d.ts.map +1 -0
- package/dist/features/tabs/public.js +5 -0
- package/dist/features/tabs/public.js.map +1 -0
- package/dist/features/tabs/utils/resolveNextTabValue.d.ts +4 -0
- package/dist/features/tabs/utils/resolveNextTabValue.d.ts.map +1 -0
- package/dist/{components/tabs/navigation.js → features/tabs/utils/resolveNextTabValue.js} +10 -13
- package/dist/features/tabs/utils/resolveNextTabValue.js.map +1 -0
- package/dist/features/tabs/utils/resolveTabElementId.d.ts +3 -0
- package/dist/features/tabs/utils/resolveTabElementId.d.ts.map +1 -0
- package/dist/features/tabs/utils/resolveTabElementId.js +10 -0
- package/dist/features/tabs/utils/resolveTabElementId.js.map +1 -0
- package/dist/features/tooltip/adapters/inbound/Tooltip.d.ts +5 -0
- package/dist/features/tooltip/adapters/inbound/Tooltip.d.ts.map +1 -0
- package/dist/features/tooltip/adapters/inbound/Tooltip.js +25 -0
- package/dist/features/tooltip/adapters/inbound/Tooltip.js.map +1 -0
- package/dist/features/tooltip/composition/useTooltipVisibility.d.ts +9 -0
- package/dist/features/tooltip/composition/useTooltipVisibility.d.ts.map +1 -0
- package/dist/features/tooltip/composition/useTooltipVisibility.js +25 -0
- package/dist/features/tooltip/composition/useTooltipVisibility.js.map +1 -0
- package/dist/features/tooltip/public.d.ts +3 -0
- package/dist/features/tooltip/public.d.ts.map +1 -0
- package/dist/features/tooltip/public.js +2 -0
- package/dist/features/tooltip/public.js.map +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/types/bottomSheet.d.ts +3 -0
- package/dist/types/bottomSheet.d.ts.map +1 -1
- package/dist/types/bottomSheet.js.map +1 -1
- package/dist/{components/modal/types.d.ts → types/modal.d.ts} +2 -2
- package/dist/types/modal.d.ts.map +1 -0
- package/dist/types/modal.js +2 -0
- package/dist/types/modal.js.map +1 -0
- package/dist/types/popoverMenu.d.ts +24 -0
- package/dist/types/popoverMenu.d.ts.map +1 -0
- package/dist/types/popoverMenu.js +2 -0
- package/dist/types/popoverMenu.js.map +1 -0
- package/dist/types/tabs.d.ts +43 -0
- package/dist/types/tabs.d.ts.map +1 -0
- package/dist/types/tabs.js +2 -0
- package/dist/types/tabs.js.map +1 -0
- package/dist/{components/tooltip/types.d.ts → types/tooltip.d.ts} +2 -2
- package/dist/types/tooltip.d.ts.map +1 -0
- package/dist/types/tooltip.js +2 -0
- package/dist/types/tooltip.js.map +1 -0
- package/package.json +1 -1
- package/src/features/bottom-sheet/BottomSheetProvider.tsx +13 -8
- package/src/features/bottom-sheet/public.test.ts +23 -0
- package/src/features/bottom-sheet/public.ts +12 -1
- package/src/features/modal/adapters/inbound/Modal.tsx +74 -0
- package/src/features/modal/public.ts +2 -0
- package/src/features/popover-menu/adapters/inbound/PopoverMenu.tsx +201 -0
- package/src/features/popover-menu/composition/usePopoverMenuController.ts +55 -0
- package/src/features/popover-menu/public.test.ts +13 -0
- package/src/features/popover-menu/public.ts +6 -0
- package/src/{components/menu/navigation.test.ts → features/popover-menu/utils/resolveNextMenuIndex.test.ts} +4 -4
- package/src/features/popover-menu/utils/resolveNextMenuIndex.ts +21 -0
- package/src/features/tabs/adapters/inbound/Tab.tsx +67 -0
- package/src/features/tabs/adapters/inbound/TabList.tsx +50 -0
- package/src/{components/tabs → features/tabs/adapters/inbound}/TabPanel.tsx +6 -10
- package/src/features/tabs/adapters/inbound/Tabs.tsx +17 -0
- package/src/features/tabs/composition/TabsContext.tsx +5 -0
- package/src/features/tabs/composition/useTabRegistration.ts +27 -0
- package/src/features/tabs/composition/useTabsContext.ts +13 -0
- package/src/features/tabs/composition/useTabsController.ts +55 -0
- package/src/features/tabs/public.ts +5 -0
- package/src/{components/tabs/navigation.test.ts → features/tabs/utils/resolveNextTabValue.test.ts} +2 -2
- package/src/features/tabs/utils/resolveNextTabValue.ts +24 -0
- package/src/features/tabs/utils/resolveTabElementId.test.ts +19 -0
- package/src/features/tabs/utils/resolveTabElementId.ts +14 -0
- package/src/features/tooltip/adapters/inbound/Tooltip.tsx +56 -0
- package/src/features/tooltip/composition/useTooltipVisibility.ts +29 -0
- package/src/features/tooltip/public.test.ts +11 -0
- package/src/features/tooltip/public.ts +2 -0
- package/src/index.test.ts +12 -5
- package/src/index.ts +12 -8
- package/src/types/bottomSheet.ts +4 -0
- package/src/{components/modal/types.ts → types/modal.ts} +1 -1
- package/src/types/popoverMenu.ts +27 -0
- package/src/types/tabs.ts +51 -0
- package/src/{components/tooltip/types.ts → types/tooltip.ts} +1 -1
- package/dist/components/menu/Menu.d.ts +0 -4
- package/dist/components/menu/Menu.d.ts.map +0 -1
- package/dist/components/menu/Menu.js +0 -152
- package/dist/components/menu/Menu.js.map +0 -1
- package/dist/components/menu/index.d.ts +0 -3
- package/dist/components/menu/index.d.ts.map +0 -1
- package/dist/components/menu/index.js +0 -2
- package/dist/components/menu/index.js.map +0 -1
- package/dist/components/menu/navigation.d.ts +0 -4
- package/dist/components/menu/navigation.d.ts.map +0 -1
- package/dist/components/menu/navigation.js +0 -25
- package/dist/components/menu/navigation.js.map +0 -1
- package/dist/components/menu/types.d.ts +0 -23
- package/dist/components/menu/types.d.ts.map +0 -1
- package/dist/components/menu/types.js +0 -2
- package/dist/components/menu/types.js.map +0 -1
- package/dist/components/modal/Modal.d.ts.map +0 -1
- package/dist/components/modal/Modal.js +0 -59
- package/dist/components/modal/Modal.js.map +0 -1
- package/dist/components/modal/index.d.ts +0 -3
- package/dist/components/modal/index.d.ts.map +0 -1
- package/dist/components/modal/index.js +0 -2
- package/dist/components/modal/index.js.map +0 -1
- package/dist/components/modal/types.d.ts.map +0 -1
- package/dist/components/modal/types.js +0 -2
- package/dist/components/modal/types.js.map +0 -1
- package/dist/components/tabs/Tab.d.ts +0 -4
- package/dist/components/tabs/Tab.d.ts.map +0 -1
- package/dist/components/tabs/Tab.js +0 -48
- package/dist/components/tabs/Tab.js.map +0 -1
- package/dist/components/tabs/TabList.d.ts +0 -4
- package/dist/components/tabs/TabList.d.ts.map +0 -1
- package/dist/components/tabs/TabList.js +0 -37
- package/dist/components/tabs/TabList.js.map +0 -1
- package/dist/components/tabs/TabPanel.d.ts +0 -4
- package/dist/components/tabs/TabPanel.d.ts.map +0 -1
- package/dist/components/tabs/TabPanel.js +0 -16
- package/dist/components/tabs/TabPanel.js.map +0 -1
- package/dist/components/tabs/Tabs.d.ts +0 -4
- package/dist/components/tabs/Tabs.d.ts.map +0 -1
- package/dist/components/tabs/Tabs.js +0 -54
- package/dist/components/tabs/Tabs.js.map +0 -1
- package/dist/components/tabs/a11y.d.ts +0 -3
- package/dist/components/tabs/a11y.d.ts.map +0 -1
- package/dist/components/tabs/a11y.js +0 -13
- package/dist/components/tabs/a11y.js.map +0 -1
- package/dist/components/tabs/context.d.ts +0 -20
- package/dist/components/tabs/context.d.ts.map +0 -1
- package/dist/components/tabs/context.js.map +0 -1
- package/dist/components/tabs/index.d.ts +0 -6
- package/dist/components/tabs/index.d.ts.map +0 -1
- package/dist/components/tabs/index.js +0 -5
- package/dist/components/tabs/index.js.map +0 -1
- package/dist/components/tabs/navigation.d.ts +0 -3
- package/dist/components/tabs/navigation.d.ts.map +0 -1
- package/dist/components/tabs/navigation.js.map +0 -1
- package/dist/components/tabs/types.d.ts +0 -26
- package/dist/components/tabs/types.d.ts.map +0 -1
- package/dist/components/tabs/types.js +0 -2
- package/dist/components/tabs/types.js.map +0 -1
- package/dist/components/tooltip/Tooltip.d.ts +0 -4
- package/dist/components/tooltip/Tooltip.d.ts.map +0 -1
- package/dist/components/tooltip/Tooltip.js +0 -75
- package/dist/components/tooltip/Tooltip.js.map +0 -1
- package/dist/components/tooltip/index.d.ts +0 -3
- package/dist/components/tooltip/index.d.ts.map +0 -1
- package/dist/components/tooltip/index.js +0 -2
- package/dist/components/tooltip/index.js.map +0 -1
- package/dist/components/tooltip/types.d.ts.map +0 -1
- package/dist/components/tooltip/types.js +0 -2
- package/dist/components/tooltip/types.js.map +0 -1
- package/src/components/menu/Menu.tsx +0 -228
- package/src/components/menu/index.ts +0 -2
- package/src/components/menu/navigation.ts +0 -34
- package/src/components/menu/types.ts +0 -26
- package/src/components/modal/Modal.tsx +0 -96
- package/src/components/modal/index.ts +0 -2
- package/src/components/tabs/Tab.tsx +0 -89
- package/src/components/tabs/TabList.tsx +0 -51
- package/src/components/tabs/Tabs.tsx +0 -67
- package/src/components/tabs/a11y.test.ts +0 -15
- package/src/components/tabs/a11y.ts +0 -15
- package/src/components/tabs/context.tsx +0 -31
- package/src/components/tabs/index.ts +0 -5
- package/src/components/tabs/navigation.ts +0 -32
- package/src/components/tabs/types.ts +0 -30
- package/src/components/tooltip/Tooltip.tsx +0 -117
- package/src/components/tooltip/index.ts +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottomSheet.d.ts","sourceRoot":"","sources":["../../src/types/bottomSheet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;IAC3D,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACtD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB"}
|
|
1
|
+
{"version":3,"file":"bottomSheet.d.ts","sourceRoot":"","sources":["../../src/types/bottomSheet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;IAC3D,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACtD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottomSheet.js","sourceRoot":"","sources":["../../src/types/bottomSheet.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nexport interface BottomSheetPresentOptions {\n content: React.ReactNode;\n snapPoints?: readonly (number | string)[];\n initialIndex?: number;\n enableDynamicSizing?: boolean;\n enablePanDownToClose?: boolean;\n dismissOnBackdropPress?: boolean;\n keyboardBehavior?: 'extend' | 'fillParent' | 'interactive';\n keyboardBlurBehavior?: 'none' | 'restore';\n onIndexChange?: (index: number) => void;\n onDismiss?: () => void;\n}\n\nexport interface BottomSheetController {\n present: (options: BottomSheetPresentOptions) => void;\n dismiss: () => void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bottomSheet.js","sourceRoot":"","sources":["../../src/types/bottomSheet.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nexport type BottomSheetContentMode = 'view' | 'direct';\n\nexport interface BottomSheetPresentOptions {\n content: React.ReactNode;\n contentMode?: BottomSheetContentMode;\n snapPoints?: readonly (number | string)[];\n initialIndex?: number;\n enableDynamicSizing?: boolean;\n maxDynamicContentSize?: number;\n enablePanDownToClose?: boolean;\n dismissOnBackdropPress?: boolean;\n keyboardBehavior?: 'extend' | 'fillParent' | 'interactive';\n keyboardBlurBehavior?: 'none' | 'restore';\n onIndexChange?: (index: number) => void;\n onDismiss?: () => void;\n}\n\nexport interface BottomSheetController {\n present: (options: BottomSheetPresentOptions) => void;\n dismiss: () => void;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import type { InteractionPolicy } from '
|
|
2
|
+
import type { InteractionPolicy } from '../interactionPolicy';
|
|
3
3
|
export interface ModalProps {
|
|
4
4
|
visible: boolean;
|
|
5
5
|
onDismiss?: (() => void) | undefined;
|
|
@@ -8,4 +8,4 @@ export interface ModalProps {
|
|
|
8
8
|
interactionPolicy?: InteractionPolicy;
|
|
9
9
|
testID?: string;
|
|
10
10
|
}
|
|
11
|
-
//# sourceMappingURL=
|
|
11
|
+
//# sourceMappingURL=modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../../src/types/modal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal.js","sourceRoot":"","sources":["../../src/types/modal.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { InteractionPolicy } from '../interactionPolicy';\n\nexport interface ModalProps {\n visible: boolean;\n onDismiss?: (() => void) | undefined;\n children?: React.ReactNode;\n closeOnBackdrop?: boolean;\n interactionPolicy?: InteractionPolicy;\n testID?: string;\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { InteractionPolicy } from '../interactionPolicy';
|
|
3
|
+
import type { PopoverAnchorRenderProps } from './popover';
|
|
4
|
+
export type PopoverMenuActionIntent = 'default' | 'danger';
|
|
5
|
+
export interface PopoverMenuAction {
|
|
6
|
+
id: string;
|
|
7
|
+
title: React.ReactNode;
|
|
8
|
+
description?: React.ReactNode;
|
|
9
|
+
leading?: React.ReactNode;
|
|
10
|
+
trailing?: React.ReactNode;
|
|
11
|
+
intent?: PopoverMenuActionIntent;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
selected?: boolean;
|
|
14
|
+
activate?: () => void;
|
|
15
|
+
}
|
|
16
|
+
export interface PopoverMenuProps {
|
|
17
|
+
trigger: (controls: PopoverAnchorRenderProps) => React.ReactNode;
|
|
18
|
+
actions: readonly PopoverMenuAction[];
|
|
19
|
+
dismiss?: () => void;
|
|
20
|
+
closeOnSelect?: boolean;
|
|
21
|
+
interactionPolicy?: InteractionPolicy;
|
|
22
|
+
testID?: string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=popoverMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popoverMenu.d.ts","sourceRoot":"","sources":["../../src/types/popoverMenu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE1D,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,KAAK,CAAC,SAAS,CAAC;IACjE,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popoverMenu.js","sourceRoot":"","sources":["../../src/types/popoverMenu.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { InteractionPolicy } from '../interactionPolicy';\nimport type { PopoverAnchorRenderProps } from './popover';\n\nexport type PopoverMenuActionIntent = 'default' | 'danger';\n\nexport interface PopoverMenuAction {\n id: string;\n title: React.ReactNode;\n description?: React.ReactNode;\n leading?: React.ReactNode;\n trailing?: React.ReactNode;\n intent?: PopoverMenuActionIntent;\n disabled?: boolean;\n selected?: boolean;\n activate?: () => void;\n}\n\nexport interface PopoverMenuProps {\n trigger: (controls: PopoverAnchorRenderProps) => React.ReactNode;\n actions: readonly PopoverMenuAction[];\n dismiss?: () => void;\n closeOnSelect?: boolean;\n interactionPolicy?: InteractionPolicy;\n testID?: string;\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { InteractionPolicy } from '../interactionPolicy';
|
|
3
|
+
export interface TabsProps {
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
value?: string;
|
|
6
|
+
defaultValue?: string;
|
|
7
|
+
onValueChange?: ((value: string) => void) | undefined;
|
|
8
|
+
testID?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TabListProps {
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
testID?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TabProps {
|
|
15
|
+
value: string;
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
interactionPolicy?: InteractionPolicy;
|
|
19
|
+
testID?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface TabPanelProps {
|
|
22
|
+
value: string;
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
testID?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface TabRegistration {
|
|
27
|
+
disabled: boolean;
|
|
28
|
+
focus: () => void;
|
|
29
|
+
value: string;
|
|
30
|
+
}
|
|
31
|
+
export interface TabsContextValue {
|
|
32
|
+
activeValue: string | undefined;
|
|
33
|
+
focusedValue: string | undefined;
|
|
34
|
+
getPanelId: (value: string) => string;
|
|
35
|
+
getTabId: (value: string) => string;
|
|
36
|
+
registerTab: (tab: TabRegistration) => void;
|
|
37
|
+
setActiveValue: (value: string) => void;
|
|
38
|
+
setFocusedValue: (value: string | undefined) => void;
|
|
39
|
+
tabs: readonly TabRegistration[];
|
|
40
|
+
unregisterTab: (value: string) => void;
|
|
41
|
+
}
|
|
42
|
+
export type TabNavigationKey = 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown' | 'Home' | 'End';
|
|
43
|
+
//# sourceMappingURL=tabs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/types/tabs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,WAAW,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACrD,IAAI,EAAE,SAAS,eAAe,EAAE,CAAC;IACjC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,MAAM,MAAM,gBAAgB,GAC1B,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../src/types/tabs.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { InteractionPolicy } from '../interactionPolicy';\n\nexport interface TabsProps {\n children?: React.ReactNode;\n value?: string;\n defaultValue?: string;\n onValueChange?: ((value: string) => void) | undefined;\n testID?: string;\n}\n\nexport interface TabListProps {\n children?: React.ReactNode;\n testID?: string;\n}\n\nexport interface TabProps {\n value: string;\n children?: React.ReactNode;\n disabled?: boolean;\n interactionPolicy?: InteractionPolicy;\n testID?: string;\n}\n\nexport interface TabPanelProps {\n value: string;\n children?: React.ReactNode;\n testID?: string;\n}\n\nexport interface TabRegistration {\n disabled: boolean;\n focus: () => void;\n value: string;\n}\n\nexport interface TabsContextValue {\n activeValue: string | undefined;\n focusedValue: string | undefined;\n getPanelId: (value: string) => string;\n getTabId: (value: string) => string;\n registerTab: (tab: TabRegistration) => void;\n setActiveValue: (value: string) => void;\n setFocusedValue: (value: string | undefined) => void;\n tabs: readonly TabRegistration[];\n unregisterTab: (value: string) => void;\n}\n\nexport type TabNavigationKey =\n 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown' | 'Home' | 'End';\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import type { InteractionPolicy } from '
|
|
2
|
+
import type { InteractionPolicy } from '../interactionPolicy';
|
|
3
3
|
export interface TooltipProps {
|
|
4
4
|
children?: React.ReactNode;
|
|
5
5
|
content?: React.ReactNode;
|
|
@@ -8,4 +8,4 @@ export interface TooltipProps {
|
|
|
8
8
|
placement?: 'top' | 'bottom';
|
|
9
9
|
testID?: string;
|
|
10
10
|
}
|
|
11
|
-
//# sourceMappingURL=
|
|
11
|
+
//# sourceMappingURL=tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../src/types/tooltip.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../src/types/tooltip.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { InteractionPolicy } from '../interactionPolicy';\n\nexport interface TooltipProps {\n children?: React.ReactNode;\n content?: React.ReactNode;\n delay?: number;\n interactionPolicy?: InteractionPolicy;\n placement?: 'top' | 'bottom';\n testID?: string;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -77,10 +77,7 @@ function BottomSheetHost({
|
|
|
77
77
|
presentedRef,
|
|
78
78
|
}: BottomSheetHostProps) {
|
|
79
79
|
React.useEffect(() => {
|
|
80
|
-
if (!activeRequest || presentedRef.current)
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
80
|
+
if (!activeRequest || presentedRef.current) return;
|
|
84
81
|
presentedRef.current = true;
|
|
85
82
|
modalRef.current?.present();
|
|
86
83
|
}, [activeRequest, modalRef, presentedRef]);
|
|
@@ -91,9 +88,7 @@ function BottomSheetHost({
|
|
|
91
88
|
[activeRequest?.snapPoints],
|
|
92
89
|
);
|
|
93
90
|
|
|
94
|
-
if (!activeRequest)
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
91
|
+
if (!activeRequest) return null;
|
|
97
92
|
|
|
98
93
|
return (
|
|
99
94
|
<BottomSheetModal
|
|
@@ -104,15 +99,25 @@ function BottomSheetHost({
|
|
|
104
99
|
index={activeRequest.initialIndex ?? 0}
|
|
105
100
|
keyboardBehavior={activeRequest.keyboardBehavior ?? 'interactive'}
|
|
106
101
|
keyboardBlurBehavior={activeRequest.keyboardBlurBehavior ?? 'restore'}
|
|
102
|
+
maxDynamicContentSize={activeRequest.maxDynamicContentSize}
|
|
107
103
|
onChange={activeRequest.onIndexChange}
|
|
108
104
|
onDismiss={handleDismiss}
|
|
109
105
|
snapPoints={snapPoints}
|
|
110
106
|
>
|
|
111
|
-
|
|
107
|
+
{renderBottomSheetContent(activeRequest)}
|
|
112
108
|
</BottomSheetModal>
|
|
113
109
|
);
|
|
114
110
|
}
|
|
115
111
|
|
|
112
|
+
/*** Renders default content in BottomSheetView and direct content without an extra wrapper. */
|
|
113
|
+
function renderBottomSheetContent(activeRequest: BottomSheetPresentOptions): React.ReactNode {
|
|
114
|
+
return activeRequest.contentMode === 'direct' ? (
|
|
115
|
+
activeRequest.content
|
|
116
|
+
) : (
|
|
117
|
+
<BottomSheetView>{activeRequest.content}</BottomSheetView>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
116
121
|
/*** Creates the backdrop renderer for the active bottom-sheet request. */
|
|
117
122
|
function useBottomSheetBackdrop(dismissOnBackdropPress: boolean | undefined) {
|
|
118
123
|
return React.useCallback(
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
test('bottom-sheet boundary exposes Gorhom integrated scrollables and direct content mode', async () => {
|
|
4
|
+
const [publicSource, providerSource, typeSource] = await Promise.all([
|
|
5
|
+
Bun.file('src/features/bottom-sheet/public.ts').text(),
|
|
6
|
+
Bun.file('src/features/bottom-sheet/BottomSheetProvider.tsx').text(),
|
|
7
|
+
Bun.file('src/types/bottomSheet.ts').text(),
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
for (const name of [
|
|
11
|
+
'BottomSheetFlatList',
|
|
12
|
+
'BottomSheetScrollView',
|
|
13
|
+
'BottomSheetSectionList',
|
|
14
|
+
'BottomSheetView',
|
|
15
|
+
'BottomSheetVirtualizedList',
|
|
16
|
+
]) {
|
|
17
|
+
expect(publicSource).toContain(name);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
expect(typeSource).toContain("export type BottomSheetContentMode = 'view' | 'direct';");
|
|
21
|
+
expect(providerSource).toContain("activeRequest.contentMode === 'direct'");
|
|
22
|
+
expect(providerSource).toContain('maxDynamicContentSize={activeRequest.maxDynamicContentSize}');
|
|
23
|
+
});
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type {
|
|
2
|
+
BottomSheetContentMode,
|
|
3
|
+
BottomSheetController,
|
|
4
|
+
BottomSheetPresentOptions,
|
|
5
|
+
} from '../../types/bottomSheet';
|
|
2
6
|
export { BottomSheetProvider } from './BottomSheetProvider';
|
|
3
7
|
export { useBottomSheet } from './useBottomSheet';
|
|
8
|
+
export {
|
|
9
|
+
BottomSheetFlatList,
|
|
10
|
+
BottomSheetScrollView,
|
|
11
|
+
BottomSheetSectionList,
|
|
12
|
+
BottomSheetView,
|
|
13
|
+
BottomSheetVirtualizedList,
|
|
14
|
+
} from '@gorhom/bottom-sheet';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Pressable, StyleSheet, View, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { FocusScope } from '../../../../internal/focus/FocusScope';
|
|
5
|
+
import { Portal } from '../../../../internal/overlay/Portal';
|
|
6
|
+
import { resolvePointerEvents } from '../../../../internal/resolvePointerEvents';
|
|
7
|
+
import { resolveOverlayAnimation } from '../../../../internal/resolvers';
|
|
8
|
+
import { Center } from '../../../../layout';
|
|
9
|
+
import { useTheme } from '../../../../theme/ThemeContext';
|
|
10
|
+
import type { ModalProps } from '../../../../types/modal';
|
|
11
|
+
import { Surface } from '../../../surface/public';
|
|
12
|
+
|
|
13
|
+
const boxNonePointerEvents = resolvePointerEvents('box-none');
|
|
14
|
+
|
|
15
|
+
/*** Renders the generic Surface modal overlay and focus boundary. */
|
|
16
|
+
export function Modal({
|
|
17
|
+
visible,
|
|
18
|
+
onDismiss,
|
|
19
|
+
children,
|
|
20
|
+
closeOnBackdrop = true,
|
|
21
|
+
interactionPolicy = 'enabled',
|
|
22
|
+
testID,
|
|
23
|
+
}: ModalProps) {
|
|
24
|
+
const { theme } = useTheme();
|
|
25
|
+
const animation = resolveOverlayAnimation('modal');
|
|
26
|
+
const passive = interactionPolicy === 'passive';
|
|
27
|
+
const backdropPress = passive || !closeOnBackdrop ? undefined : onDismiss;
|
|
28
|
+
|
|
29
|
+
if (!visible) return null;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Portal layer="modal" visible={visible}>
|
|
33
|
+
<View {...boxNonePointerEvents.props} style={[boxNonePointerEvents.style, styles.fill]}>
|
|
34
|
+
<Pressable
|
|
35
|
+
onPress={backdropPress}
|
|
36
|
+
style={[
|
|
37
|
+
styles.fill,
|
|
38
|
+
resolveBackdropStyle(theme.semantics.neutral.text, animation.backdropOpacity),
|
|
39
|
+
]}
|
|
40
|
+
testID={testID ? `${testID}-backdrop` : undefined}
|
|
41
|
+
/>
|
|
42
|
+
<FocusScope
|
|
43
|
+
active={visible}
|
|
44
|
+
onEscape={passive ? undefined : onDismiss}
|
|
45
|
+
testID={testID ? `${testID}-focus` : undefined}
|
|
46
|
+
>
|
|
47
|
+
<Center p="l" style={styles.center}>
|
|
48
|
+
<View accessible accessibilityViewIsModal style={styles.content}>
|
|
49
|
+
<Surface p="l" style={styles.surface} testID={testID} variant="raised">
|
|
50
|
+
{children}
|
|
51
|
+
</Surface>
|
|
52
|
+
</View>
|
|
53
|
+
</Center>
|
|
54
|
+
</FocusScope>
|
|
55
|
+
</View>
|
|
56
|
+
</Portal>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/*** Resolves the runtime backdrop color and opacity without inline JSX styles. */
|
|
61
|
+
function resolveBackdropStyle(backgroundColor: string, opacity: number): ViewStyle {
|
|
62
|
+
return { backgroundColor, opacity };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
center: { flex: 1 },
|
|
67
|
+
content: { maxWidth: 560, width: '100%' },
|
|
68
|
+
fill: { bottom: 0, left: 0, position: 'absolute', right: 0, top: 0 },
|
|
69
|
+
surface: {
|
|
70
|
+
shadowOffset: { height: 8, width: 0 },
|
|
71
|
+
shadowOpacity: 0.14,
|
|
72
|
+
shadowRadius: 18,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Pressable } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { FocusScope } from '../../../../internal/focus/FocusScope';
|
|
5
|
+
import { useFocusManager } from '../../../../internal/focus/useFocusManager';
|
|
6
|
+
import { Inline } from '../../../../layout';
|
|
7
|
+
import { useTheme } from '../../../../theme/ThemeContext';
|
|
8
|
+
import type { PopoverMenuAction, PopoverMenuProps } from '../../../../types/popoverMenu';
|
|
9
|
+
import { Box, Stack } from '../../../layout/public';
|
|
10
|
+
import { Popover } from '../../../popover/public';
|
|
11
|
+
import { Surface } from '../../../surface/public';
|
|
12
|
+
import { Text } from '../../../typography/public';
|
|
13
|
+
import { usePopoverMenuController } from '../../composition/usePopoverMenuController';
|
|
14
|
+
import { resolveNextMenuIndex } from '../../utils/resolveNextMenuIndex';
|
|
15
|
+
|
|
16
|
+
/*** Presents an anchored action menu using the shared Popover capability. */
|
|
17
|
+
export function PopoverMenu({
|
|
18
|
+
trigger,
|
|
19
|
+
actions,
|
|
20
|
+
dismiss,
|
|
21
|
+
closeOnSelect = true,
|
|
22
|
+
interactionPolicy = 'enabled',
|
|
23
|
+
testID,
|
|
24
|
+
}: PopoverMenuProps) {
|
|
25
|
+
const passive = interactionPolicy === 'passive';
|
|
26
|
+
const controller = usePopoverMenuController({ actions, closeOnSelect, dismiss, passive });
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Popover
|
|
30
|
+
anchor={trigger}
|
|
31
|
+
closeOnOutsidePress
|
|
32
|
+
interactionPolicy={interactionPolicy}
|
|
33
|
+
onOpenChange={controller.handleOpenChange}
|
|
34
|
+
open={controller.open}
|
|
35
|
+
placement="bottom-start"
|
|
36
|
+
testID={testID}
|
|
37
|
+
>
|
|
38
|
+
<PopoverMenuContent
|
|
39
|
+
actions={actions}
|
|
40
|
+
activeIndex={controller.activeIndex}
|
|
41
|
+
activateAction={controller.activateAction}
|
|
42
|
+
close={controller.close}
|
|
43
|
+
passive={passive}
|
|
44
|
+
setActiveIndex={controller.setActiveIndex}
|
|
45
|
+
testID={testID}
|
|
46
|
+
/>
|
|
47
|
+
</Popover>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface PopoverMenuContentProps {
|
|
52
|
+
actions: readonly PopoverMenuAction[];
|
|
53
|
+
activeIndex: number;
|
|
54
|
+
activateAction: (action: PopoverMenuAction) => void;
|
|
55
|
+
close: () => void;
|
|
56
|
+
passive: boolean;
|
|
57
|
+
setActiveIndex: React.Dispatch<React.SetStateAction<number>>;
|
|
58
|
+
testID?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*** Renders menu semantics and keyboard navigation inside an open Popover. */
|
|
62
|
+
function PopoverMenuContent({
|
|
63
|
+
actions,
|
|
64
|
+
activeIndex,
|
|
65
|
+
activateAction,
|
|
66
|
+
close,
|
|
67
|
+
passive,
|
|
68
|
+
setActiveIndex,
|
|
69
|
+
testID,
|
|
70
|
+
}: PopoverMenuContentProps) {
|
|
71
|
+
usePopoverMenuKeyboard({ actions, activeIndex, activateAction, close, passive, setActiveIndex });
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<FocusScope active onEscape={passive ? undefined : close}>
|
|
75
|
+
<Surface accessibilityRole="menu" p="xs" testID={testID} variant="raised">
|
|
76
|
+
{actions.map((action, index) => (
|
|
77
|
+
<PopoverMenuItem
|
|
78
|
+
action={action}
|
|
79
|
+
active={index === activeIndex}
|
|
80
|
+
activateAction={activateAction}
|
|
81
|
+
key={action.id}
|
|
82
|
+
passive={passive}
|
|
83
|
+
testID={testID}
|
|
84
|
+
/>
|
|
85
|
+
))}
|
|
86
|
+
</Surface>
|
|
87
|
+
</FocusScope>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/*** Subscribes an open PopoverMenu to standard menu keyboard controls. */
|
|
92
|
+
function usePopoverMenuKeyboard({
|
|
93
|
+
actions,
|
|
94
|
+
activeIndex,
|
|
95
|
+
activateAction,
|
|
96
|
+
close,
|
|
97
|
+
passive,
|
|
98
|
+
setActiveIndex,
|
|
99
|
+
}: Omit<PopoverMenuContentProps, 'testID'>) {
|
|
100
|
+
const { bindKeydown } = useFocusManager();
|
|
101
|
+
|
|
102
|
+
React.useEffect(
|
|
103
|
+
() =>
|
|
104
|
+
bindKeydown((event) => {
|
|
105
|
+
if (passive) return;
|
|
106
|
+
|
|
107
|
+
const { key } = event;
|
|
108
|
+
if (key === undefined) return;
|
|
109
|
+
|
|
110
|
+
if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(key)) {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
setActiveIndex((current) => resolveNextMenuIndex(actions, current, key));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (key === 'Enter') {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
const action = actions.at(activeIndex);
|
|
118
|
+
if (action) activateAction(action);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (key === 'Escape') {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
close();
|
|
124
|
+
}
|
|
125
|
+
}),
|
|
126
|
+
[actions, activeIndex, activateAction, bindKeydown, close, passive, setActiveIndex],
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/*** Renders one accessible PopoverMenu action row. */
|
|
131
|
+
function PopoverMenuItem({
|
|
132
|
+
action,
|
|
133
|
+
active,
|
|
134
|
+
activateAction,
|
|
135
|
+
passive,
|
|
136
|
+
testID,
|
|
137
|
+
}: {
|
|
138
|
+
action: PopoverMenuAction;
|
|
139
|
+
active: boolean;
|
|
140
|
+
activateAction: (action: PopoverMenuAction) => void;
|
|
141
|
+
passive: boolean;
|
|
142
|
+
testID?: string;
|
|
143
|
+
}) {
|
|
144
|
+
const { theme } = useTheme();
|
|
145
|
+
const selected = action.selected ?? active;
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<Pressable
|
|
149
|
+
accessibilityRole="menuitem"
|
|
150
|
+
accessibilityState={{ disabled: action.disabled, selected }}
|
|
151
|
+
disabled={action.disabled}
|
|
152
|
+
onPress={passive ? undefined : () => activateAction(action)}
|
|
153
|
+
testID={testID ? `${testID}-item-${action.id}` : undefined}
|
|
154
|
+
>
|
|
155
|
+
<Box
|
|
156
|
+
bg={selected ? theme.semantics.action.neutral.softBg : 'transparent'}
|
|
157
|
+
opacity={action.disabled ? 0.56 : 1}
|
|
158
|
+
px="m"
|
|
159
|
+
py="s"
|
|
160
|
+
radius="s"
|
|
161
|
+
>
|
|
162
|
+
<PopoverMenuItemContent action={action} active={active} />
|
|
163
|
+
</Box>
|
|
164
|
+
</Pressable>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/*** Renders the leading, textual, and trailing content of one menu action. */
|
|
169
|
+
function PopoverMenuItemContent({
|
|
170
|
+
action,
|
|
171
|
+
active,
|
|
172
|
+
}: {
|
|
173
|
+
action: PopoverMenuAction;
|
|
174
|
+
active: boolean;
|
|
175
|
+
}) {
|
|
176
|
+
const titleColor =
|
|
177
|
+
action.intent === 'danger' ? 'danger' : active || action.selected ? 'neutral' : undefined;
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<Inline align="center" gap="s" justify="space-between" wrap="nowrap">
|
|
181
|
+
{action.leading ? <Box>{action.leading}</Box> : null}
|
|
182
|
+
<Box flex={1}>
|
|
183
|
+
<Stack gap="xxs">
|
|
184
|
+
<Text
|
|
185
|
+
color={titleColor}
|
|
186
|
+
variant="bodySmall"
|
|
187
|
+
weight={action.selected ? 'semiBold' : 'medium'}
|
|
188
|
+
>
|
|
189
|
+
{action.title}
|
|
190
|
+
</Text>
|
|
191
|
+
{action.description ? (
|
|
192
|
+
<Text emphasis="muted" variant="caption">
|
|
193
|
+
{action.description}
|
|
194
|
+
</Text>
|
|
195
|
+
) : null}
|
|
196
|
+
</Stack>
|
|
197
|
+
</Box>
|
|
198
|
+
{action.trailing ? <Box>{action.trailing}</Box> : null}
|
|
199
|
+
</Inline>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { PopoverMenuAction } from '../../../types/popoverMenu';
|
|
4
|
+
|
|
5
|
+
interface PopoverMenuControllerOptions {
|
|
6
|
+
actions: readonly PopoverMenuAction[];
|
|
7
|
+
closeOnSelect: boolean;
|
|
8
|
+
dismiss?: () => void;
|
|
9
|
+
passive: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/*** Owns PopoverMenu visibility, active action state, and selection behavior. */
|
|
13
|
+
export function usePopoverMenuController({
|
|
14
|
+
actions,
|
|
15
|
+
closeOnSelect,
|
|
16
|
+
dismiss,
|
|
17
|
+
passive,
|
|
18
|
+
}: PopoverMenuControllerOptions) {
|
|
19
|
+
const [open, setOpen] = React.useState(false);
|
|
20
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
21
|
+
|
|
22
|
+
const handleOpenChange = React.useCallback(
|
|
23
|
+
(nextOpen: boolean) => {
|
|
24
|
+
if (nextOpen) setActiveIndex(resolveInitialIndex(actions));
|
|
25
|
+
if (open && !nextOpen) dismiss?.();
|
|
26
|
+
setOpen(nextOpen);
|
|
27
|
+
},
|
|
28
|
+
[actions, dismiss, open],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const close = React.useCallback(() => handleOpenChange(false), [handleOpenChange]);
|
|
32
|
+
const activateAction = React.useCallback(
|
|
33
|
+
(action: PopoverMenuAction) => {
|
|
34
|
+
if (action.disabled || passive) return;
|
|
35
|
+
action.activate?.();
|
|
36
|
+
if (closeOnSelect) close();
|
|
37
|
+
},
|
|
38
|
+
[close, closeOnSelect, passive],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
activeIndex,
|
|
43
|
+
activateAction,
|
|
44
|
+
close,
|
|
45
|
+
handleOpenChange,
|
|
46
|
+
open,
|
|
47
|
+
setActiveIndex,
|
|
48
|
+
} as const;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*** Returns the first enabled action index for a newly opened menu. */
|
|
52
|
+
function resolveInitialIndex(actions: readonly PopoverMenuAction[]): number {
|
|
53
|
+
const index = actions.findIndex((action) => !action.disabled);
|
|
54
|
+
return index === -1 ? 0 : index;
|
|
55
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
test('PopoverMenu composes Popover instead of owning anchor and portal mechanics', async () => {
|
|
4
|
+
const source = await Bun.file(
|
|
5
|
+
'src/features/popover-menu/adapters/inbound/PopoverMenu.tsx',
|
|
6
|
+
).text();
|
|
7
|
+
|
|
8
|
+
expect(source).toContain("import { Popover } from '../../../popover/public';");
|
|
9
|
+
expect(source).toContain('<Popover');
|
|
10
|
+
expect(source).not.toContain('measureInWindow');
|
|
11
|
+
expect(source).not.toContain('internal/overlay');
|
|
12
|
+
expect(source).not.toContain('<Portal');
|
|
13
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import { resolveNextMenuIndex } from './
|
|
3
|
+
import { resolveNextMenuIndex } from './resolveNextMenuIndex';
|
|
4
4
|
|
|
5
5
|
describe('resolveNextMenuIndex', () => {
|
|
6
|
-
const items = [{}, { disabled: true }, {}, {}];
|
|
6
|
+
const items = [{}, { disabled: true }, {}, {}] as const;
|
|
7
7
|
|
|
8
|
-
it('cycles down and up through
|
|
8
|
+
it('cycles down and up through enabled actions', () => {
|
|
9
9
|
expect(resolveNextMenuIndex(items, 0, 'ArrowDown')).toBe(2);
|
|
10
10
|
expect(resolveNextMenuIndex(items, 0, 'ArrowUp')).toBe(3);
|
|
11
11
|
});
|
|
@@ -15,7 +15,7 @@ describe('resolveNextMenuIndex', () => {
|
|
|
15
15
|
expect(resolveNextMenuIndex(items, 0, 'End')).toBe(3);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
it('returns -1 when all
|
|
18
|
+
it('returns -1 when all actions are disabled', () => {
|
|
19
19
|
expect(resolveNextMenuIndex([{ disabled: true }], 0, 'ArrowDown')).toBe(-1);
|
|
20
20
|
});
|
|
21
21
|
});
|