1 | import React, { createElement } from 'react';
|
2 |
|
3 | import {
|
4 | attachProps,
|
5 | camelToDashCase,
|
6 | createForwardRef,
|
7 | dashToPascalCase,
|
8 | isCoveredByReact,
|
9 | mergeRefs,
|
10 | } from './utils';
|
11 |
|
12 | export interface HTMLStencilElement extends HTMLElement {
|
13 | componentOnReady(): Promise<this>;
|
14 | }
|
15 |
|
16 | interface StencilReactInternalProps<ElementType> extends React.HTMLAttributes<ElementType> {
|
17 | forwardedRef: React.RefObject<ElementType>;
|
18 | ref?: React.Ref<any>;
|
19 | }
|
20 |
|
21 | export const createReactComponent = <
|
22 | PropType,
|
23 | ElementType extends HTMLStencilElement,
|
24 | ContextStateType = {},
|
25 | ExpandedPropsTypes = {}
|
26 | >(
|
27 | tagName: string,
|
28 | ReactComponentContext?: React.Context<ContextStateType>,
|
29 | manipulatePropsFunction?: (
|
30 | originalProps: StencilReactInternalProps<ElementType>,
|
31 | propsToPass: any,
|
32 | ) => ExpandedPropsTypes,
|
33 | defineCustomElement?: () => void,
|
34 | ) => {
|
35 | if (defineCustomElement !== undefined) {
|
36 | defineCustomElement();
|
37 | }
|
38 |
|
39 | const displayName = dashToPascalCase(tagName);
|
40 | const ReactComponent = class extends React.Component<StencilReactInternalProps<ElementType>> {
|
41 | componentEl!: ElementType;
|
42 |
|
43 | setComponentElRef = (element: ElementType) => {
|
44 | this.componentEl = element;
|
45 | };
|
46 |
|
47 | constructor(props: StencilReactInternalProps<ElementType>) {
|
48 | super(props);
|
49 | }
|
50 |
|
51 | componentDidMount() {
|
52 | this.componentDidUpdate(this.props);
|
53 | }
|
54 |
|
55 | componentDidUpdate(prevProps: StencilReactInternalProps<ElementType>) {
|
56 | attachProps(this.componentEl, this.props, prevProps);
|
57 | }
|
58 |
|
59 | render() {
|
60 | const { children, forwardedRef, style, className, ref, ...cProps } = this.props;
|
61 |
|
62 | let propsToPass = Object.keys(cProps).reduce((acc: any, name) => {
|
63 | const value = (cProps as any)[name];
|
64 |
|
65 | if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
66 | const eventName = name.substring(2).toLowerCase();
|
67 | if (typeof document !== 'undefined' && isCoveredByReact(eventName)) {
|
68 | acc[name] = value;
|
69 | }
|
70 | } else {
|
71 |
|
72 |
|
73 | const type = typeof value;
|
74 |
|
75 | if (type === 'string' || type === 'boolean' || type === 'number') {
|
76 | acc[camelToDashCase(name)] = value;
|
77 | }
|
78 | }
|
79 | return acc;
|
80 | }, {});
|
81 |
|
82 | if (manipulatePropsFunction) {
|
83 | propsToPass = manipulatePropsFunction(this.props, propsToPass);
|
84 | }
|
85 |
|
86 | const newProps: Omit<StencilReactInternalProps<ElementType>, 'forwardedRef'> = {
|
87 | ...propsToPass,
|
88 | ref: mergeRefs(forwardedRef, this.setComponentElRef),
|
89 | style,
|
90 | };
|
91 |
|
92 | |
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | return createElement(tagName, newProps, children);
|
100 | }
|
101 |
|
102 | static get displayName() {
|
103 | return displayName;
|
104 | }
|
105 | };
|
106 |
|
107 |
|
108 | if (ReactComponentContext) {
|
109 | ReactComponent.contextType = ReactComponentContext;
|
110 | }
|
111 |
|
112 | return createForwardRef<PropType, ElementType>(ReactComponent, displayName);
|
113 | };
|