@fluentui-react-native/callout 0.28.6 → 0.29.4

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 (69) hide show
  1. package/.cache/tsconfig.tsbuildinfo +1 -1
  2. package/CHANGELOG.md +62 -0
  3. package/FRNCallout.podspec +1 -1
  4. package/NuGet.config +10 -0
  5. package/SPEC.md +46 -0
  6. package/lib/Callout.d.ts +2 -5
  7. package/lib/Callout.d.ts.map +1 -1
  8. package/lib/Callout.js +57 -58
  9. package/lib/Callout.js.map +1 -1
  10. package/lib/Callout.types.d.ts +51 -152
  11. package/lib/Callout.types.d.ts.map +1 -1
  12. package/lib/Callout.types.js.map +1 -1
  13. package/lib/CalloutNativeComponent.d.ts +7 -3
  14. package/lib/CalloutNativeComponent.d.ts.map +1 -1
  15. package/lib/CalloutNativeComponent.js +3 -1
  16. package/lib/CalloutNativeComponent.js.map +1 -1
  17. package/lib/__tests__/Callout.test.js +94 -10
  18. package/lib/__tests__/Callout.test.js.map +1 -1
  19. package/lib/__tests__/Callout.types.test.d.ts +2 -0
  20. package/lib/__tests__/Callout.types.test.d.ts.map +1 -0
  21. package/lib/__tests__/Callout.types.test.js +21 -0
  22. package/lib/__tests__/Callout.types.test.js.map +1 -0
  23. package/lib/index.d.ts +1 -2
  24. package/lib/index.d.ts.map +1 -1
  25. package/macos/CalloutManager.swift +1 -1
  26. package/macos/CalloutView.swift +49 -17
  27. package/macos/FRNCalloutManager.m +11 -11
  28. package/macos/RCTCalloutComponentView.h +12 -0
  29. package/macos/RCTCalloutComponentView.mm +209 -0
  30. package/package.json +26 -10
  31. package/react-native.config.cjs +15 -0
  32. package/src/Callout.stories.tsx +276 -0
  33. package/src/Callout.tsx +98 -64
  34. package/src/Callout.types.ts +50 -172
  35. package/src/CalloutNativeComponent.ts +10 -5
  36. package/src/__tests__/Callout.test.tsx +152 -8
  37. package/src/__tests__/Callout.types.test.ts +30 -0
  38. package/src/__tests__/__snapshots__/Callout.test.tsx.snap +8 -10
  39. package/src/index.ts +4 -4
  40. package/tsconfig.json +2 -13
  41. package/windows/Callout/Callout.cpp +420 -0
  42. package/windows/Callout/Callout.def +3 -0
  43. package/windows/Callout/Callout.h +25 -0
  44. package/windows/Callout/Callout.vcxproj +152 -0
  45. package/windows/Callout/ReactPackageProvider.cpp +25 -0
  46. package/windows/Callout/ReactPackageProvider.h +25 -0
  47. package/windows/Callout/ReactPackageProvider.idl +12 -0
  48. package/windows/Callout/codegen/.clang-format +2 -0
  49. package/windows/Callout/codegen/react/components/FRNCalloutSpec/Callout.g.h +375 -0
  50. package/windows/Callout/packages.lock.json +151 -0
  51. package/windows/Callout/pch.cpp +1 -0
  52. package/windows/Callout/pch.h +28 -0
  53. package/windows/ExperimentalFeatures.props +33 -0
  54. package/windows/NuGet.Config +13 -0
  55. package/lib/Callout.settings.d.ts +0 -4
  56. package/lib/Callout.settings.d.ts.map +0 -1
  57. package/lib/Callout.settings.js +0 -16
  58. package/lib/Callout.settings.js.map +0 -1
  59. package/lib/Callout.settings.macos.d.ts +0 -4
  60. package/lib/Callout.settings.macos.d.ts.map +0 -1
  61. package/lib/Callout.settings.macos.js +0 -19
  62. package/lib/Callout.settings.macos.js.map +0 -1
  63. package/lib/CalloutNativeComponent.macos.d.ts +0 -45
  64. package/lib/CalloutNativeComponent.macos.d.ts.map +0 -1
  65. package/lib/CalloutNativeComponent.macos.js +0 -7
  66. package/lib/CalloutNativeComponent.macos.js.map +0 -1
  67. package/src/Callout.settings.macos.ts +0 -22
  68. package/src/Callout.settings.ts +0 -19
  69. package/src/CalloutNativeComponent.macos.ts +0 -69
