@bravostudioai/react 0.1.0 → 0.1.2
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/_virtual/main.js +3 -2
- package/dist/cli/commands/generate.js +161 -1438
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/codegen/generator.js +473 -0
- package/dist/codegen/generator.js.map +1 -0
- package/dist/codegen/parser.js +720 -0
- package/dist/codegen/parser.js.map +1 -0
- package/dist/components/EncoreApp.js +197 -162
- package/dist/components/EncoreApp.js.map +1 -1
- package/dist/contexts/EncoreRouterContext.js +13 -0
- package/dist/contexts/EncoreRouterContext.js.map +1 -0
- package/dist/hooks/usePusherUpdates.js +4 -2
- package/dist/hooks/usePusherUpdates.js.map +1 -1
- package/dist/lib/dynamicModules.js +75 -85
- package/dist/lib/dynamicModules.js.map +1 -1
- package/dist/lib/moduleRegistry.js +20 -0
- package/dist/lib/moduleRegistry.js.map +1 -0
- package/dist/lib/packages.js +1 -3
- package/dist/lib/packages.js.map +1 -1
- package/dist/src/cli/commands/generate.d.ts.map +1 -1
- package/dist/src/codegen/generator.d.ts +10 -0
- package/dist/src/codegen/generator.d.ts.map +1 -0
- package/dist/src/codegen/index.d.ts +4 -0
- package/dist/src/codegen/index.d.ts.map +1 -0
- package/dist/src/codegen/parser.d.ts +37 -0
- package/dist/src/codegen/parser.d.ts.map +1 -0
- package/dist/src/codegen/types.d.ts +53 -0
- package/dist/src/codegen/types.d.ts.map +1 -0
- package/dist/src/components/EncoreApp.d.ts +5 -1
- package/dist/src/components/EncoreApp.d.ts.map +1 -1
- package/dist/src/contexts/EncoreRouterContext.d.ts +10 -0
- package/dist/src/contexts/EncoreRouterContext.d.ts.map +1 -0
- package/dist/src/hooks/useAuthRedirect.d.ts.map +1 -1
- package/dist/src/lib/dynamicModules.d.ts +1 -5
- package/dist/src/lib/dynamicModules.d.ts.map +1 -1
- package/dist/src/lib/moduleRegistry.d.ts +9 -0
- package/dist/src/lib/moduleRegistry.d.ts.map +1 -0
- package/dist/src/lib/packages.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/generate.ts +88 -2723
- package/src/codegen/generator.ts +877 -0
- package/src/codegen/index.ts +3 -0
- package/src/codegen/parser.ts +1614 -0
- package/src/codegen/types.ts +58 -0
- package/src/components/EncoreApp.tsx +75 -22
- package/src/contexts/EncoreRouterContext.ts +28 -0
- package/src/hooks/useAuthRedirect.ts +56 -55
- package/src/lib/packages.ts +8 -15
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface ComponentInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
tags: string[];
|
|
6
|
+
propName: string; // Sanitized name for prop
|
|
7
|
+
propType: string; // TypeScript type for the prop
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ArrayContainerInfo {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
propName: string;
|
|
14
|
+
components: ComponentInfo[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SliderInfo {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
arrayContainer: ArrayContainerInfo | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface InputGroupInfo {
|
|
24
|
+
groupName: string;
|
|
25
|
+
groupType: string; // "single" for radio button behavior
|
|
26
|
+
elements: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface FormInfo {
|
|
33
|
+
formId: string;
|
|
34
|
+
formName: string;
|
|
35
|
+
submitButtonId?: string;
|
|
36
|
+
inputs: Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
type: string;
|
|
40
|
+
propName: string; // Sanitized and qualified prop name
|
|
41
|
+
_parentPath?: string[]; // Temporary: parent path for qualification
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SelectInputInfo {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
propName: string; // e.g., "contractType" for value prop, generates onContractTypeChange handler
|
|
49
|
+
_parentPath?: string[]; // For qualification if duplicates
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ActionButtonInfo {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
propName: string; // e.g., "bookButton" generates onBookButtonClick handler
|
|
56
|
+
actionType: string; // "remote", "link", etc.
|
|
57
|
+
_parentPath?: string[];
|
|
58
|
+
}
|
|
@@ -19,15 +19,35 @@ import EncoreRepeatingContainerContext, {
|
|
|
19
19
|
type RepeatingContainerControl,
|
|
20
20
|
} from "../contexts/EncoreRepeatingContainerContext";
|
|
21
21
|
import DynamicComponent from "./DynamicComponent";
|
|
22
|
-
import { Link } from "react-router-dom";
|
|
22
|
+
// import { Link } from "react-router-dom"; // Removed dependency
|
|
23
|
+
import { useEncoreRouter } from "../contexts/EncoreRouterContext";
|
|
23
24
|
import { usePusherUpdates } from "../hooks/usePusherUpdates";
|
|
24
25
|
|
|
26
|
+
// Simple internal Link component that uses our router context
|
|
27
|
+
const Link = ({ to, children, style, ...props }: any) => {
|
|
28
|
+
const { navigate } = useEncoreRouter();
|
|
29
|
+
return (
|
|
30
|
+
<a
|
|
31
|
+
href={to}
|
|
32
|
+
onClick={(e) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
navigate(to);
|
|
35
|
+
}}
|
|
36
|
+
style={{ cursor: "pointer", ...style }}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</a>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
25
44
|
type Props = {
|
|
26
45
|
appId: string;
|
|
27
46
|
pageId?: string;
|
|
28
47
|
componentId?: string;
|
|
29
48
|
fallback?: React.ReactNode;
|
|
30
49
|
onSizeChange?: (size: { width: number; height: number }) => void;
|
|
50
|
+
onContentSizeChange?: (size: { width: number; height: number }) => void;
|
|
31
51
|
onAction?: (payload: EncoreActionPayload) => void | Promise<void>;
|
|
32
52
|
data?: Record<string, string | number | any[]>;
|
|
33
53
|
// When provided, force the runtime to load from either remote or local sources
|
|
@@ -66,6 +86,7 @@ const EncoreApp = ({
|
|
|
66
86
|
componentId,
|
|
67
87
|
fallback,
|
|
68
88
|
onSizeChange,
|
|
89
|
+
onContentSizeChange,
|
|
69
90
|
onAction,
|
|
70
91
|
data,
|
|
71
92
|
source,
|
|
@@ -102,6 +123,31 @@ const EncoreApp = ({
|
|
|
102
123
|
const setPageId = useEncoreState(setPageIdSelector);
|
|
103
124
|
const assetsById = useEncoreState(assetsByIdSelector);
|
|
104
125
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
126
|
+
const contentWrapperRef = useRef<HTMLDivElement | null>(null);
|
|
127
|
+
|
|
128
|
+
// Monitor content size changes
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (!onContentSizeChange) return;
|
|
131
|
+
const element = contentWrapperRef.current;
|
|
132
|
+
if (!element) return;
|
|
133
|
+
|
|
134
|
+
const notify = () => {
|
|
135
|
+
// Use scroll dimensions to get full size including overflow
|
|
136
|
+
onContentSizeChange({
|
|
137
|
+
width: element.scrollWidth,
|
|
138
|
+
height: element.scrollHeight,
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const observer = new ResizeObserver(() => {
|
|
143
|
+
notify();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Emit initial size
|
|
147
|
+
notify();
|
|
148
|
+
observer.observe(element);
|
|
149
|
+
return () => observer.disconnect();
|
|
150
|
+
}, [onContentSizeChange]);
|
|
105
151
|
|
|
106
152
|
// State to force DynamicComponent reload when updates are received
|
|
107
153
|
const [reloadKey, setReloadKey] = useState<string | number>(0);
|
|
@@ -730,28 +776,35 @@ const EncoreApp = ({
|
|
|
730
776
|
return (
|
|
731
777
|
<div
|
|
732
778
|
ref={containerRef}
|
|
733
|
-
style={{
|
|
779
|
+
style={{
|
|
780
|
+
width: "100%",
|
|
781
|
+
height: "100%",
|
|
782
|
+
position: "relative",
|
|
783
|
+
overflow: "hidden",
|
|
784
|
+
}}
|
|
734
785
|
>
|
|
735
|
-
<
|
|
736
|
-
<
|
|
737
|
-
<
|
|
738
|
-
<
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
<
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
786
|
+
<div ref={contentWrapperRef} style={{ width: "100%", minHeight: "100%" }}>
|
|
787
|
+
<Suspense fallback={fallback || <div />}>
|
|
788
|
+
<EncoreComponentIdContext.Provider value={{ componentId }}>
|
|
789
|
+
<EncoreActionContext.Provider value={{ onAction }}>
|
|
790
|
+
<EncoreRepeatingContainerContext.Provider
|
|
791
|
+
value={repeatingContainerContextValue}
|
|
792
|
+
>
|
|
793
|
+
<EncoreBindingContext.Provider value={context}>
|
|
794
|
+
<DynamicComponent
|
|
795
|
+
name={`${appId}/draft/components/${pageId}`}
|
|
796
|
+
fallback={fallback}
|
|
797
|
+
reloadKey={reloadKey}
|
|
798
|
+
componentCode={componentCode}
|
|
799
|
+
>
|
|
800
|
+
{" "}
|
|
801
|
+
</DynamicComponent>
|
|
802
|
+
</EncoreBindingContext.Provider>
|
|
803
|
+
</EncoreRepeatingContainerContext.Provider>
|
|
804
|
+
</EncoreActionContext.Provider>
|
|
805
|
+
</EncoreComponentIdContext.Provider>
|
|
806
|
+
</Suspense>
|
|
807
|
+
</div>
|
|
755
808
|
</div>
|
|
756
809
|
);
|
|
757
810
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
|
+
|
|
3
|
+
export type EncoreRouterContextType = {
|
|
4
|
+
navigate: (path: string) => void;
|
|
5
|
+
pathname: string;
|
|
6
|
+
searchParams: URLSearchParams;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// Default implementation using window for basic fallback
|
|
10
|
+
const defaultRouter: EncoreRouterContextType = {
|
|
11
|
+
navigate: (path: string) => {
|
|
12
|
+
if (typeof window !== "undefined") {
|
|
13
|
+
window.location.href = path;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "",
|
|
17
|
+
searchParams:
|
|
18
|
+
typeof window !== "undefined"
|
|
19
|
+
? new URLSearchParams(window.location.search)
|
|
20
|
+
: new URLSearchParams(),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const EncoreRouterContext =
|
|
24
|
+
React.createContext<EncoreRouterContextType>(defaultRouter);
|
|
25
|
+
|
|
26
|
+
export const useEncoreRouter = () => useContext(EncoreRouterContext);
|
|
27
|
+
|
|
28
|
+
export default EncoreRouterContext;
|
|
@@ -1,63 +1,64 @@
|
|
|
1
1
|
import EncoreAppContext from "../contexts/EncoreAppContext";
|
|
2
2
|
import useEncoreState from "../stores/useEncoreState";
|
|
3
|
-
import {
|
|
3
|
+
import { useEncoreRouter } from "../contexts/EncoreRouterContext";
|
|
4
4
|
import { useContext, useEffect, useMemo } from "react";
|
|
5
5
|
|
|
6
6
|
const useAuthRedirect = () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
7
|
+
const { searchParams, pathname, navigate } = useEncoreRouter();
|
|
8
|
+
|
|
9
|
+
// Polyfill location object style access if needed, or just use pathname directly since that's all that WAS used
|
|
10
|
+
const location = { pathname };
|
|
11
|
+
|
|
12
|
+
const appId =
|
|
13
|
+
searchParams.get("appId") ||
|
|
14
|
+
location.pathname.split("/apps/")[1]?.split("/")[0];
|
|
15
|
+
const pageId =
|
|
16
|
+
searchParams.get("pageId") ||
|
|
17
|
+
location.pathname.split("/pages/")[1]?.split("/")[0];
|
|
18
|
+
const noRedirect = searchParams.get("noRedirect");
|
|
19
|
+
|
|
20
|
+
const { app } = useContext(EncoreAppContext);
|
|
21
|
+
const accessToken = useEncoreState((state) => state.accessToken);
|
|
22
|
+
|
|
23
|
+
const navMap = app.data?.app?.navigationMap;
|
|
24
|
+
const loginPageId = app.data?.app?.loginPageId;
|
|
25
|
+
const isAuthenticated = accessToken && accessToken?.expireAt > Date.now();
|
|
26
|
+
|
|
27
|
+
// Some apps have multiple pages in the signin flow.
|
|
28
|
+
// Existing apps don't have a "page requires auth" declaration (or the inverse) so we must infer it.
|
|
29
|
+
// We infer it using pages linked from the tagged login page
|
|
30
|
+
const loginPages = useMemo(() => {
|
|
31
|
+
if (!navMap) return [];
|
|
32
|
+
|
|
33
|
+
const findConnectedPages = (
|
|
34
|
+
pageMap: Record<string, { linksTo: string[] }>,
|
|
35
|
+
currentPage: string,
|
|
36
|
+
visited = new Set()
|
|
37
|
+
): string[] => {
|
|
38
|
+
if (visited.has(currentPage)) return [];
|
|
39
|
+
visited.add(currentPage);
|
|
40
|
+
|
|
41
|
+
const directLinks = pageMap[currentPage]?.linksTo || [];
|
|
42
|
+
const indirectLinks = directLinks.flatMap((link) =>
|
|
43
|
+
findConnectedPages(pageMap, link, visited)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return [...directLinks, ...indirectLinks];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const result = new Set(findConnectedPages(navMap.pages, loginPageId));
|
|
50
|
+
result.add(loginPageId);
|
|
51
|
+
|
|
52
|
+
return Array.from(result);
|
|
53
|
+
}, [navMap]);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (noRedirect) return;
|
|
57
|
+
if (isAuthenticated) return;
|
|
58
|
+
if (!loginPageId || loginPages.includes(pageId as string)) return;
|
|
59
|
+
|
|
60
|
+
navigate(`/apps/${appId}/pages/${loginPageId}`);
|
|
61
|
+
}, [loginPageId, pageId, isAuthenticated, noRedirect]);
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
export default useAuthRedirect;
|
package/src/lib/packages.ts
CHANGED
|
@@ -1,33 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
//
|
|
3
|
-
// Link,
|
|
4
|
-
// useSearchParams,
|
|
5
|
-
// useNavigate,
|
|
6
|
-
// useLocation,
|
|
7
|
-
// } from "react-router-dom";
|
|
2
|
+
// React router dom dependency removed
|
|
8
3
|
import axios from "axios";
|
|
9
4
|
import EncoreComponents from "../components";
|
|
10
|
-
import * as EncoreApp from "../app"
|
|
5
|
+
import * as EncoreApp from "../app";
|
|
11
6
|
// Note: ../app module doesn't exist in encore-lib, commenting out for now
|
|
12
7
|
|
|
13
8
|
export type Package = {
|
|
14
|
-
|
|
9
|
+
exports: unknown;
|
|
15
10
|
};
|
|
16
11
|
|
|
17
12
|
// These are the packages that will be available to remotely loaded code
|
|
18
13
|
const Packages: Record<string, () => Package> = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
axios: () => ({ exports: axios }),
|
|
24
|
-
react: () => ({ exports: React }),
|
|
14
|
+
"@/bravo/app": () => ({ exports: EncoreApp }), // Disabled: app module doesn't exist
|
|
15
|
+
// React router dom removed
|
|
16
|
+
axios: () => ({ exports: axios }),
|
|
17
|
+
react: () => ({ exports: React }),
|
|
25
18
|
};
|
|
26
19
|
|
|
27
20
|
// Enable this if you want to use components from local project
|
|
28
21
|
const USE_LOCAL_BRAVO_RN = true;
|
|
29
22
|
if (USE_LOCAL_BRAVO_RN) {
|
|
30
|
-
|
|
23
|
+
Packages["@/bravo/components"] = () => ({ exports: EncoreComponents });
|
|
31
24
|
}
|
|
32
25
|
|
|
33
26
|
export default Packages;
|