@ui5/webcomponents-base 2.20.0 → 2.21.0-rc.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.
- package/CHANGELOG.md +16 -0
- package/dist/.tsbuildinfobuild +1 -1
- package/dist/CustomElementsScopeUtils.d.ts +2 -2
- package/dist/CustomElementsScopeUtils.js +3 -3
- package/dist/CustomElementsScopeUtils.js.map +1 -1
- package/dist/Runtimes.d.ts +1 -0
- package/dist/Runtimes.js +1 -0
- package/dist/Runtimes.js.map +1 -1
- package/dist/createReactComponent.d.ts +26 -0
- package/dist/createReactComponent.js +51 -0
- package/dist/createReactComponent.js.map +1 -0
- package/dist/custom-elements-internal.json +15 -0
- package/dist/custom-elements.json +15 -0
- package/dist/generated/VersionInfo.js +4 -4
- package/dist/generated/VersionInfo.js.map +1 -1
- package/dist/generated/css/FontFace.css.d.ts +1 -1
- package/dist/generated/css/FontFace.css.js +36 -36
- package/dist/generated/css/FontFace.css.js.map +1 -1
- package/dist/prod/CustomElementsScopeUtils.js +1 -1
- package/dist/prod/CustomElementsScopeUtils.js.map +2 -2
- package/dist/prod/Runtimes.js +1 -1
- package/dist/prod/Runtimes.js.map +2 -2
- package/dist/prod/createReactComponent.js +2 -0
- package/dist/prod/createReactComponent.js.map +7 -0
- package/dist/prod/generated/VersionInfo.js +1 -1
- package/dist/prod/generated/VersionInfo.js.map +2 -2
- package/dist/prod/generated/css/FontFace.css.js +36 -36
- package/dist/prod/generated/css/FontFace.css.js.map +1 -1
- package/dist/react18/createReactComponent.d.ts +38 -0
- package/dist/react18/createReactComponent.js +112 -0
- package/dist/react18/createReactComponent.js.map +1 -0
- package/package.json +12 -4
- package/src/react18/createReactComponent.tsx +140 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React wrapper factory for UI5 Web Components.
|
|
3
|
+
*
|
|
4
|
+
* This lightweight factory creates typed React components that wrap UI5 Web Components.
|
|
5
|
+
* It handles:
|
|
6
|
+
* - Event prop conversion (onXxx → ui5-xxx event listeners)
|
|
7
|
+
* - Ref forwarding
|
|
8
|
+
* - Children handling
|
|
9
|
+
*
|
|
10
|
+
* Note: The function supports react >= 18.
|
|
11
|
+
* Note: This is for documentation samples only - for production React apps,
|
|
12
|
+
* use the official @ui5/webcomponents-react library.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as React from "react";
|
|
16
|
+
import type { ReactNode } from "react";
|
|
17
|
+
import {
|
|
18
|
+
useRef,
|
|
19
|
+
useEffect,
|
|
20
|
+
forwardRef,
|
|
21
|
+
} from "react";
|
|
22
|
+
import type UI5Element from "../UI5Element.js";
|
|
23
|
+
|
|
24
|
+
type EventHandler<E = Event> = (event: E) => void;
|
|
25
|
+
|
|
26
|
+
// Interface for UI5 Web Component classes with _jsxProps support
|
|
27
|
+
interface UI5ComponentClass<T extends UI5Element = UI5Element> {
|
|
28
|
+
new (): T;
|
|
29
|
+
getMetadata(): {
|
|
30
|
+
getTag(): string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Helper to convert event name
|
|
35
|
+
const toEventName = (propName: string): string => {
|
|
36
|
+
return propName
|
|
37
|
+
.slice(2) // Remove "on"
|
|
38
|
+
.replace(/([A-Z])/g, (match: string, letter: string, index: number): string => {
|
|
39
|
+
return index === 0 ? letter.toLowerCase() : `-${letter.toLowerCase()}`;
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Helper to create cleanup function for event listener
|
|
44
|
+
const createEventCleanup = (element: UI5Element, eventName: string, handler: EventHandler): (() => void) => {
|
|
45
|
+
element.addEventListener(eventName, handler);
|
|
46
|
+
return () => element.removeEventListener(eventName, handler);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a React component wrapper for a UI5 Web Component.
|
|
51
|
+
* Uses the component's _jsxProps type for full TypeScript support.
|
|
52
|
+
*
|
|
53
|
+
* @param ComponentClass - The UI5 Web Component class (e.g., Button from "@ui5/webcomponents/dist/Button.js")
|
|
54
|
+
* @returns A React component that renders the custom element with proper TypeScript types
|
|
55
|
+
* @since 2.21.0
|
|
56
|
+
* @example
|
|
57
|
+
* import Button from "@ui5/webcomponents/dist/Button.js";
|
|
58
|
+
* const ReactButton = createReactComponent(Button);
|
|
59
|
+
* // ReactButton props are typed based on Button's _jsxProps
|
|
60
|
+
*/
|
|
61
|
+
function createReactComponent<T extends UI5Element>(
|
|
62
|
+
ComponentClass: UI5ComponentClass<T>,
|
|
63
|
+
): React.ForwardRefExoticComponent<
|
|
64
|
+
React.PropsWithoutRef<T["_jsxProps"] & { children?: ReactNode }> & React.RefAttributes<T>
|
|
65
|
+
> {
|
|
66
|
+
const tagName = ComponentClass.getMetadata().getTag();
|
|
67
|
+
|
|
68
|
+
const Component = forwardRef<T, T["_jsxProps"] & { children?: ReactNode }>((props, ref) => {
|
|
69
|
+
const { children, ...restProps } = props;
|
|
70
|
+
const elementRef = useRef<T>(null);
|
|
71
|
+
|
|
72
|
+
// Forward ref
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (ref) {
|
|
75
|
+
if (typeof ref === "function") {
|
|
76
|
+
ref(elementRef.current);
|
|
77
|
+
} else {
|
|
78
|
+
ref.current = elementRef.current;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, [ref]);
|
|
82
|
+
|
|
83
|
+
// Handle event props and boolean props imperatively
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const element = elementRef.current;
|
|
86
|
+
if (!element) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const eventCleanups: Array<() => void> = [];
|
|
91
|
+
|
|
92
|
+
Object.keys(restProps).forEach(propName => {
|
|
93
|
+
const propValue = (restProps as Record<string, unknown>)[propName];
|
|
94
|
+
if (propName.startsWith("on") && typeof propValue === "function") {
|
|
95
|
+
// Convert React event naming (onClick, onSelectionChange) to DOM event naming
|
|
96
|
+
// onClick -> click, onSelectionChange -> selection-change
|
|
97
|
+
const eventName = toEventName(propName);
|
|
98
|
+
const handler = propValue as EventHandler;
|
|
99
|
+
eventCleanups.push(createEventCleanup(element, eventName, handler));
|
|
100
|
+
} else if (typeof propValue === "boolean") {
|
|
101
|
+
// React 18 sets false booleans as empty string attributes on custom elements.
|
|
102
|
+
// Set as property directly to avoid this.
|
|
103
|
+
(element as any)[propName] = propValue;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return () => {
|
|
108
|
+
eventCleanups.forEach(cleanup => cleanup());
|
|
109
|
+
};
|
|
110
|
+
}, [restProps]);
|
|
111
|
+
|
|
112
|
+
// Filter out event handlers and booleans from DOM props
|
|
113
|
+
const domProps: Record<string, unknown> = {};
|
|
114
|
+
Object.keys(restProps).forEach(propName => {
|
|
115
|
+
const propValue = (restProps as Record<string, unknown>)[propName];
|
|
116
|
+
if (propName.startsWith("on") && typeof propValue === "function") { return; }
|
|
117
|
+
if (typeof propValue === "boolean") { return; } // handled in useEffect
|
|
118
|
+
// className → class for React compatibility
|
|
119
|
+
if (propName === "className") {
|
|
120
|
+
// eslint-disable-next-line dot-notation
|
|
121
|
+
domProps["class"] = propValue;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Convert camelCase to kebab-case for HTML attributes
|
|
125
|
+
const attrName = propName.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
126
|
+
domProps[attrName] = propValue;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return React.createElement(tagName, { ref: elementRef, ...domProps }, children);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
Component.displayName = tagName
|
|
133
|
+
.split("-")
|
|
134
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
135
|
+
.join("");
|
|
136
|
+
|
|
137
|
+
return Component;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default createReactComponent;
|