@@ -1,12 +1,96 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { act } from 'react';
3
- import * as renderer from 'react-test-renderer';
4
- import { Callout } from '..';
5
- it('Callout default props', () => {
6
- let component;
7
- act(() => {
8
- component = renderer.create(_jsx(Callout, {}));
9
- });
10
- expect(component.toJSON()).toMatchSnapshot();
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@fluentui-react-native/framework-base/jsx-runtime";
2
+ /** @jsxImportSource @fluentui-react-native/framework-base */
3
+ import * as React from 'react';
4
+ import { StyleSheet, Text, View } from 'react-native';
5
+ import { act, render } from '@testing-library/react-native';
6
+ import { isPhasedComponent, useSlot } from '@fluentui-react-native/framework-base';
7
+ import { Callout } from '../Callout';
8
+ import { Commands } from '../CalloutNativeComponent';
9
+ jest.mock('react-native/Libraries/ReactNative/RendererProxy', () => ({
10
+ ...jest.requireActual('react-native/Libraries/ReactNative/RendererProxy'),
11
+ findNodeHandle: jest.fn(() => 42),
12
+ }));
13
+ function CalloutSlotConsumer({ callout }) {
14
+ const CalloutSlot = useSlot(Callout, callout);
15
+ return (_jsx(CalloutSlot, { accessibilityHint: "rendered as a slot", testID: "slotted-callout", children: _jsx(Text, { children: "Slot content" }) }));
16
+ }
17
+ describe('Callout', () => {
18
+ afterEach(() => {
19
+ jest.restoreAllMocks();
20
+ });
21
+ it('uses phased rendering and applies native-safe default styling', async () => {
22
+ expect(isPhasedComponent(Callout)).toBe(true);
23
+ const component = await render(_jsx(Callout, { testID: "callout" }));
24
+ const callout = component.getByTestId('callout');
25
+ expect(callout.type).toBe('RCTCallout');
26
+ expect(StyleSheet.flatten(callout.props.style)).toMatchSnapshot();
27
+ });
28
+ it('translates caller-supplied appearance values and preserves user style precedence', async () => {
29
+ const component = await render(_jsx(Callout, { backgroundColor: "red", borderColor: "green", borderRadius: 8, borderWidth: 2, maxHeight: 240, maxWidth: "80%", minWidth: 120, style: { backgroundColor: 'blue', opacity: 0.5 }, testID: "callout" }));
30
+ const callout = component.getByTestId('callout');
31
+ expect(StyleSheet.flatten(callout.props.style)).toEqual({
32
+ position: 'absolute',
33
+ backgroundColor: 'blue',
34
+ borderColor: 'green',
35
+ borderRadius: 8,
36
+ borderWidth: 2,
37
+ maxHeight: 240,
38
+ maxWidth: '80%',
39
+ minWidth: 120,
40
+ opacity: 0.5,
41
+ });
42
+ expect(callout.props.backgroundColor).toBeUndefined();
43
+ expect(callout.props.maxHeight).toBe(240);
44
+ expect(callout.props.maxWidth).toBeUndefined();
45
+ expect(callout.props.minWidth).toBe(120);
46
+ });
47
+ it('forwards native behavior, accessibility, callbacks, and children', async () => {
48
+ const onDismiss = jest.fn();
49
+ const onShow = jest.fn();
50
+ const anchorRect = { screenX: 10, screenY: 20, width: 30, height: 40 };
51
+ const component = await render(_jsx(Callout, { accessibilityLabel: "Formatting options", anchorRect: anchorRect, beakWidth: 12, directionalHint: "rightTopEdge", dismissBehaviors: ['preventDismissOnClickOutside'], gapSpace: 4, minPadding: 8, onDismiss: onDismiss, onShow: onShow, target: "editor-anchor", testID: "callout", children: _jsx(Text, { children: "Callout content" }) }));
52
+ const callout = component.getByTestId('callout');
53
+ expect(callout.props).toMatchObject({
54
+ accessibilityLabel: 'Formatting options',
55
+ anchorRect,
56
+ beakWidth: 12,
57
+ directionalHint: 'rightTopEdge',
58
+ dismissBehaviors: ['preventDismissOnClickOutside'],
59
+ gapSpace: 4,
60
+ minPadding: 8,
61
+ onDismiss,
62
+ onShow,
63
+ target: 'editor-anchor',
64
+ });
65
+ expect(component.getByText('Callout content')).toBeTruthy();
66
+ });
67
+ it('resolves a React target ref to its native handle', async () => {
68
+ const target = React.createRef();
69
+ const component = await render(_jsxs(_Fragment, { children: [_jsx(View, { ref: target }), _jsx(Callout, { target: target, testID: "callout" })] }));
70
+ expect(target.current).not.toBeNull();
71
+ expect(component.getByTestId('callout').props.target).toBe(42);
72
+ });
73
+ it('exposes native window commands through componentRef', async () => {
74
+ const focusWindow = jest.spyOn(Commands, 'focusWindow').mockImplementation(() => undefined);
75
+ const blurWindow = jest.spyOn(Commands, 'blurWindow').mockImplementation(() => undefined);
76
+ const componentRef = React.createRef();
77
+ await render(_jsx(Callout, { componentRef: componentRef, testID: "callout" }));
78
+ await act(async () => {
79
+ componentRef.current?.focusWindow();
80
+ componentRef.current?.blurWindow();
81
+ });
82
+ expect(focusWindow).toHaveBeenCalledTimes(1);
83
+ expect(blurWindow).toHaveBeenCalledTimes(1);
84
+ });
85
+ it('can be consumed as a slot with merged render props', async () => {
86
+ const component = await render(_jsx(CalloutSlotConsumer, { callout: { accessibilityLabel: 'Slotted Callout', target: 'slot-anchor' } }));
87
+ const callout = component.getByTestId('slotted-callout');
88
+ expect(callout.props).toMatchObject({
89
+ accessibilityHint: 'rendered as a slot',
90
+ accessibilityLabel: 'Slotted Callout',
91
+ target: 'slot-anchor',
92
+ });
93
+ expect(component.getByText('Slot content')).toBeTruthy();
94
+ });
11
95
  });
12
96
  //# sourceMappingURL=Callout.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Callout.test.js","sourceRoot":"","sources":["../../src/__tests__/Callout.test.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;IAC/B,IAAI,SAAqC,CAAC;IAC1C,GAAG,CAAC,GAAG,EAAE;QACP,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAC,OAAO,KAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,SAAU,CAAC,MAAM,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC;AAChD,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Callout.test.js","sourceRoot":"","sources":["../../src/__tests__/Callout.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Callout.types.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Callout.types.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/Callout.types.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import * as React from 'react';
3
+ const componentRef = React.createRef();
4
+ const target = React.createRef();
5
+ const props = {
6
+ accessibilityLabel: 'Formatting options',
7
+ componentRef,
8
+ directionalHint: 'bottomCenter',
9
+ target,
10
+ };
11
+ const calloutSlot = props;
12
+ const legacyProps = props;
13
+ // @ts-expect-error Callout targets must be a ref or registered native anchor identifier.
14
+ const invalidTarget = { target: 42 };
15
+ describe('Callout types', () => {
16
+ it('accepts public props as a slot contract', () => {
17
+ expect(calloutSlot).toBeDefined();
18
+ expect(legacyProps).toBeDefined();
19
+ });
20
+ });
21
+ //# sourceMappingURL=Callout.types.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Callout.types.test.js","sourceRoot":"","sources":["../../src/__tests__/Callout.types.test.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,EAAiB,CAAC;AACtD,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAmB,CAAC;AAElD,MAAM,KAAK,GAAiB;IAC1B,kBAAkB,EAAE,oBAAoB;IACxC,YAAY;IACZ,eAAe,EAAE,cAAc;IAC/B,MAAM;CACP,CAAC;AAEF,MAAM,WAAW,GAA6B,KAAK,CAAC;AACpD,MAAM,WAAW,GAAkB,KAAK,CAAC;AAEzC,yFAAyF;AACzF,MAAM,aAAa,GAAiB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAEnD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/lib/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export { calloutName } from './Callout.types';
2
- export type { DirectionalHint, DismissBehaviors, ICalloutProps, ICalloutRenderData, ICalloutSlotProps, ICalloutTokens, ICalloutType, RestoreFocusEvent, } from './Callout.types';
3
- export type { CalloutNativeCommands } from './CalloutNativeComponent';
2
+ export type { CalloutHandle, CalloutNativeCommands, CalloutProps, CalloutTokens, DirectionalHint, DismissBehaviors, ICalloutProps, ICalloutTokens, RestoreFocusEvent, } from './Callout.types';
4
3
  export { Callout } from './Callout';
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
@@ -4,7 +4,7 @@ import Foundation
4
4
  import React
5
5
  #endif // USE_REACT_AS_MODULE
6
6
 
7
- @objc(FRNCalloutManager)
7
+ @objc(RCTCalloutManager)
8
8
  class CalloutManager: RCTViewManager {
9
9
  override func view()->NSView! {
10
10
  return CalloutView(bridge: bridge)
@@ -46,17 +46,11 @@ open class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
46
46
 
47
47
  public weak var bridge: RCTBridge?
48
48
 
49
- private init() {
50
- super.init(frame: .zero)
51
- }
49
+ @objc public override init(frame frameRect: NSRect) {
50
+ super.init(frame: frameRect)
52
51
 
53
- public required init?(coder: NSCoder) {
54
- preconditionFailure()
55
- }
56
-
57
- convenience init(bridge: RCTBridge) {
58
- self.init()
59
- self.bridge = bridge
52
+ backgroundColor = .clear
53
+ borderColor = .clear
60
54
 
61
55
  // Listens for mouse clicks in the main menu bar while callout is shown
62
56
  NotificationCenter.default.addObserver(
@@ -66,6 +60,15 @@ open class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
66
60
  object: nil)
67
61
  }
68
62
 
63
+ public required init?(coder: NSCoder) {
64
+ preconditionFailure()
65
+ }
66
+
67
+ convenience init(bridge: RCTBridge) {
68
+ self.init(frame: .zero)
69
+ self.bridge = bridge
70
+ }
71
+
69
72
  public override func viewDidMoveToWindow() {
70
73
  super.viewDidMoveToWindow()
71
74
  if (window != nil) {
@@ -84,6 +87,36 @@ open class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
84
87
  }
85
88
  }
86
89
 
90
+ // MARK: Fabric interface
91
+
92
+ @objc public var contentProxyView: NSView {
93
+ return proxyView
94
+ }
95
+
96
+ @objc public func setAnchorView(_ view: NSView?) {
97
+ anchorView = view
98
+ updateCalloutFrameToAnchor()
99
+ }
100
+
101
+ @objc public func mountContentSubview(_ subview: NSView, at index: Int) {
102
+ if index >= proxyView.subviews.count {
103
+ proxyView.addSubview(subview)
104
+ } else {
105
+ proxyView.addSubview(subview, positioned: .below, relativeTo: proxyView.subviews[index])
106
+ }
107
+ }
108
+
109
+ @objc public func unmountContentSubview(_ subview: NSView) {
110
+ if subview.superview == proxyView {
111
+ subview.removeFromSuperview()
112
+ }
113
+ }
114
+
115
+ @objc public func updateContentSize(_ size: NSSize) {
116
+ proxyView.frame = NSRect(origin: .zero, size: size)
117
+ updateCalloutFrameToAnchor()
118
+ }
119
+
87
120
  // MARK: RCTComponent Overrides
88
121
 
89
122
  public override func insertReactSubview(_ subview: NSView!, at atIndex: Int) {
@@ -362,10 +395,7 @@ open class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
362
395
 
363
396
  private func onDismissCallout() {
364
397
  if let onDismiss = onDismiss {
365
- guard let reactTag = reactTag else {
366
- preconditionFailure("React Tag missing")
367
- }
368
- let event: [AnyHashable: Any] = ["target": reactTag]
398
+ let event: [AnyHashable: Any] = ["target": reactTag ?? NSNumber(value: 0)]
369
399
  onDismiss(event)
370
400
  }
371
401
  }
@@ -393,10 +423,12 @@ open class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
393
423
  * We can't directly call touchHandler.attach(to:) because `visualEffectView` is not an RCTUIView.
394
424
  * We get around this limitation by just replicating what `attach` did internally: add a gestureRecognizer.
395
425
  */
396
- guard let touchHandler = RCTTouchHandler(bridge: bridge) else {
397
- preconditionFailure("Callout could not create RCTTouchHandler")
426
+ if let bridge = bridge {
427
+ guard let touchHandler = RCTTouchHandler(bridge: bridge) else {
428
+ preconditionFailure("Callout could not create RCTTouchHandler")
429
+ }
430
+ visualEffectView.addGestureRecognizer(touchHandler)
398
431
  }
399
- visualEffectView.addGestureRecognizer(touchHandler)
400
432
 
401
433
  return visualEffectView
402
434
  }()
@@ -16,28 +16,28 @@
16
16
  return NSMakeRect(x, y, width, height);
17
17
  }
18
18
 
19
- // Collapse the directional hint options to the 4 NSRectEdge options
19
+ // Collapse the directional hint options to the 4 NSRectEdge options.
20
+ // CalloutView uses MinY for placement above the anchor and MaxY for placement below it.
20
21
  RCT_ENUM_CONVERTER(NSRectEdge, (@{
21
22
  @"leftTopEdge": @(NSRectEdgeMinX),
22
23
  @"leftCenter": @(NSRectEdgeMinX),
23
24
  @"leftBottomEdge": @(NSRectEdgeMinX),
24
- @"topLeftEdge": @(NSRectEdgeMaxY),
25
- @"topAutoEdge": @(NSRectEdgeMaxY),
26
- @"topCenter": @(NSRectEdgeMaxY),
27
- @"topRightEdge": @(NSRectEdgeMaxY),
25
+ @"topLeftEdge": @(NSRectEdgeMinY),
26
+ @"topAutoEdge": @(NSRectEdgeMinY),
27
+ @"topCenter": @(NSRectEdgeMinY),
28
+ @"topRightEdge": @(NSRectEdgeMinY),
28
29
  @"rightTopEdge": @(NSRectEdgeMaxX),
29
30
  @"rightCenter": @(NSRectEdgeMaxX),
30
31
  @"rightBottomEdge": @(NSRectEdgeMaxX),
31
- @"bottomLeftEdge": @(NSRectEdgeMinY),
32
- @"bottonLeftEdge": @(NSRectEdgeMinY), // Add both the correct and incorrect spellings during the transition period
33
- @"bottomAutoEdge": @(NSRectEdgeMinY),
34
- @"bottomCenter": @(NSRectEdgeMinY),
35
- @"bottomRightEdge": @(NSRectEdgeMinY),
32
+ @"bottomLeftEdge": @(NSRectEdgeMaxY),
33
+ @"bottomAutoEdge": @(NSRectEdgeMaxY),
34
+ @"bottomCenter": @(NSRectEdgeMaxY),
35
+ @"bottomRightEdge": @(NSRectEdgeMaxY),
36
36
  }), NSRectEdgeMaxY, integerValue);
37
37
 
38
38
  @end
39
39
 
40
- @interface RCT_EXTERN_MODULE(FRNCalloutManager, RCTViewManager)
40
+ @interface RCT_EXTERN_MODULE(RCTCalloutManager, RCTViewManager)
41
41
 
42
42
  RCT_EXPORT_METHOD(focusWindow : (nonnull NSNumber *)viewTag)
43
43
  {
@@ -0,0 +1,12 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <React/RCTViewComponentView.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface RCTCalloutComponentView : RCTViewComponentView
8
+ @end
9
+
10
+ NS_ASSUME_NONNULL_END
11
+
12
+ #endif
@@ -0,0 +1,209 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #import "RCTCalloutComponentView.h"
4
+
5
+ #import <react/renderer/components/FRNCalloutSpec/ComponentDescriptors.h>
6
+ #import <react/renderer/components/FRNCalloutSpec/EventEmitters.h>
7
+ #import <react/renderer/components/FRNCalloutSpec/Props.h>
8
+ #import <react/renderer/components/FRNCalloutSpec/RCTComponentViewHelpers.h>
9
+
10
+ #import <React/RCTComponent.h>
11
+ #import <React/RCTComponentViewProtocol.h>
12
+ #import <React/RCTConversions.h>
13
+ #import <React/RCTSurfaceTouchHandler.h>
14
+ #import <React/RCTView.h>
15
+
16
+ #import "FRNCallout-Swift.h"
17
+
18
+ using namespace facebook::react;
19
+
20
+ static NSRectEdge RCTNSRectEdgeFromDirectionalHint(CalloutDirectionalHint hint)
21
+ {
22
+ // CalloutView uses MinY for placement above the anchor and MaxY for placement below it.
23
+ switch (hint) {
24
+ case CalloutDirectionalHint::LeftTopEdge:
25
+ case CalloutDirectionalHint::LeftCenter:
26
+ case CalloutDirectionalHint::LeftBottomEdge:
27
+ return NSRectEdgeMinX;
28
+ case CalloutDirectionalHint::TopLeftEdge:
29
+ case CalloutDirectionalHint::TopAutoEdge:
30
+ case CalloutDirectionalHint::TopCenter:
31
+ case CalloutDirectionalHint::TopRightEdge:
32
+ return NSRectEdgeMinY;
33
+ case CalloutDirectionalHint::RightTopEdge:
34
+ case CalloutDirectionalHint::RightCenter:
35
+ case CalloutDirectionalHint::RightBottomEdge:
36
+ return NSRectEdgeMaxX;
37
+ case CalloutDirectionalHint::BottomLeftEdge:
38
+ case CalloutDirectionalHint::BottomAutoEdge:
39
+ case CalloutDirectionalHint::BottomCenter:
40
+ case CalloutDirectionalHint::BottomRightEdge:
41
+ return NSRectEdgeMaxY;
42
+ }
43
+ }
44
+
45
+ static void RCTApplyCalloutAppearance(
46
+ FRNCalloutView *calloutView,
47
+ const CalloutProps &props,
48
+ const LayoutMetrics &layoutMetrics)
49
+ {
50
+ const auto borderMetrics = props.resolveBorderMetrics(layoutMetrics);
51
+ calloutView.backgroundColor = RCTUIColorFromSharedColor(props.backgroundColor) ?: NSColor.clearColor;
52
+ calloutView.borderColor = RCTUIColorFromSharedColor(borderMetrics.borderColors.left) ?: NSColor.clearColor;
53
+ calloutView.borderWidth = borderMetrics.borderWidths.left;
54
+ calloutView.borderRadius = borderMetrics.borderRadii.topLeft.horizontal;
55
+ [calloutView setNeedsDisplay:YES];
56
+ }
57
+
58
+ static RCTPlatformView *RCTFindComponentViewWithTag(RCTPlatformView *rootView, NSInteger tag)
59
+ {
60
+ if ([rootView conformsToProtocol:@protocol(RCTComponentViewProtocol)] &&
61
+ [((id<RCTComponentViewProtocol>)rootView).reactTag integerValue] == tag) {
62
+ return rootView;
63
+ }
64
+
65
+ for (RCTPlatformView *subview in rootView.subviews) {
66
+ RCTPlatformView *match = RCTFindComponentViewWithTag(subview, tag);
67
+ if (match) {
68
+ return match;
69
+ }
70
+ }
71
+
72
+ return nil;
73
+ }
74
+
75
+ @interface RCTCalloutComponentView () <RCTCalloutViewProtocol>
76
+ @end
77
+
78
+ @implementation RCTCalloutComponentView {
79
+ FRNCalloutView *_calloutView;
80
+ NSInteger _targetTag;
81
+ RCTSurfaceTouchHandler *_touchHandler;
82
+ }
83
+
84
+ + (ComponentDescriptorProvider)componentDescriptorProvider
85
+ {
86
+ // Fabric normalizes the legacy RCT-prefixed view name before component lookup.
87
+ return concreteComponentDescriptorProvider<CalloutComponentDescriptor>();
88
+ }
89
+
90
+ - (instancetype)initWithFrame:(CGRect)frame
91
+ {
92
+ if (self = [super initWithFrame:frame]) {
93
+ static const auto defaultProps = std::make_shared<const CalloutProps>();
94
+ _props = defaultProps;
95
+
96
+ // This view manages content mounted in the Callout window and must not render in the React surface.
97
+ self.hidden = YES;
98
+
99
+ _calloutView = [[FRNCalloutView alloc] initWithFrame:self.bounds];
100
+
101
+ __weak RCTCalloutComponentView *weakSelf = self;
102
+ _calloutView.onShow = ^(__unused NSDictionary *event) {
103
+ [weakSelf emitOnShow];
104
+ };
105
+ _calloutView.onDismiss = ^(__unused NSDictionary *event) {
106
+ [weakSelf emitOnDismiss];
107
+ };
108
+
109
+ _touchHandler = [RCTSurfaceTouchHandler new];
110
+ [_touchHandler attachToView:_calloutView.contentProxyView];
111
+
112
+ self.contentView = _calloutView;
113
+ }
114
+ return self;
115
+ }
116
+
117
+ - (void)mountChildComponentView:(RCTUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
118
+ {
119
+ [_calloutView mountContentSubview:childComponentView at:index];
120
+ }
121
+
122
+ - (void)unmountChildComponentView:(RCTUIView<RCTComponentViewProtocol> *)childComponentView index:(__unused NSInteger)index
123
+ {
124
+ [_calloutView unmountContentSubview:childComponentView];
125
+ }
126
+
127
+ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
128
+ {
129
+ const auto &newProps = *std::static_pointer_cast<const CalloutProps>(props);
130
+
131
+ _calloutView.directionalHint = RCTNSRectEdgeFromDirectionalHint(newProps.directionalHint);
132
+ _calloutView.setInitialFocus = newProps.setInitialFocus;
133
+
134
+ const auto &rect = newProps.anchorRect;
135
+ _calloutView.anchorRect = NSMakeRect(rect.screenX, rect.screenY, rect.width, rect.height);
136
+
137
+ _targetTag = 0;
138
+ if (newProps.target.isNumber()) {
139
+ _targetTag = (NSInteger)newProps.target.asDouble();
140
+ }
141
+ [self updateAnchorView];
142
+ RCTApplyCalloutAppearance(_calloutView, newProps, _layoutMetrics);
143
+
144
+ [super updateProps:props oldProps:oldProps];
145
+ }
146
+
147
+ - (void)viewDidMoveToWindow
148
+ {
149
+ [super viewDidMoveToWindow];
150
+ [self updateAnchorView];
151
+ }
152
+
153
+ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
154
+ oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
155
+ {
156
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
157
+
158
+ const auto size = layoutMetrics.frame.size;
159
+ [_calloutView updateContentSize:NSMakeSize(size.width, size.height)];
160
+
161
+ const auto &props = *std::static_pointer_cast<const CalloutProps>(_props);
162
+ RCTApplyCalloutAppearance(_calloutView, props, layoutMetrics);
163
+ }
164
+
165
+ - (void)prepareForRecycle
166
+ {
167
+ [super prepareForRecycle];
168
+ _targetTag = 0;
169
+ [_calloutView setAnchorView:nil];
170
+ }
171
+
172
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
173
+ {
174
+ RCTCalloutHandleCommand(self, commandName, args);
175
+ }
176
+
177
+ - (void)focusWindow
178
+ {
179
+ [_calloutView focusWindow];
180
+ }
181
+
182
+ - (void)blurWindow
183
+ {
184
+ [_calloutView blurWindow];
185
+ }
186
+
187
+ - (void)updateAnchorView
188
+ {
189
+ RCTPlatformView *rootView = self.window.contentView;
190
+ [_calloutView setAnchorView:_targetTag > 0 && rootView ? RCTFindComponentViewWithTag(rootView, _targetTag) : nil];
191
+ }
192
+
193
+ - (void)emitOnShow
194
+ {
195
+ if (_eventEmitter) {
196
+ std::static_pointer_cast<const CalloutEventEmitter>(_eventEmitter)->onShow({.target = 0});
197
+ }
198
+ }
199
+
200
+ - (void)emitOnDismiss
201
+ {
202
+ if (_eventEmitter) {
203
+ std::static_pointer_cast<const CalloutEventEmitter>(_eventEmitter)->onDismiss({.target = 0});
204
+ }
205
+ }
206
+
207
+ @end
208
+
209
+ #endif
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluentui-react-native/callout",
3
- "version": "0.28.6",
3
+ "version": "0.29.4",
4
4
  "description": "A cross-platform Callout component using the Fluent Design System",
5
5
  "homepage": "https://github.com/microsoft/fluentui-react-native",
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/microsoft/fluentui-react-native.git",
11
- "directory": "packages/components/Callout"
11
+ "directory": "packages/native/Callout"
12
12
  },
13
13
  "type": "module",
14
14
  "main": "lib/index.js",
@@ -31,11 +31,7 @@
31
31
  "update-snapshots": "fluentui-scripts jest -u"
32
32
  },
33
33
  "dependencies": {
34
- "@fluentui-react-native/adapters": "0.15.0",
35
- "@fluentui-react-native/tokens": "0.24.6",
36
- "@uifabricshared/foundation-composable": "0.15.0",
37
- "@uifabricshared/foundation-compose": "1.16.6",
38
- "@uifabricshared/foundation-settings": "0.16.6"
34
+ "@fluentui-react-native/framework-base": "0.7.0"
39
35
  },
40
36
  "devDependencies": {
41
37
  "@babel/core": "^7.20.0",
@@ -46,13 +42,15 @@
46
42
  "@react-native-community/cli-platform-ios": "^20.0.0",
47
43
  "@react-native/babel-preset": "^0.81.0",
48
44
  "@react-native/metro-config": "^0.81.0",
45
+ "@testing-library/react-native": "^14.0.1",
49
46
  "@types/react": "~19.1.4",
50
47
  "@types/react-test-renderer": "^19.1.0",
51
48
  "react": "19.1.4",
52
49
  "react-native": "^0.81.6",
53
50
  "react-native-macos": "^0.81.0",
54
51
  "react-native-windows": "^0.81.0",
55
- "react-test-renderer": "19.1.4"
52
+ "react-test-renderer": "19.1.4",
53
+ "test-renderer": "1.1.0"
56
54
  },
57
55
  "peerDependencies": {
58
56
  "@office-iss/react-native-win32": "^0.74.0 || ^0.81.0",
@@ -78,12 +76,30 @@
78
76
  },
79
77
  "codegenConfig": {
80
78
  "name": "FRNCalloutSpec",
81
- "type": "components",
82
- "jsSrcsDir": "lib"
79
+ "type": "all",
80
+ "jsSrcsDir": "src",
81
+ "ios": {
82
+ "componentProvider": {
83
+ "Callout": "RCTCalloutComponentView"
84
+ }
85
+ },
86
+ "windows": {
87
+ "generators": "componentsWindows",
88
+ "namespace": "winrt::FluentUI::Callout::Codegen",
89
+ "outputDirectory": "windows/Callout/codegen",
90
+ "separateDataTypes": true
91
+ }
83
92
  },
84
93
  "furn": {
85
94
  "jestPlatform": "win32"
86
95
  },
96
+ "react-native-windows": {
97
+ "init-windows": {
98
+ "name": "Callout",
99
+ "namespace": "FluentUI.Callout",
100
+ "template": "cpp-lib"
101
+ }
102
+ },
87
103
  "rnx-kit": {
88
104
  "kitType": "library",
89
105
  "alignDeps": {
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ windows: {
5
+ sourceDir: 'windows',
6
+ projects: [
7
+ {
8
+ projectFile: 'Callout/Callout.vcxproj',
9
+ directDependency: true,
10
+ },
11
+ ],
12
+ },
13
+ },
14
+ },
15
+ };