@servlyadmin/runtime-react 0.1.30 → 0.1.33
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/dist/index.cjs +41 -26
- package/dist/index.d.cts +54 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +44 -27
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -126,7 +126,8 @@ function ServlyComponent({
|
|
|
126
126
|
cacheStrategy = "memory",
|
|
127
127
|
retryConfig,
|
|
128
128
|
eventHandlers,
|
|
129
|
-
children
|
|
129
|
+
children,
|
|
130
|
+
waitForStyles = true
|
|
130
131
|
}) {
|
|
131
132
|
const containerRef = (0, import_react.useRef)(null);
|
|
132
133
|
const renderResultRef = (0, import_react.useRef)(null);
|
|
@@ -135,7 +136,9 @@ function ServlyComponent({
|
|
|
135
136
|
const [state, setState] = (0, import_react.useState)({
|
|
136
137
|
loading: true,
|
|
137
138
|
error: null,
|
|
138
|
-
data: null
|
|
139
|
+
data: null,
|
|
140
|
+
stylesReady: !waitForStyles
|
|
141
|
+
// If not waiting, styles are "ready"
|
|
139
142
|
});
|
|
140
143
|
const slotElements = useSlotElements(containerRef, isRendered);
|
|
141
144
|
const effectiveSlots = (0, import_react.useMemo)(() => {
|
|
@@ -160,24 +163,26 @@ function ServlyComponent({
|
|
|
160
163
|
};
|
|
161
164
|
try {
|
|
162
165
|
const result = await (0, import_runtime_core.fetchComponent)(id, fetchOptions);
|
|
163
|
-
setState({
|
|
166
|
+
setState((prev) => ({
|
|
167
|
+
...prev,
|
|
164
168
|
loading: false,
|
|
165
169
|
error: null,
|
|
166
170
|
data: result.data,
|
|
167
171
|
views: result.views,
|
|
168
172
|
registry: result.registry
|
|
169
|
-
});
|
|
173
|
+
}));
|
|
170
174
|
onLoad?.();
|
|
171
175
|
} catch (error) {
|
|
172
176
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
173
177
|
if (err.message === "Fetch aborted") return;
|
|
174
|
-
setState({
|
|
178
|
+
setState((prev) => ({
|
|
179
|
+
...prev,
|
|
175
180
|
loading: false,
|
|
176
181
|
error: err,
|
|
177
182
|
data: null,
|
|
178
183
|
views: void 0,
|
|
179
184
|
registry: void 0
|
|
180
|
-
});
|
|
185
|
+
}));
|
|
181
186
|
onError?.(err);
|
|
182
187
|
}
|
|
183
188
|
}, [id, version, cacheStrategy, retryConfig, onLoad, onError]);
|
|
@@ -191,24 +196,34 @@ function ServlyComponent({
|
|
|
191
196
|
}, [loadComponent]);
|
|
192
197
|
(0, import_react.useEffect)(() => {
|
|
193
198
|
if (!state.data || !containerRef.current) return;
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
const doRender = async () => {
|
|
200
|
+
if (waitForStyles) {
|
|
201
|
+
await (0, import_runtime_core.waitForTailwind)();
|
|
202
|
+
setState((prev) => ({ ...prev, stylesReady: true }));
|
|
203
|
+
}
|
|
204
|
+
const context = {
|
|
205
|
+
props,
|
|
206
|
+
state: {},
|
|
207
|
+
context: {}
|
|
208
|
+
};
|
|
209
|
+
if (renderResultRef.current) {
|
|
210
|
+
renderResultRef.current.update(context);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
renderResultRef.current = (0, import_runtime_core.render)({
|
|
214
|
+
container: containerRef.current,
|
|
215
|
+
elements: state.data.layout,
|
|
216
|
+
context,
|
|
217
|
+
eventHandlers,
|
|
218
|
+
views: state.views,
|
|
219
|
+
componentRegistry: state.registry
|
|
220
|
+
});
|
|
221
|
+
if (containerRef.current) {
|
|
222
|
+
(0, import_runtime_core.markElementReady)(containerRef.current);
|
|
223
|
+
}
|
|
224
|
+
setIsRendered(true);
|
|
198
225
|
};
|
|
199
|
-
|
|
200
|
-
renderResultRef.current.update(context);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
renderResultRef.current = (0, import_runtime_core.render)({
|
|
204
|
-
container: containerRef.current,
|
|
205
|
-
elements: state.data.layout,
|
|
206
|
-
context,
|
|
207
|
-
eventHandlers,
|
|
208
|
-
views: state.views,
|
|
209
|
-
componentRegistry: state.registry
|
|
210
|
-
});
|
|
211
|
-
setIsRendered(true);
|
|
226
|
+
doRender();
|
|
212
227
|
return () => {
|
|
213
228
|
if (renderResultRef.current) {
|
|
214
229
|
renderResultRef.current.destroy();
|
|
@@ -216,7 +231,7 @@ function ServlyComponent({
|
|
|
216
231
|
setIsRendered(false);
|
|
217
232
|
}
|
|
218
233
|
};
|
|
219
|
-
}, [state.data, eventHandlers]);
|
|
234
|
+
}, [state.data, eventHandlers, waitForStyles]);
|
|
220
235
|
(0, import_react.useEffect)(() => {
|
|
221
236
|
if (!renderResultRef.current || !state.data) return;
|
|
222
237
|
const context = {
|
|
@@ -226,7 +241,7 @@ function ServlyComponent({
|
|
|
226
241
|
};
|
|
227
242
|
renderResultRef.current.update(context);
|
|
228
243
|
}, [props, state.data]);
|
|
229
|
-
if (state.loading) {
|
|
244
|
+
if (state.loading || waitForStyles && !state.stylesReady) {
|
|
230
245
|
if (fallback) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: fallback });
|
|
231
246
|
if (showSkeleton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingSkeleton, { className });
|
|
232
247
|
return null;
|
|
@@ -248,7 +263,7 @@ function ServlyComponent({
|
|
|
248
263
|
"div",
|
|
249
264
|
{
|
|
250
265
|
ref: containerRef,
|
|
251
|
-
className: `servly-component ${className || ""}`,
|
|
266
|
+
className: `servly-component servly-ready ${className || ""}`,
|
|
252
267
|
style,
|
|
253
268
|
"data-servly-id": id,
|
|
254
269
|
"data-servly-version": state.data?.version
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CacheStrategy, RetryConfig } from '@servlyadmin/runtime-core';
|
|
3
|
+
export { BindingContext, CacheStrategy, ComponentData, FetchOptions, LayoutElement, PropDefinition, RetryConfig, clearAllCaches, compareVersions, fetchComponent, getRegistryUrl, invalidateCache, isComponentAvailable, parseVersion, prefetchComponents, resolveVersion, satisfiesVersion, setRegistryUrl } from '@servlyadmin/runtime-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ServlyComponent
|
|
7
|
+
* React wrapper for Servly runtime renderer with slot support
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Slot content type - React nodes keyed by slot name
|
|
12
|
+
*/
|
|
13
|
+
type SlotContent = Record<string, React.ReactNode>;
|
|
14
|
+
/**
|
|
15
|
+
* Props for ServlyComponent
|
|
16
|
+
*/
|
|
17
|
+
interface ServlyComponentProps<P = Record<string, any>> {
|
|
18
|
+
/** Component ID from the registry */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Version specifier (exact, range, or "latest") */
|
|
21
|
+
version?: string;
|
|
22
|
+
/** Props to pass to the component */
|
|
23
|
+
props?: P;
|
|
24
|
+
/** Slot content - React nodes to portal into named slots */
|
|
25
|
+
slots?: SlotContent;
|
|
26
|
+
/** Fallback UI while loading or on error */
|
|
27
|
+
fallback?: React.ReactNode;
|
|
28
|
+
/** Error callback */
|
|
29
|
+
onError?: (error: Error) => void;
|
|
30
|
+
/** Load complete callback */
|
|
31
|
+
onLoad?: () => void;
|
|
32
|
+
/** Custom className for wrapper */
|
|
33
|
+
className?: string;
|
|
34
|
+
/** Custom styles for wrapper */
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
/** Show loading skeleton */
|
|
37
|
+
showSkeleton?: boolean;
|
|
38
|
+
/** Cache strategy */
|
|
39
|
+
cacheStrategy?: CacheStrategy;
|
|
40
|
+
/** Retry configuration */
|
|
41
|
+
retryConfig?: Partial<RetryConfig>;
|
|
42
|
+
/** Event handlers keyed by element ID then event name */
|
|
43
|
+
eventHandlers?: Record<string, Record<string, (e: Event) => void>>;
|
|
44
|
+
/** Children - rendered into default slot if no slots prop */
|
|
45
|
+
children?: React.ReactNode;
|
|
46
|
+
/** Wait for Tailwind CSS to load before showing component (prevents FOUC) */
|
|
47
|
+
waitForStyles?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* ServlyComponent - React wrapper for Servly runtime with slot support
|
|
51
|
+
*/
|
|
52
|
+
declare function ServlyComponent<P = Record<string, any>>({ id, version, props, slots, fallback, onError, onLoad, className, style, showSkeleton, cacheStrategy, retryConfig, eventHandlers, children, waitForStyles, }: ServlyComponentProps<P>): React.ReactElement | null;
|
|
53
|
+
|
|
54
|
+
export { ServlyComponent, type ServlyComponentProps, ServlyComponent as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CacheStrategy, RetryConfig } from '@servlyadmin/runtime-core';
|
|
3
|
+
export { BindingContext, CacheStrategy, ComponentData, FetchOptions, LayoutElement, PropDefinition, RetryConfig, clearAllCaches, compareVersions, fetchComponent, getRegistryUrl, invalidateCache, isComponentAvailable, parseVersion, prefetchComponents, resolveVersion, satisfiesVersion, setRegistryUrl } from '@servlyadmin/runtime-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ServlyComponent
|
|
7
|
+
* React wrapper for Servly runtime renderer with slot support
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Slot content type - React nodes keyed by slot name
|
|
12
|
+
*/
|
|
13
|
+
type SlotContent = Record<string, React.ReactNode>;
|
|
14
|
+
/**
|
|
15
|
+
* Props for ServlyComponent
|
|
16
|
+
*/
|
|
17
|
+
interface ServlyComponentProps<P = Record<string, any>> {
|
|
18
|
+
/** Component ID from the registry */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Version specifier (exact, range, or "latest") */
|
|
21
|
+
version?: string;
|
|
22
|
+
/** Props to pass to the component */
|
|
23
|
+
props?: P;
|
|
24
|
+
/** Slot content - React nodes to portal into named slots */
|
|
25
|
+
slots?: SlotContent;
|
|
26
|
+
/** Fallback UI while loading or on error */
|
|
27
|
+
fallback?: React.ReactNode;
|
|
28
|
+
/** Error callback */
|
|
29
|
+
onError?: (error: Error) => void;
|
|
30
|
+
/** Load complete callback */
|
|
31
|
+
onLoad?: () => void;
|
|
32
|
+
/** Custom className for wrapper */
|
|
33
|
+
className?: string;
|
|
34
|
+
/** Custom styles for wrapper */
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
/** Show loading skeleton */
|
|
37
|
+
showSkeleton?: boolean;
|
|
38
|
+
/** Cache strategy */
|
|
39
|
+
cacheStrategy?: CacheStrategy;
|
|
40
|
+
/** Retry configuration */
|
|
41
|
+
retryConfig?: Partial<RetryConfig>;
|
|
42
|
+
/** Event handlers keyed by element ID then event name */
|
|
43
|
+
eventHandlers?: Record<string, Record<string, (e: Event) => void>>;
|
|
44
|
+
/** Children - rendered into default slot if no slots prop */
|
|
45
|
+
children?: React.ReactNode;
|
|
46
|
+
/** Wait for Tailwind CSS to load before showing component (prevents FOUC) */
|
|
47
|
+
waitForStyles?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* ServlyComponent - React wrapper for Servly runtime with slot support
|
|
51
|
+
*/
|
|
52
|
+
declare function ServlyComponent<P = Record<string, any>>({ id, version, props, slots, fallback, onError, onLoad, className, style, showSkeleton, cacheStrategy, retryConfig, eventHandlers, children, waitForStyles, }: ServlyComponentProps<P>): React.ReactElement | null;
|
|
53
|
+
|
|
54
|
+
export { ServlyComponent, type ServlyComponentProps, ServlyComponent as default };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,9 @@ import { useRef, useEffect, useState, useCallback, useMemo } from "react";
|
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import {
|
|
5
5
|
render,
|
|
6
|
-
fetchComponent
|
|
6
|
+
fetchComponent,
|
|
7
|
+
waitForTailwind,
|
|
8
|
+
markElementReady
|
|
7
9
|
} from "@servlyadmin/runtime-core";
|
|
8
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
11
|
var LoadingSkeleton = ({ className }) => /* @__PURE__ */ jsx(
|
|
@@ -91,7 +93,8 @@ function ServlyComponent({
|
|
|
91
93
|
cacheStrategy = "memory",
|
|
92
94
|
retryConfig,
|
|
93
95
|
eventHandlers,
|
|
94
|
-
children
|
|
96
|
+
children,
|
|
97
|
+
waitForStyles = true
|
|
95
98
|
}) {
|
|
96
99
|
const containerRef = useRef(null);
|
|
97
100
|
const renderResultRef = useRef(null);
|
|
@@ -100,7 +103,9 @@ function ServlyComponent({
|
|
|
100
103
|
const [state, setState] = useState({
|
|
101
104
|
loading: true,
|
|
102
105
|
error: null,
|
|
103
|
-
data: null
|
|
106
|
+
data: null,
|
|
107
|
+
stylesReady: !waitForStyles
|
|
108
|
+
// If not waiting, styles are "ready"
|
|
104
109
|
});
|
|
105
110
|
const slotElements = useSlotElements(containerRef, isRendered);
|
|
106
111
|
const effectiveSlots = useMemo(() => {
|
|
@@ -125,24 +130,26 @@ function ServlyComponent({
|
|
|
125
130
|
};
|
|
126
131
|
try {
|
|
127
132
|
const result = await fetchComponent(id, fetchOptions);
|
|
128
|
-
setState({
|
|
133
|
+
setState((prev) => ({
|
|
134
|
+
...prev,
|
|
129
135
|
loading: false,
|
|
130
136
|
error: null,
|
|
131
137
|
data: result.data,
|
|
132
138
|
views: result.views,
|
|
133
139
|
registry: result.registry
|
|
134
|
-
});
|
|
140
|
+
}));
|
|
135
141
|
onLoad?.();
|
|
136
142
|
} catch (error) {
|
|
137
143
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
138
144
|
if (err.message === "Fetch aborted") return;
|
|
139
|
-
setState({
|
|
145
|
+
setState((prev) => ({
|
|
146
|
+
...prev,
|
|
140
147
|
loading: false,
|
|
141
148
|
error: err,
|
|
142
149
|
data: null,
|
|
143
150
|
views: void 0,
|
|
144
151
|
registry: void 0
|
|
145
|
-
});
|
|
152
|
+
}));
|
|
146
153
|
onError?.(err);
|
|
147
154
|
}
|
|
148
155
|
}, [id, version, cacheStrategy, retryConfig, onLoad, onError]);
|
|
@@ -156,24 +163,34 @@ function ServlyComponent({
|
|
|
156
163
|
}, [loadComponent]);
|
|
157
164
|
useEffect(() => {
|
|
158
165
|
if (!state.data || !containerRef.current) return;
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
166
|
+
const doRender = async () => {
|
|
167
|
+
if (waitForStyles) {
|
|
168
|
+
await waitForTailwind();
|
|
169
|
+
setState((prev) => ({ ...prev, stylesReady: true }));
|
|
170
|
+
}
|
|
171
|
+
const context = {
|
|
172
|
+
props,
|
|
173
|
+
state: {},
|
|
174
|
+
context: {}
|
|
175
|
+
};
|
|
176
|
+
if (renderResultRef.current) {
|
|
177
|
+
renderResultRef.current.update(context);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
renderResultRef.current = render({
|
|
181
|
+
container: containerRef.current,
|
|
182
|
+
elements: state.data.layout,
|
|
183
|
+
context,
|
|
184
|
+
eventHandlers,
|
|
185
|
+
views: state.views,
|
|
186
|
+
componentRegistry: state.registry
|
|
187
|
+
});
|
|
188
|
+
if (containerRef.current) {
|
|
189
|
+
markElementReady(containerRef.current);
|
|
190
|
+
}
|
|
191
|
+
setIsRendered(true);
|
|
163
192
|
};
|
|
164
|
-
|
|
165
|
-
renderResultRef.current.update(context);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
renderResultRef.current = render({
|
|
169
|
-
container: containerRef.current,
|
|
170
|
-
elements: state.data.layout,
|
|
171
|
-
context,
|
|
172
|
-
eventHandlers,
|
|
173
|
-
views: state.views,
|
|
174
|
-
componentRegistry: state.registry
|
|
175
|
-
});
|
|
176
|
-
setIsRendered(true);
|
|
193
|
+
doRender();
|
|
177
194
|
return () => {
|
|
178
195
|
if (renderResultRef.current) {
|
|
179
196
|
renderResultRef.current.destroy();
|
|
@@ -181,7 +198,7 @@ function ServlyComponent({
|
|
|
181
198
|
setIsRendered(false);
|
|
182
199
|
}
|
|
183
200
|
};
|
|
184
|
-
}, [state.data, eventHandlers]);
|
|
201
|
+
}, [state.data, eventHandlers, waitForStyles]);
|
|
185
202
|
useEffect(() => {
|
|
186
203
|
if (!renderResultRef.current || !state.data) return;
|
|
187
204
|
const context = {
|
|
@@ -191,7 +208,7 @@ function ServlyComponent({
|
|
|
191
208
|
};
|
|
192
209
|
renderResultRef.current.update(context);
|
|
193
210
|
}, [props, state.data]);
|
|
194
|
-
if (state.loading) {
|
|
211
|
+
if (state.loading || waitForStyles && !state.stylesReady) {
|
|
195
212
|
if (fallback) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
196
213
|
if (showSkeleton) return /* @__PURE__ */ jsx(LoadingSkeleton, { className });
|
|
197
214
|
return null;
|
|
@@ -213,7 +230,7 @@ function ServlyComponent({
|
|
|
213
230
|
"div",
|
|
214
231
|
{
|
|
215
232
|
ref: containerRef,
|
|
216
|
-
className: `servly-component ${className || ""}`,
|
|
233
|
+
className: `servly-component servly-ready ${className || ""}`,
|
|
217
234
|
style,
|
|
218
235
|
"data-servly-id": id,
|
|
219
236
|
"data-servly-version": state.data?.version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servlyadmin/runtime-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "React wrapper for Servly runtime renderer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": ">=17.0.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@servlyadmin/runtime-core": "^0.1.
|
|
39
|
+
"@servlyadmin/runtime-core": "^0.1.43"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/react": "^18.2.0",
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"tsup": "^8.0.0",
|
|
47
47
|
"typescript": "^5.3.0"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|