@ramesesinc/platform-core 0.1.3 → 0.1.4

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.
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { compile } from "expression-eval";
2
3
  import { useEffect, useState } from "react";
3
4
  import { usePageContext } from "../../core/PageContext";
4
5
  const UIComponent = (props) => {
@@ -20,7 +21,8 @@ const UIComponent = (props) => {
20
21
  return JSON.stringify(val !== null && val !== void 0 ? val : null);
21
22
  });
22
23
  // eslint-disable-next-line no-new-func
23
- return Boolean(new Function(`return (${jsExpr})`)());
24
+ // return Boolean(new Function(`return (${jsExpr})`)());
25
+ return Boolean(compile(jsExpr)(context));
24
26
  }
25
27
  catch (e) {
26
28
  console.warn("visibleWhen eval error:", e);
@@ -1,3 +1,4 @@
1
+ import { compile } from "expression-eval";
1
2
  import { useEffect, useState } from "react";
2
3
  import { usePageContext } from "../../core/PageContext";
3
4
 
@@ -28,7 +29,8 @@ const UIComponent = (props: UIComponentProps) => {
28
29
  return JSON.stringify(val ?? null);
29
30
  });
30
31
  // eslint-disable-next-line no-new-func
31
- return Boolean(new Function(`return (${jsExpr})`)());
32
+ // return Boolean(new Function(`return (${jsExpr})`)());
33
+ return Boolean(compile(jsExpr)(context));
32
34
  } catch (e) {
33
35
  console.warn("visibleWhen eval error:", e);
34
36
  return true;
@@ -1,5 +1,4 @@
1
1
  type LabelProps = {
2
- label?: string;
3
2
  depends?: string;
4
3
  expr?: string;
5
4
  html?: boolean;
@@ -5,7 +5,6 @@ import { render } from "../../lib/utils/ExprUtil";
5
5
  import UIComponent from "../common/UIComponent";
6
6
 
7
7
  type LabelProps = {
8
- label?: string;
9
8
  depends?: string;
10
9
  expr?: string;
11
10
  html?: boolean;
@@ -1,11 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { DynamicComponent } from "../../core/DynamicComponent";
2
+ import { localAPI } from "@ramesesinc/lib/local-api";
3
+ import React, { useEffect, useMemo, useState } from "react";
3
4
  import { useApp } from "../../core/AppContext";
5
+ import { DynamicComponent } from "../../core/DynamicComponent";
4
6
  import useDependHandler from "../../core/UIDependHandler";
5
- import { localAPI } from "@ramesesinc/lib/local-api";
6
- import { useEffect, useMemo, useState } from "react";
7
7
  import UIComponent from "../common/UIComponent";
8
- import React from "react";
9
8
  // ─────────────────────────────────────────────────────────────────────────────
10
9
  // Attr Parser
11
10
  // Handles both strict JSON {"key":"value"} and JS object literals {key: "value"}
@@ -19,7 +18,9 @@ function parseAttr(attrStr) {
19
18
  catch (_a) {
20
19
  try {
21
20
  // eslint-disable-next-line no-new-func
22
- return new Function(`"use strict"; return (${attrStr})`)();
21
+ // return new Function(`"use strict"; return (${attrStr})`)();
22
+ const normalized = attrStr.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)(\s*:)/g, '$1"$2"$3').replace(/'([^'\\]*(\\.[^'\\]*)*)'/g, '"$1"');
23
+ return JSON.parse(normalized);
23
24
  }
24
25
  catch (_b) {
25
26
  console.warn("HtmlForm: could not parse attr →", attrStr);
@@ -59,9 +60,7 @@ function domAttrsToProps(el) {
59
60
  if (["component", "attr"].includes(attr.name))
60
61
  continue;
61
62
  const reactName = (_a = ATTR_MAP[attr.name]) !== null && _a !== void 0 ? _a : attr.name;
62
- props[reactName] = attr.name === "style"
63
- ? styleStringToObject(attr.value)
64
- : attr.value;
63
+ props[reactName] = attr.name === "style" ? styleStringToObject(attr.value) : attr.value;
65
64
  }
66
65
  return props;
67
66
  }
@@ -95,16 +94,14 @@ function walkNode(node, parentTag) {
95
94
  if (tag === "span" && componentName) {
96
95
  const attr = parseAttr(el.getAttribute("attr"));
97
96
  const config = { component: componentName, attr };
98
- return (_jsx(DynamicComponent, { config: config }, nextKey()));
97
+ return _jsx(DynamicComponent, { config: config }, nextKey());
99
98
  }
100
99
  // ── Regular element → recurse ──────────────────────────────────────────
101
100
  const props = Object.assign(Object.assign({}, domAttrsToProps(el)), { key: nextKey() });
102
101
  const children = Array.from(el.childNodes)
103
102
  .map((child) => walkNode(child, tag)) // pass current tag as parentTag
104
103
  .filter((n) => n !== null && n !== undefined);
105
- return children.length > 0
106
- ? React.createElement(tag, props, ...children)
107
- : React.createElement(tag, props);
104
+ return children.length > 0 ? React.createElement(tag, props, ...children) : React.createElement(tag, props);
108
105
  }
109
106
  return null;
110
107
  }
@@ -1,10 +1,9 @@
1
- import { DynamicComponent } from "../../core/DynamicComponent";
1
+ import { localAPI } from "@ramesesinc/lib/local-api";
2
+ import React, { useEffect, useMemo, useState } from "react";
2
3
  import { useApp } from "../../core/AppContext";
4
+ import { DynamicComponent } from "../../core/DynamicComponent";
3
5
  import useDependHandler from "../../core/UIDependHandler";
4
- import { localAPI } from "@ramesesinc/lib/local-api";
5
- import { useEffect, useMemo, useState } from "react";
6
6
  import UIComponent from "../common/UIComponent";
7
- import React from "react";
8
7
 
9
8
  // ─────────────────────────────────────────────────────────────────────────────
10
9
  // Types
@@ -33,7 +32,9 @@ function parseAttr(attrStr: string | null): any {
33
32
  } catch {
34
33
  try {
35
34
  // eslint-disable-next-line no-new-func
36
- return new Function(`"use strict"; return (${attrStr})`)();
35
+ // return new Function(`"use strict"; return (${attrStr})`)();
36
+ const normalized = attrStr.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)(\s*:)/g, '$1"$2"$3').replace(/'([^'\\]*(\\.[^'\\]*)*)'/g, '"$1"');
37
+ return JSON.parse(normalized);
37
38
  } catch {
38
39
  console.warn("HtmlForm: could not parse attr →", attrStr);
39
40
  return {};
@@ -74,9 +75,7 @@ function domAttrsToProps(el: Element): Record<string, any> {
74
75
  for (const attr of Array.from(el.attributes)) {
75
76
  if (["component", "attr"].includes(attr.name)) continue;
76
77
  const reactName = ATTR_MAP[attr.name] ?? attr.name;
77
- props[reactName] = attr.name === "style"
78
- ? styleStringToObject(attr.value)
79
- : attr.value;
78
+ props[reactName] = attr.name === "style" ? styleStringToObject(attr.value) : attr.value;
80
79
  }
81
80
  return props;
82
81
  }
@@ -116,23 +115,16 @@ function walkNode(node: ChildNode, parentTag?: string): React.ReactNode {
116
115
  if (tag === "span" && componentName) {
117
116
  const attr: any = parseAttr(el.getAttribute("attr"));
118
117
  const config: SentinelConfig = { component: componentName, attr };
119
- return (
120
- <DynamicComponent
121
- key={nextKey()}
122
- config={config}
123
- />
124
- );
118
+ return <DynamicComponent key={nextKey()} config={config} />;
125
119
  }
126
120
 
127
121
  // ── Regular element → recurse ──────────────────────────────────────────
128
122
  const props = { ...domAttrsToProps(el), key: nextKey() };
129
123
  const children: React.ReactNode[] = Array.from(el.childNodes)
130
- .map((child) => walkNode(child, tag)) // pass current tag as parentTag
124
+ .map((child) => walkNode(child, tag)) // pass current tag as parentTag
131
125
  .filter((n) => n !== null && n !== undefined);
132
126
 
133
- return children.length > 0
134
- ? React.createElement(tag, props, ...children)
135
- : React.createElement(tag, props);
127
+ return children.length > 0 ? React.createElement(tag, props, ...children) : React.createElement(tag, props);
136
128
  }
137
129
 
138
130
  return null;
@@ -176,11 +168,9 @@ const HtmlForm = (props: HtmlFormProps) => {
176
168
 
177
169
  return (
178
170
  <UIComponent {...(props ?? {})}>
179
- <div className="h-[calc(100vh-80px)] overflow-y-auto overflow-x-auto">
180
- {reactTree}
181
- </div>
171
+ <div className="h-[calc(100vh-80px)] overflow-y-auto overflow-x-auto">{reactTree}</div>
182
172
  </UIComponent>
183
173
  );
184
174
  };
185
175
 
186
- export default HtmlForm;
176
+ export default HtmlForm;
@@ -8,37 +8,26 @@ import { render } from "../../lib/utils/ExprUtil";
8
8
  import UIComponent from "../common/UIComponent";
9
9
  const Html = (props) => {
10
10
  const { depends, expr, templateid } = props !== null && props !== void 0 ? props : {};
11
- const [htmlTemplate, setHtmlTemplate] = useState(""); // Store the raw template
12
- const [renderedHtml, setRenderedHtml] = useState(""); // Store the rendered HTML
13
- const [resData, setResData] = useState({});
11
+ const [htmlTemplate, setHtmlTemplate] = useState(""); // raw template
12
+ const [renderedHtml, setRenderedHtml] = useState(""); // rendered output
14
13
  const pageContext = usePageContext();
15
14
  const binding = pageContext === null || pageContext === void 0 ? void 0 : pageContext.binding;
16
15
  const { tenant, module } = useApp();
17
- const loadHtmlContent = async (tempid) => {
16
+ // single render function used everywhere
17
+ const renderTemplate = (template) => {
18
18
  var _a;
19
+ if (!template)
20
+ return "";
19
21
  try {
20
- const htmlContent = await localAPI.useMgmt(tenant, module).get("html_templates", tempid);
21
- if (htmlContent != null) {
22
- // Store the raw template
23
- setHtmlTemplate(htmlContent.htmlCode);
24
- // Fetch data from API
25
- const data = (_a = pageContext === null || pageContext === void 0 ? void 0 : pageContext.getAllData()) !== null && _a !== void 0 ? _a : {};
26
- // Render the template with the data
27
- const rendered = render(htmlContent.htmlCode, data);
28
- setRenderedHtml(rendered);
29
- }
30
- else {
31
- setHtmlTemplate("");
32
- setRenderedHtml("");
33
- }
22
+ const data = (_a = pageContext === null || pageContext === void 0 ? void 0 : pageContext.getAllData()) !== null && _a !== void 0 ? _a : {};
23
+ return render(template, data);
34
24
  }
35
25
  catch (error) {
36
- console.error("Error loading HTML template:", error);
37
- setHtmlTemplate("");
38
- setRenderedHtml("");
26
+ console.error("Template render error:", error);
27
+ return template;
39
28
  }
40
29
  };
41
- // Render the expression using ExprUtil
30
+ // single expression render
42
31
  const renderExpression = (expression) => {
43
32
  var _a;
44
33
  if (!expression)
@@ -52,34 +41,46 @@ const Html = (props) => {
52
41
  return expression;
53
42
  }
54
43
  };
55
- const initialValue = renderExpression(expr !== null && expr !== void 0 ? expr : "");
56
- const [value, setValue] = useState(initialValue);
57
- const onRefresh = () => {
58
- const newValue = renderExpression(expr !== null && expr !== void 0 ? expr : "");
59
- setValue(newValue);
60
- // Re-render the HTML template if data changes
61
- if (htmlTemplate && resData) {
62
- const rendered = render(htmlTemplate, resData);
63
- setRenderedHtml(rendered);
64
- }
65
- };
66
- useDependHandler({ name: depends, onRefresh });
44
+ // load template only once when templateid changes
67
45
  useEffect(() => {
68
- if (templateid != null && templateid.trim() !== "") {
69
- loadHtmlContent(templateid);
70
- }
46
+ if (templateid == null || templateid.trim() === "")
47
+ return;
48
+ const loadHtmlContent = async () => {
49
+ try {
50
+ const htmlContent = await localAPI.useMgmt(tenant, module).get("html_templates", templateid);
51
+ if (htmlContent != null) {
52
+ setHtmlTemplate(htmlContent.htmlCode); // ← just store raw template
53
+ }
54
+ else {
55
+ setHtmlTemplate("");
56
+ }
57
+ }
58
+ catch (error) {
59
+ console.error("Error loading HTML template:", error);
60
+ setHtmlTemplate("");
61
+ }
62
+ };
63
+ loadHtmlContent();
71
64
  }, [templateid]);
65
+ // ← re-render whenever template OR binding data changes
72
66
  useEffect(() => {
73
- const newValue = renderExpression(expr !== null && expr !== void 0 ? expr : "");
74
- setValue(newValue);
75
- }, [binding === null || binding === void 0 ? void 0 : binding.raw, expr]);
76
- // Re-render template when data changes
77
- useEffect(() => {
78
- if (htmlTemplate && resData && Object.keys(resData).length > 0) {
79
- const rendered = render(htmlTemplate, resData);
80
- setRenderedHtml(rendered);
67
+ if (htmlTemplate) {
68
+ setRenderedHtml(renderTemplate(htmlTemplate));
81
69
  }
82
- }, [resData, htmlTemplate]);
70
+ else if (expr) {
71
+ setRenderedHtml(renderExpression(expr));
72
+ }
73
+ }, [htmlTemplate, binding === null || binding === void 0 ? void 0 : binding.raw]); // ← binding.raw triggers when data changes
74
+ // ← onRefresh called when depends changes
75
+ const onRefresh = () => {
76
+ if (htmlTemplate) {
77
+ setRenderedHtml(renderTemplate(htmlTemplate));
78
+ }
79
+ else if (expr) {
80
+ setRenderedHtml(renderExpression(expr));
81
+ }
82
+ };
83
+ useDependHandler({ name: depends, onRefresh });
83
84
  return (_jsx(UIComponent, Object.assign({}, (props !== null && props !== void 0 ? props : {}), { children: _jsx("div", { className: "h-[calc(100vh-80px)] overflow-y-auto overflow-x-auto", dangerouslySetInnerHTML: { __html: renderedHtml } }) })));
84
85
  };
85
86
  export default Html;
@@ -16,39 +16,26 @@ type HtmlLabelProps = {
16
16
 
17
17
  const Html = (props: HtmlLabelProps) => {
18
18
  const { depends, expr, templateid } = props ?? {};
19
- const [htmlTemplate, setHtmlTemplate] = useState(""); // Store the raw template
20
- const [renderedHtml, setRenderedHtml] = useState(""); // Store the rendered HTML
21
- const [resData, setResData] = useState<Record<string, any>>({});
19
+
20
+ const [htmlTemplate, setHtmlTemplate] = useState(""); // raw template
21
+ const [renderedHtml, setRenderedHtml] = useState(""); // rendered output
22
22
  const pageContext = usePageContext();
23
23
  const binding = pageContext?.binding;
24
24
  const { tenant, module } = useApp();
25
25
 
26
- const loadHtmlContent = async (tempid: string) => {
26
+ // single render function used everywhere
27
+ const renderTemplate = (template: string) => {
28
+ if (!template) return "";
27
29
  try {
28
- const htmlContent = await localAPI.useMgmt(tenant!, module!).get("html_templates", tempid);
29
-
30
- if (htmlContent != null) {
31
- // Store the raw template
32
- setHtmlTemplate(htmlContent.htmlCode);
33
-
34
- // Fetch data from API
35
- const data = pageContext?.getAllData() ?? {};
36
-
37
- // Render the template with the data
38
- const rendered = render(htmlContent.htmlCode, data);
39
- setRenderedHtml(rendered);
40
- } else {
41
- setHtmlTemplate("");
42
- setRenderedHtml("");
43
- }
30
+ const data = pageContext?.getAllData() ?? {};
31
+ return render(template, data);
44
32
  } catch (error) {
45
- console.error("Error loading HTML template:", error);
46
- setHtmlTemplate("");
47
- setRenderedHtml("");
33
+ console.error("Template render error:", error);
34
+ return template;
48
35
  }
49
36
  };
50
37
 
51
- // Render the expression using ExprUtil
38
+ // single expression render
52
39
  const renderExpression = (expression: string) => {
53
40
  if (!expression) return "";
54
41
  try {
@@ -60,52 +47,49 @@ const Html = (props: HtmlLabelProps) => {
60
47
  }
61
48
  };
62
49
 
63
- const initialValue = renderExpression(expr ?? "");
64
- const [value, setValue] = useState(initialValue);
65
-
66
- const onRefresh = () => {
67
- const newValue = renderExpression(expr ?? "");
68
- setValue(newValue);
69
-
70
- // Re-render the HTML template if data changes
71
- if (htmlTemplate && resData) {
72
- const rendered = render(htmlTemplate, resData);
73
- setRenderedHtml(rendered);
74
- }
75
- };
76
-
77
- useDependHandler({ name: depends, onRefresh });
78
-
50
+ // load template only once when templateid changes
79
51
  useEffect(() => {
80
- if (templateid != null && templateid.trim() !== "") {
81
- loadHtmlContent(templateid);
82
- }
52
+ if (templateid == null || templateid.trim() === "") return;
53
+
54
+ const loadHtmlContent = async () => {
55
+ try {
56
+ const htmlContent = await localAPI.useMgmt(tenant!, module!).get("html_templates", templateid);
57
+ if (htmlContent != null) {
58
+ setHtmlTemplate(htmlContent.htmlCode); // ← just store raw template
59
+ } else {
60
+ setHtmlTemplate("");
61
+ }
62
+ } catch (error) {
63
+ console.error("Error loading HTML template:", error);
64
+ setHtmlTemplate("");
65
+ }
66
+ };
67
+
68
+ loadHtmlContent();
83
69
  }, [templateid]);
84
70
 
71
+ // ← re-render whenever template OR binding data changes
85
72
  useEffect(() => {
86
- const newValue = renderExpression(expr ?? "");
87
- setValue(newValue);
88
- }, [binding?.raw, expr]);
73
+ if (htmlTemplate) {
74
+ setRenderedHtml(renderTemplate(htmlTemplate));
75
+ } else if (expr) {
76
+ setRenderedHtml(renderExpression(expr));
77
+ }
78
+ }, [htmlTemplate, binding?.raw]); // ← binding.raw triggers when data changes
89
79
 
90
- // Re-render template when data changes
91
- useEffect(() => {
92
- if (htmlTemplate && resData && Object.keys(resData).length > 0) {
93
- const rendered = render(htmlTemplate, resData);
94
- setRenderedHtml(rendered);
80
+ // onRefresh called when depends changes
81
+ const onRefresh = () => {
82
+ if (htmlTemplate) {
83
+ setRenderedHtml(renderTemplate(htmlTemplate));
84
+ } else if (expr) {
85
+ setRenderedHtml(renderExpression(expr));
95
86
  }
96
- }, [resData, htmlTemplate]);
87
+ };
88
+
89
+ useDependHandler({ name: depends, onRefresh });
97
90
 
98
91
  return (
99
92
  <UIComponent {...(props ?? {})}>
100
- {/* Debug info - remove in production */}
101
- {/* <details style={{ marginBottom: "20px" }}>
102
- <summary>Debug Data</summary>
103
- <pre style={{ fontSize: "10px", maxHeight: "200px", overflow: "auto" }}>
104
- {JSON.stringify(resData, null, 2)}
105
- </pre>
106
- </details> */}
107
-
108
- {/* Rendered HTML */}
109
93
  <div className="h-[calc(100vh-80px)] overflow-y-auto overflow-x-auto" dangerouslySetInnerHTML={{ __html: renderedHtml }} />
110
94
  </UIComponent>
111
95
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramesesinc/platform-core",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Platform Core Library",
5
5
  "author": "Rameses Systems Inc.",
6
6
  "license": "MIT",
@@ -28,12 +28,12 @@
28
28
  "copy-res": "node -e \"const fs=require('fs');const apps=['etracs','agos','filipizen'];apps.forEach(a=>{const d='../../apps/'+a+'/public/_res';fs.cpSync('src/public/_res',d,{recursive:true,force:true})})\""
29
29
  },
30
30
  "peerDependencies": {
31
+ "clsx": "^2.1.1",
32
+ "lucide-react": "^0.553.0",
31
33
  "next": ">=13.5.6 <15.0.0",
32
34
  "nunjucks": "^3.2.4",
33
35
  "react": ">=18.2.0",
34
- "react-dom": ">=18.2.0",
35
- "lucide-react": "^0.553.0",
36
- "clsx": "^2.1.1"
36
+ "react-dom": ">=18.2.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@ramesesinc/app-forms": "0.1.0",
@@ -50,13 +50,14 @@
50
50
  "clsx": "^2.1.1",
51
51
  "eslint": "^8",
52
52
  "eslint-config-next": "14.2.9",
53
+ "next": ">=13.5.6 <15.0.0",
53
54
  "postcss": "^8",
54
55
  "postcss-cli": "^11.0.1",
55
56
  "supports-color": "^10.2.2",
56
57
  "tailwind-merge": "^3.3.1",
57
58
  "tailwindcss": "^3.4.1",
58
- "typescript": "^5",
59
- "next": ">=13.5.6 <15.0.0"
59
+ "expression-eval": "^5.0.1",
60
+ "typescript": "^5"
60
61
  },
61
62
  "keywords": [
62
63
  "ramesesinc-platform-core"