|
12 | 12 | // See the License for the specific language governing permissions and
|
13 | 13 | // limitations under the License.
|
14 | 14 |
|
| 15 | +import 'dart:js_util'; |
| 16 | + |
15 | 17 | import 'package:over_react/src/component_declaration/component_type_checking.dart';
|
16 | 18 | import 'package:react/react_client/react_interop.dart' as react_interop;
|
17 | 19 | import 'package:react/react_client.dart';
|
@@ -52,52 +54,153 @@ Ref<T> createRef<T>() {
|
52 | 54 | ///
|
53 | 55 | /// > __NOTE:__ This should only be used to wrap components that extend from `Component2`.
|
54 | 56 | ///
|
55 |
| -/// __Example__: |
| 57 | +/// __Example 1:__ Forwarding refs to DOM components |
56 | 58 | ///
|
57 |
| -/// UiFactory<DomProps> DivForwarded = forwardRef<DomProps>((props, ref) { |
58 |
| -/// return (Dom.div() |
59 |
| -/// ..ref = ref |
60 |
| -/// ..className = 'special-class' |
61 |
| -/// )( |
62 |
| -/// props.children |
63 |
| -/// ); |
64 |
| -/// })(Dom.div); |
| 59 | +/// ```dart |
| 60 | +/// import 'dart:html'; |
| 61 | +/// import 'package:over_react/over_react.dart'; |
| 62 | +/// import 'package:over_react/react_dom.dart' as react_dom; |
65 | 63 | ///
|
66 |
| -/// ___ OR ___ |
| 64 | +/// // ---------- Component Definition ---------- |
67 | 65 | ///
|
68 |
| -/// UiFactory<FooProps> FooForwarded = forwardRef<FooProps>((props, ref) { |
69 |
| -/// return (Foo() |
70 |
| -/// ..forwardedRef = ref |
71 |
| -/// )(); |
72 |
| -/// })(Foo); |
| 66 | +/// final FancyButton = forwardRef<DomProps>((props, ref) { |
| 67 | +/// final classes = ClassNameBuilder.fromProps(props) |
| 68 | +/// ..add('FancyButton'); |
73 | 69 | ///
|
74 |
| -/// UiFactory<FooProps> Foo = _$Foo; |
| 70 | +/// return (Dom.button() |
| 71 | +/// ..addProps(getPropsToForward(props, onlyCopyDomProps: true)) |
| 72 | +/// ..className = classes.toClassName() |
| 73 | +/// ..ref = ref |
| 74 | +/// )('Click me!'); |
| 75 | +/// })(Dom.button); |
75 | 76 | ///
|
76 |
| -/// mixin FooProps on UiProps { |
77 |
| -/// Ref forwardedRef; |
78 |
| -/// } |
| 77 | +/// // ---------- Component Consumption ---------- |
79 | 78 | ///
|
80 |
| -/// @Component2() |
81 |
| -/// class FooComponent extends UiComponent2<FooProps> { |
82 |
| -/// @override |
83 |
| -/// render() { |
84 |
| -/// return (Dom.button() |
85 |
| -/// ..ref = props.forwardedRef |
86 |
| -/// )('Click this button'); |
87 |
| -/// } |
88 |
| -/// } |
| 79 | +/// void main() { |
| 80 | +/// setClientConfiguration(); |
| 81 | +/// final ref = createRef<Element>(); |
| 82 | +/// |
| 83 | +/// react_dom.render( |
| 84 | +/// (FancyButton() |
| 85 | +/// ..ref = ref |
| 86 | +/// ..onClick = (_) { |
| 87 | +/// print(ref.current.outerHtml); |
| 88 | +/// } |
| 89 | +/// )(), |
| 90 | +/// querySelector('#idOfSomeNodeInTheDom') |
| 91 | +/// ); |
| 92 | +/// |
| 93 | +/// // You can still get a ref directly to the DOM button: |
| 94 | +/// final buttonNode = ref.current; |
| 95 | +/// } |
| 96 | +/// ``` |
| 97 | +/// |
| 98 | +/// __Example 2:__ Forwarding refs in higher-order components |
| 99 | +/// |
| 100 | +/// ```dart |
| 101 | +/// import 'dart:html'; |
| 102 | +/// import 'package:over_react/over_react.dart'; |
| 103 | +/// import 'package:over_react/react_dom.dart' as react_dom; |
| 104 | +/// |
| 105 | +/// // ---------- Component Definitions ---------- |
| 106 | +/// |
| 107 | +/// final FancyButton = forwardRef<DomProps>((props, ref) { |
| 108 | +/// final classes = ClassNameBuilder.fromProps(props) |
| 109 | +/// ..add('FancyButton'); |
| 110 | +/// |
| 111 | +/// return (Dom.button() |
| 112 | +/// ..addProps(getPropsToForward(props, onlyCopyDomProps: true)) |
| 113 | +/// ..className = classes.toClassName() |
| 114 | +/// ..ref = ref |
| 115 | +/// )('Click me!'); |
| 116 | +/// })(Dom.button); |
| 117 | +/// |
| 118 | +/// final LogProps = forwardRef<LogPropsProps>((props, ref) { |
| 119 | +/// return (_LogProps() |
| 120 | +/// ..addProps(props) |
| 121 | +/// .._forwardedRef = ref |
| 122 | +/// )('Click me!'); |
| 123 | +/// })(_LogProps); |
| 124 | +/// |
| 125 | +/// UiFactory<LogPropsProps> _LogProps = _$_LogProps; |
| 126 | +/// |
| 127 | +/// mixin LogPropsProps on UiProps { |
| 128 | +/// BuilderOnlyUiFactory<DomProps> builder; |
| 129 | +/// |
| 130 | +/// // Private since we only use this to pass along the value of `ref` to |
| 131 | +/// // the return value of forwardRef. |
| 132 | +/// // |
| 133 | +/// // Consumers can set this private field value using the public `ref` setter. |
| 134 | +/// Ref _forwardedRef; |
| 135 | +/// } |
| 136 | +/// |
| 137 | +/// @Component2() |
| 138 | +/// class LogPropsComponent extends UiComponent2<LogPropsProps> { |
| 139 | +/// @override |
| 140 | +/// void componentDidUpdate(Map prevProps, _, [__]) { |
| 141 | +/// print('old props: $prevProps'); |
| 142 | +/// print('new props: $props'); |
| 143 | +/// } |
| 144 | +/// |
| 145 | +/// @override |
| 146 | +/// render() { |
| 147 | +/// return (props.builder() |
| 148 | +/// ..modifyProps(addUnconsumedDomProps) |
| 149 | +/// ..ref = props._forwardedRef |
| 150 | +/// )(props.children); |
| 151 | +/// } |
| 152 | +/// } |
| 153 | +/// |
| 154 | +/// // ---------- Component Consumption ---------- |
| 155 | +/// |
| 156 | +/// void main() { |
| 157 | +/// setClientConfiguration(); |
| 158 | +/// final ref = createRef<Element>(); |
| 159 | +/// |
| 160 | +/// react_dom.render( |
| 161 | +/// (LogProps() |
| 162 | +/// ..builder = FancyButton |
| 163 | +/// ..className = 'btn btn-primary' |
| 164 | +/// ..ref = ref |
| 165 | +/// ..onClick = (_) { |
| 166 | +/// print(ref.current.outerHtml); |
| 167 | +/// } |
| 168 | +/// )(), |
| 169 | +/// querySelector('#idOfSomeNodeInTheDom') |
| 170 | +/// ); |
| 171 | +/// |
| 172 | +/// // You can still get a ref directly to the DOM button: |
| 173 | +/// final buttonNode = ref.current; |
| 174 | +/// } |
| 175 | +/// ``` |
89 | 176 | ///
|
90 | 177 | /// Learn more: <https://reactjs.org/docs/forwarding-refs.html>.
|
91 | 178 | UiFactory<TProps> Function(UiFactory<TProps>) forwardRef<TProps extends UiProps>(
|
92 |
| - Function(TProps props, Ref ref) wrapperFunction) { |
| 179 | + Function(TProps props, Ref ref) wrapperFunction, {String displayName}) { |
93 | 180 |
|
94 | 181 | UiFactory<TProps> wrapWithForwardRef(UiFactory<TProps> factory) {
|
95 | 182 | enforceMinimumComponentVersionFor(factory().componentFactory);
|
| 183 | + |
| 184 | + if (displayName == null) { |
| 185 | + final componentFactoryType = factory().componentFactory.type; |
| 186 | + if (componentFactoryType is String) { |
| 187 | + // DomComponent |
| 188 | + displayName = componentFactoryType; |
| 189 | + } else if (componentFactoryType is Function) { |
| 190 | + // JS component factories, Dart function components, Dart composite components |
| 191 | + displayName = getProperty(componentFactoryType, 'displayName'); |
96 | 192 |
|
| 193 | + if (displayName == null) { |
| 194 | + final ctor = getProperty(componentFactoryType, 'constructor'); |
| 195 | + displayName = (ctor == null ? null : getProperty(ctor, 'name')) ?? 'Anonymous'; |
| 196 | + } |
| 197 | + } |
| 198 | + } |
| 199 | + |
97 | 200 | Object wrapProps(Map props, Ref ref) {
|
98 | 201 | return wrapperFunction(factory(props), ref);
|
99 | 202 | }
|
100 |
| - ReactComponentFactoryProxy hoc = react_interop.forwardRef(wrapProps); |
| 203 | + ReactComponentFactoryProxy hoc = react_interop.forwardRef(wrapProps, displayName: displayName); |
101 | 204 | setComponentTypeMeta(hoc, isHoc: true, parentType: factory().componentFactory);
|
102 | 205 |
|
103 | 206 | TProps forwardedFactory([Map props]) {
|
|
0 commit comments