@ramesesinc/platform-core 0.1.10 → 0.1.11

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.
Files changed (34) hide show
  1. package/dist/components/action/DeleteData.d.ts +1 -0
  2. package/dist/components/action/DeleteData.js +16 -5
  3. package/dist/components/action/ViewPage.d.ts +1 -0
  4. package/dist/components/action/ViewPage.js +4 -2
  5. package/dist/components/common/UIMenu.d.ts +1 -0
  6. package/dist/components/common/UIMenu.js +1 -0
  7. package/dist/components/input/Combo.js +2 -2
  8. package/dist/components/input/YearPicker.js +3 -2
  9. package/dist/components/list/EditableMenu.js +6 -6
  10. package/dist/components/list/TreeMenu.js +18 -1
  11. package/dist/components/table/DataList.js +8 -6
  12. package/dist/components/view/HtmlView.js +1 -1
  13. package/dist/components/view/PageView.d.ts +1 -0
  14. package/dist/components/view/PageView.js +2 -2
  15. package/dist/components/view/PopupView.d.ts +1 -0
  16. package/dist/components/view/PopupView.js +4 -4
  17. package/dist/components/view/RootView.d.ts +1 -0
  18. package/dist/components/view/RootView.js +2 -2
  19. package/dist/components/view/WizardView.d.ts +1 -1
  20. package/dist/components/view/WizardView.js +7 -25
  21. package/dist/core/DynamicComponent.d.ts +2 -1
  22. package/dist/core/DynamicComponent.js +24 -2
  23. package/dist/core/Page.d.ts +1 -0
  24. package/dist/core/Page.js +4 -1
  25. package/dist/core/PageContext.d.ts +1 -0
  26. package/dist/core/PageContext.js +4 -2
  27. package/dist/core/PageViewContext.js +29 -14
  28. package/dist/core/Panel.js +4 -4
  29. package/dist/core/StepHandler.d.ts +1 -1
  30. package/dist/core/StepHandler.js +58 -21
  31. package/dist/index.css +19 -0
  32. package/dist/templates/WizardTemplate.d.ts +3 -0
  33. package/dist/templates/WizardTemplate.js +16 -11
  34. package/package.json +1 -1
@@ -8,6 +8,7 @@ interface DeleteDataProps extends AbstractComponent {
8
8
  opt?: Record<string, any>;
9
9
  iconOnly?: boolean;
10
10
  params: Record<string, any>;
11
+ confirmMessage?: string;
11
12
  }
12
13
  declare const DeleteData: (props: DeleteDataProps) => import("react/jsx-runtime").JSX.Element;
13
14
  export default DeleteData;
@@ -1,20 +1,23 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AlertCircle } from "lucide-react";
4
+ import { useState } from "react";
3
5
  import { usePageContext } from "../../core/PageContext";
4
6
  import { usePageViewContext } from "../../core/PageViewContext";
5
7
  import { useRowContext } from "../../core/RowContext";
6
8
  import { getValue } from "../../lib/utils/BeanUtils";
7
9
  import { getUrlPageParams } from "../../lib/utils/PageUtils";
10
+ const ConfirmDialog = ({ message, onConfirm, onCancel }) => (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40", children: _jsxs("div", { className: "bg-white rounded-xl shadow-xl w-full max-w-sm p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-3", children: [_jsx(AlertCircle, { className: "text-red-500 shrink-0", size: 24 }), _jsx("p", { className: "text-base font-semibold text-gray-800", children: "Confirm Deletion" })] }), _jsxs("p", { className: "text-sm mb-6", children: [message, " This cannot be undone."] }), _jsxs("div", { className: "flex justify-end gap-3", children: [_jsx("button", { onClick: onCancel, className: "px-4 py-2 text-sm rounded-md border border-gray-300 text-gray-700 hover:bg-gray-100 transition-colors", children: "Cancel" }), _jsx("button", { onClick: onConfirm, className: "px-4 py-2 text-sm rounded-md bg-red-500 text-white hover:bg-red-600 transition-colors", children: "Delete" })] })] }) }));
8
11
  /* ------------------------------------------------------------------ */
9
12
  /* Component */
10
13
  const DeleteData = (props) => {
11
- const { api, className = "", title, icon, opt = {}, params, iconOnly = false } = props !== null && props !== void 0 ? props : {};
14
+ const { api, className = "", title, icon, opt = {}, params, iconOnly = false, confirmMessage } = props !== null && props !== void 0 ? props : {};
12
15
  const { data = {} } = opt;
13
16
  const rowContext = useRowContext();
14
17
  const pageView = usePageViewContext();
15
18
  const pageContext = usePageContext();
16
- /* ---------------------- Events ---------------------- */
17
- const handleClick = async () => {
19
+ const [showConfirm, setShowConfirm] = useState(false);
20
+ const doDelete = async () => {
18
21
  var _a, _b;
19
22
  try {
20
23
  if (!api)
@@ -31,13 +34,21 @@ const DeleteData = (props) => {
31
34
  const d = getValue(params, Object.assign(Object.assign({}, urlParams === null || urlParams === void 0 ? void 0 : urlParams.params), vdata));
32
35
  await (pageContext === null || pageContext === void 0 ? void 0 : pageContext.execService(api, vdata));
33
36
  }
34
- // pageContext?.refresh();
37
+ pageContext === null || pageContext === void 0 ? void 0 : pageContext.notifyDepends("datalist");
35
38
  }
36
39
  catch (err) {
37
40
  alert(err.message);
38
41
  }
39
42
  };
43
+ const handleClick = () => setShowConfirm(true);
44
+ const handleConfirm = async () => {
45
+ setShowConfirm(false);
46
+ await doDelete();
47
+ };
48
+ const handleCancel = () => setShowConfirm(false);
49
+ const itemLabel = (data === null || data === void 0 ? void 0 : data._id) ? `Delete item ${data._id}?` : "Delete this item?";
50
+ const message = confirmMessage !== null && confirmMessage !== void 0 ? confirmMessage : itemLabel;
40
51
  /* ---------------------- Render ---------------------- */
41
- return (_jsx(_Fragment, { children: iconOnly ? (_jsx("span", { onClick: handleClick, className: `cursor-pointer ${className}`, children: icon })) : (_jsxs("button", { onClick: handleClick, className: `px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors ${className}`, children: [icon && _jsx("span", { className: "dl-action-icon", children: icon }), title || "Remove"] })) }));
52
+ return (_jsxs(_Fragment, { children: [showConfirm && _jsx(ConfirmDialog, { message: message, onConfirm: handleConfirm, onCancel: handleCancel }), iconOnly ? (_jsx("span", { onClick: handleClick, className: `cursor-pointer ${className}`, children: icon })) : (_jsxs("button", { onClick: handleClick, className: `px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors ${className}`, children: [icon && _jsx("span", { className: "dl-action-icon", children: icon }), title || "Remove"] }))] }));
42
53
  };
43
54
  export default DeleteData;
@@ -12,6 +12,7 @@ interface ViewPageProps extends AbstractComponent {
12
12
  popupClassName?: string;
13
13
  variant?: "primary" | "secondary" | "danger" | "text" | "contained" | "outlined";
14
14
  popupOptions?: PopupOptions;
15
+ action?: "read" | "create" | "edit";
15
16
  }
16
17
  declare const ViewPage: (props: ViewPageProps) => import("react/jsx-runtime").JSX.Element;
17
18
  export default ViewPage;
@@ -11,7 +11,7 @@ import { usePopupView } from "../view/PopupView";
11
11
  /* ------------------------------------------------------------------ */
12
12
  /* Component */
13
13
  const ViewPage = (props) => {
14
- const { url, mode, className = "", title, icon, iconOnly = false, opt = {}, popupClassName = "", popupOptions = {}, variant } = props !== null && props !== void 0 ? props : {};
14
+ const { url, mode, className = "", title, icon, iconOnly = false, opt = {}, popupClassName = "", popupOptions = {}, variant, action } = props !== null && props !== void 0 ? props : {};
15
15
  const { data = {} } = opt;
16
16
  const dataContext = useDataContext();
17
17
  const pageView = usePageViewContext();
@@ -65,7 +65,9 @@ const ViewPage = (props) => {
65
65
  if (popupClassName != null && popupClassName.trim() !== "") {
66
66
  showPopupOptions.className = popupClassName;
67
67
  }
68
- const showPopupRef = popupView.create({ url: surl, onClose: onClose, eventHandler: handler }).show(showPopupOptions);
68
+ const showPopupRef = popupView
69
+ .create({ url: surl, onClose: onClose, eventHandler: handler, options: { action: "create" } })
70
+ .show(showPopupOptions);
69
71
  popupShowRef.current = showPopupRef;
70
72
  }
71
73
  else if (preferredMode === "window") {
@@ -7,6 +7,7 @@ export type MenuItem = {
7
7
  export type MenuGroup = {
8
8
  title: string;
9
9
  icon?: React.ReactNode | string;
10
+ hidden?: boolean;
10
11
  items: MenuItem[];
11
12
  };
12
13
  export type UIMenuProps = {
@@ -43,6 +43,7 @@ const useUIMenu = (props) => {
43
43
  const mappedItems = categories.map((cat) => ({
44
44
  title: cat.title,
45
45
  icon: cat.icon,
46
+ hidden: cat.hidden,
46
47
  items: sorted.filter((m) => m.category === cat.id),
47
48
  }));
48
49
  setMenus(mappedItems);
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { usePageContext } from "@/core/PageContext";
3
2
  import { ChevronDown } from "lucide-react";
4
3
  import { useEffect, useRef, useState } from "react";
4
+ import { usePageContext } from "../../core/PageContext";
5
5
  import UIComponent from "../common/UIComponent";
6
6
  import useUIInput from "../common/UIInput";
7
7
  const RADIUS_STYLES = {
@@ -23,7 +23,7 @@ const variantStyles = {
23
23
  };
24
24
  const Combo = (props) => {
25
25
  var _a, _b, _c, _d, _e, _f;
26
- const { name, immediate = true, items = [], data = {}, placeholder, variant = "outlined", radius = "md", className = "" } = props !== null && props !== void 0 ? props : {};
26
+ const { name, immediate = true, items = [], data = {}, placeholder, variant = "outlined", radius = "md", className = "", } = props !== null && props !== void 0 ? props : {};
27
27
  const { params } = data !== null && data !== void 0 ? data : {};
28
28
  const { projection } = params !== null && params !== void 0 ? params : {};
29
29
  const selectRef = useRef(null);
@@ -18,9 +18,10 @@ const YearPicker = (props) => {
18
18
  valueRef.current = initialValue !== null && initialValue !== void 0 ? initialValue : "";
19
19
  const [inputValue, setInputValue] = useState(valueRef.current);
20
20
  const handleChange = (e) => {
21
- const year = e.target.value;
21
+ const raw = e.target.value;
22
+ const year = raw !== "" ? parseInt(raw, 10) : "";
22
23
  if (year !== inputValue) {
23
- valueRef.current = year;
24
+ valueRef.current = String(year);
24
25
  setInputValue(valueRef.current);
25
26
  }
26
27
  if (immediate) {
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { DataBindingProvider } from "@/core/DataContext";
3
- import DynamicIcon from "@/core/DynamicIcon";
4
- import { usePageContext } from "@/core/PageContext";
5
- import { usePageViewContext } from "@/core/PageViewContext";
6
- import useDependHandler from "@/core/UIDependHandler";
7
- import { replaceValues } from "@/lib/utils/BeanUtils";
8
2
  import { Eye } from "lucide-react";
9
3
  import { useEffect, useRef, useState } from "react";
4
+ import { DataBindingProvider } from "../../core/DataContext";
5
+ import DynamicIcon from "../../core/DynamicIcon";
6
+ import { usePageContext } from "../../core/PageContext";
7
+ import { usePageViewContext } from "../../core/PageViewContext";
8
+ import useDependHandler from "../../core/UIDependHandler";
9
+ import { replaceValues } from "../../lib/utils/BeanUtils";
10
10
  import ViewPage from "../action/ViewPage";
11
11
  import useUIMenu from "../common/UIMenu";
12
12
  const EditableMenu = (props) => {
@@ -13,9 +13,26 @@ const buildItems = (raw) => {
13
13
  const mapped = [];
14
14
  for (const entry of raw) {
15
15
  if (Array.isArray(entry.items)) {
16
- mapped.push(Object.assign(Object.assign({}, entry), { isDropdown: (_a = entry.isDropdown) !== null && _a !== void 0 ? _a : true, items: buildItems(entry.items) }));
16
+ const visibleItems = entry.items.filter((it) => !it.hidden);
17
+ // category hidden + multiple items → skip entirely
18
+ if (entry.hidden && visibleItems.length !== 1)
19
+ continue;
20
+ // category hidden + only one visible item → show item flat without category header
21
+ if (entry.hidden && visibleItems.length === 1) {
22
+ const lastGroup = mapped[mapped.length - 1];
23
+ if (lastGroup && lastGroup.title === "" && !lastGroup.isDropdown) {
24
+ lastGroup.items.push(visibleItems[0]);
25
+ }
26
+ else {
27
+ mapped.push({ title: "", icon: undefined, isDropdown: false, items: [visibleItems[0]] });
28
+ }
29
+ continue;
30
+ }
31
+ mapped.push(Object.assign(Object.assign({}, entry), { isDropdown: (_a = entry.isDropdown) !== null && _a !== void 0 ? _a : true, items: buildItems(visibleItems) }));
17
32
  }
18
33
  else {
34
+ if (entry.hidden)
35
+ continue;
19
36
  const lastGroup = mapped[mapped.length - 1];
20
37
  if (lastGroup && lastGroup.title === "" && !lastGroup.isDropdown) {
21
38
  lastGroup.items.push(entry);
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { Tooltip } from "@ramesesinc/client";
14
- import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Columns, Eye, Funnel, RefreshCcw, Search, Trash } from "lucide-react";
14
+ import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Columns, Eye, Funnel, RefreshCcw, Search, Trash, } from "lucide-react";
15
15
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
16
16
  import { useApp } from "../../core/AppContext";
17
17
  import { useDataContext } from "../../core/DataContext";
@@ -195,7 +195,9 @@ const InnerDataList = ({ cols, emptyMessage = "No data available", errorMessage,
195
195
  handleSelectionChange([key]);
196
196
  }
197
197
  else {
198
- const newSelection = selectedRows.includes(key) ? selectedRows.filter((k) => k !== key) : [...selectedRows, key];
198
+ const newSelection = selectedRows.includes(key)
199
+ ? selectedRows.filter((k) => k !== key)
200
+ : [...selectedRows, key];
199
201
  handleSelectionChange(newSelection);
200
202
  }
201
203
  }
@@ -244,7 +246,7 @@ const InnerDataList = ({ cols, emptyMessage = "No data available", errorMessage,
244
246
  return col;
245
247
  const currentSort = listHandler.getCurrentSortedColumn();
246
248
  const currentDirection = listHandler.getCurrentSortDirection();
247
- return Object.assign(Object.assign({}, col), { title: (_jsxs("div", { className: "dl-sortable-header", onClick: () => col.id && col.id !== "_actions" && handleSort(col.id), style: { cursor: col.id !== "_actions" ? "pointer" : "default" }, children: [col.title, currentSort === col.id && _jsx("span", { className: `dl-sort-icon dl-sort-${currentDirection}`, children: currentDirection === "asc" ? "▲" : "▼" })] })) });
249
+ return Object.assign(Object.assign({}, col), { title: (_jsxs("div", { className: "dl-sortable-header", onClick: () => col.id && col.id !== "_actions" && handleSort(col.id), style: { cursor: col.id !== "_actions" ? "pointer" : "default" }, children: [col.title, currentSort === col.id && (_jsx("span", { className: `dl-sort-icon dl-sort-${currentDirection}`, children: currentDirection === "asc" ? "▲" : "▼" }))] })) });
248
250
  });
249
251
  // ============================================================================
250
252
  // RENDER TOOLBAR
@@ -331,13 +333,13 @@ export const DataList = ({ attr }) => {
331
333
  if (commonActions === null || commonActions === void 0 ? void 0 : commonActions.viewPage) {
332
334
  newRowActions.unshift({
333
335
  component: "ViewPage",
334
- attr: Object.assign(Object.assign({}, commonActions.viewPage), { title: "View", icon: _jsx(Eye, { size: 20 }), iconOnly: true }),
336
+ attr: Object.assign(Object.assign({}, commonActions.viewPage), { title: "View", icon: _jsx(Eye, { size: 20, color: "blue" }), iconOnly: true }),
335
337
  });
336
338
  }
337
339
  if (commonActions === null || commonActions === void 0 ? void 0 : commonActions.deleteData) {
338
340
  newRowActions.push({
339
341
  component: "DeleteData",
340
- attr: Object.assign(Object.assign({}, commonActions.deleteData), { title: "Delete", icon: _jsx(Trash, { size: 20 }), iconOnly: true }),
342
+ attr: Object.assign(Object.assign({}, commonActions.deleteData), { title: "Delete", icon: _jsx(Trash, { size: 20, color: "red" }), iconOnly: true }),
341
343
  });
342
344
  }
343
345
  const newToolbarActions = [...(toolbarActions || [])];
@@ -345,7 +347,7 @@ export const DataList = ({ attr }) => {
345
347
  newToolbarActions.unshift({
346
348
  label: "New",
347
349
  component: "ViewPage",
348
- attr: Object.assign(Object.assign({}, commonActions.newPage), { title: "New" }),
350
+ attr: Object.assign(Object.assign({}, commonActions.newPage), { title: "New", action: "create" }),
349
351
  });
350
352
  }
351
353
  // if (commonActions?.filterPage) {
@@ -81,6 +81,6 @@ const Html = (props) => {
81
81
  }
82
82
  };
83
83
  useDependHandler({ name: depends, onRefresh });
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
+ return (_jsx(UIComponent, Object.assign({}, (props !== null && props !== void 0 ? props : {}), { children: _jsx("div", { className: "", dangerouslySetInnerHTML: { __html: renderedHtml } }) })));
85
85
  };
86
86
  export default Html;
@@ -4,6 +4,7 @@ interface PageViewProps extends UIComponentProps {
4
4
  depends?: string;
5
5
  url?: string;
6
6
  fallback?: React.ReactNode;
7
+ options?: Record<string, any>;
7
8
  }
8
9
  declare const PageView: React.MemoExoticComponent<(props: PageViewProps) => import("react/jsx-runtime").JSX.Element>;
9
10
  export default PageView;
@@ -12,7 +12,7 @@ const StableShell = memo((props) => {
12
12
  return (_jsx(UIComponent, Object.assign({}, uiProps, { children: _jsx(PageViewProvider, { handle: handle, paths: paths !== null && paths !== void 0 ? paths : [], children: _jsx(PageSlot, { ref: slotRef }) }) })));
13
13
  });
14
14
  const PageView = memo((props) => {
15
- const { name = "selectedPage", depends, url } = props !== null && props !== void 0 ? props : {};
15
+ const { name = "selectedPage", depends, url, options = {} } = props !== null && props !== void 0 ? props : {};
16
16
  const [forceUpdate, setForceUpdate] = useState({});
17
17
  const providerRef = useRef({});
18
18
  const slotRef = useRef(null);
@@ -81,7 +81,7 @@ const PageView = memo((props) => {
81
81
  }
82
82
  setOwnPaths([page]); // ← track own paths
83
83
  const key = "page-" + Math.random().toString(36).slice(2);
84
- const comp = _createElement(Page, Object.assign({}, propsRef.current, { url: page, key: key }));
84
+ const comp = _createElement(Page, Object.assign({}, propsRef.current, { url: page, key: key, options: options }));
85
85
  (_b = slotRef.current) === null || _b === void 0 ? void 0 : _b.update(comp, page);
86
86
  },
87
87
  isStandalone: () => {
@@ -6,6 +6,7 @@ type PopupViewProps = {
6
6
  onClose?: () => void;
7
7
  eventHandler?: EventHandler | null;
8
8
  title?: string;
9
+ options?: Record<string, any>;
9
10
  };
10
11
  declare const PopupView: (props: PopupViewProps) => import("react/jsx-runtime").JSX.Element;
11
12
  export default PopupView;
@@ -7,7 +7,7 @@ import Page from "../../core/Page";
7
7
  import { PageViewProvider } from "../../core/PageViewContext";
8
8
  import { usePopupManager } from "../../core/PopupContext";
9
9
  const PopupView = (props) => {
10
- const { url, popupClassName, onClose = () => { }, eventHandler, title } = props !== null && props !== void 0 ? props : {};
10
+ const { url, popupClassName, onClose = () => { }, eventHandler, title, options = {} } = props !== null && props !== void 0 ? props : {};
11
11
  const [paths, setPaths] = useState([]);
12
12
  const providerRef = useRef({});
13
13
  const slotRef = useRef(null);
@@ -22,7 +22,7 @@ const PopupView = (props) => {
22
22
  let comp = null;
23
23
  try {
24
24
  const key = "page-" + Math.random().toString(36).slice(2);
25
- comp = _jsx(Page, { url: path }, key);
25
+ comp = _jsx(Page, { url: path, options: options }, key);
26
26
  }
27
27
  catch (error) {
28
28
  const e = getError(error);
@@ -79,9 +79,9 @@ const ChildrenSlot = memo(forwardRef((_, ref) => {
79
79
  export const usePopupView = () => {
80
80
  const popupManager = usePopupManager();
81
81
  const create = (props) => {
82
- const { url, onClose, eventHandler, title } = props !== null && props !== void 0 ? props : {};
82
+ const { url, onClose, eventHandler, title, options: optionProps = {} } = props !== null && props !== void 0 ? props : {};
83
83
  const show = (options) => {
84
- const content = _jsx(PopupView, { url: url, onClose: onClose, eventHandler: eventHandler });
84
+ const content = _jsx(PopupView, { url: url, onClose: onClose, eventHandler: eventHandler, options: optionProps });
85
85
  const popup = popupManager.create(options);
86
86
  popup.setContent(content);
87
87
  popup.show();
@@ -3,6 +3,7 @@ type RootViewProps = {
3
3
  url: string;
4
4
  eventHandler?: EventHandler | null;
5
5
  standalone?: boolean;
6
+ options?: Record<string, any>;
6
7
  };
7
8
  declare const RootView: (props: RootViewProps) => import("react/jsx-runtime").JSX.Element;
8
9
  export default RootView;
@@ -4,7 +4,7 @@ import { forwardRef, memo, useEffect, useImperativeHandle, useLayoutEffect, useR
4
4
  import Page from "../../core/Page";
5
5
  import { PageViewProvider } from "../../core/PageViewContext";
6
6
  const RootView = (props) => {
7
- const { url, eventHandler, standalone = true } = props !== null && props !== void 0 ? props : {};
7
+ const { url, eventHandler, standalone = true, options = {} } = props !== null && props !== void 0 ? props : {};
8
8
  const [paths, setPaths] = useState([]);
9
9
  const mainPath = useRef("");
10
10
  const [newRootPath, setNewRootPath] = useState(null);
@@ -17,7 +17,7 @@ const RootView = (props) => {
17
17
  // console.log("RootView loadPage", path); // main#tasklist
18
18
  const pageUUID = "page-" + Math.random().toString(36).slice(2);
19
19
  const newUrl = url.split("#");
20
- comp = _jsx(Page, { url: path }, pageUUID);
20
+ comp = _jsx(Page, { url: path, options: options }, pageUUID);
21
21
  }
22
22
  catch (error) {
23
23
  const e = getError(error);
@@ -1,5 +1,5 @@
1
1
  type WizardViewProps = {
2
- name: string;
2
+ title?: string;
3
3
  items: Record<string, any>[];
4
4
  };
5
5
  declare const WizardView: (props: WizardViewProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,34 +1,16 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useRef } from "react";
2
+ import { useRef, useState } from "react";
3
3
  import { usePageContext } from "../../core/PageContext";
4
4
  import { StepHandler } from "../../core/StepHandler";
5
- import ComponentView from "./ComponentView";
5
+ import WizardTemplate from "../../templates/WizardTemplate";
6
6
  const WizardView = (props) => {
7
- const { name = "wizard", items = [] } = props !== null && props !== void 0 ? props : {};
8
- const pageContext = usePageContext();
7
+ const { title, items = [] } = props !== null && props !== void 0 ? props : {};
8
+ const [currentItem, setCurrentItem] = useState(items[0]);
9
9
  const handlerRef = useRef(null);
10
+ const pageContext = usePageContext();
10
11
  if (handlerRef.current == null) {
11
- const callback = (item) => {
12
- var _a;
13
- const onInit = (_a = item.events) === null || _a === void 0 ? void 0 : _a.onInit;
14
- if (onInit != null && typeof onInit === "function") {
15
- onInit();
16
- }
17
- pageContext === null || pageContext === void 0 ? void 0 : pageContext.set(name, item);
18
- };
19
- const handler = StepHandler(items, callback);
20
- for (const it of items) {
21
- const item = it;
22
- if (item.attr == null)
23
- item.attr = {};
24
- item.attr.stepHandler = handler;
25
- item.attr.events = {};
26
- }
27
- handlerRef.current = handler;
12
+ handlerRef.current = StepHandler(items, (item) => setCurrentItem(item), () => pageContext.getData());
28
13
  }
29
- useEffect(() => {
30
- pageContext === null || pageContext === void 0 ? void 0 : pageContext.set(name, items[0]);
31
- }, []);
32
- return (_jsx("div", { children: _jsx(ComponentView, { name: name, depends: name }) }));
14
+ return (_jsx(WizardTemplate, { title: (currentItem === null || currentItem === void 0 ? void 0 : currentItem.title) || title, component: currentItem === null || currentItem === void 0 ? void 0 : currentItem.component, componentid: currentItem === null || currentItem === void 0 ? void 0 : currentItem.componentid, attr: currentItem === null || currentItem === void 0 ? void 0 : currentItem.attr, stepHandler: handlerRef.current }));
33
15
  };
34
16
  export default WizardView;
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  export interface DynamicComponentConfig {
3
- component: string;
3
+ component?: string;
4
+ componentid?: string;
4
5
  attr?: Record<string, any>;
5
6
  }
6
7
  interface DynamicComponentProps {
@@ -11,11 +11,33 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
14
+ import React, { useEffect, useState } from "react";
14
15
  import { ResourceLoader } from "../lib/utils/ResourceLoader";
15
- import React from "react";
16
+ import { DynamicTemplate } from "./DynamicTemplate";
17
+ import { usePageContext } from "./PageContext";
16
18
  export const DynamicComponent = ({ config, fallback = _jsx("div", { style: { color: "red" }, children: "Component not found" }), onError, }) => {
17
- const { component, attr = {} } = config !== null && config !== void 0 ? config : {};
19
+ const { component, componentid, attr = {} } = config !== null && config !== void 0 ? config : {};
18
20
  const _a = attr !== null && attr !== void 0 ? attr : {}, { key } = _a, restAttr = __rest(_a, ["key"]);
21
+ const pageContext = usePageContext();
22
+ const [resolvedNode, setResolvedNode] = useState(null);
23
+ useEffect(() => {
24
+ if (!componentid)
25
+ return;
26
+ pageContext.getMgmt("components", componentid).then((res) => {
27
+ if (!res)
28
+ return;
29
+ const { template, attr: resAttr } = res;
30
+ const mergedAttr = Object.assign(Object.assign({}, resAttr), restAttr);
31
+ setResolvedNode(_jsx(DynamicTemplate, { template: template, attr: mergedAttr }));
32
+ });
33
+ }, [componentid]);
34
+ // componentid path — wait for async resolution
35
+ if (componentid) {
36
+ return resolvedNode ? _jsx(_Fragment, { children: resolvedNode }) : null;
37
+ }
38
+ // component path — sync resolution via ResourceLoader
39
+ if (!component)
40
+ return _jsx(_Fragment, { children: fallback });
19
41
  let Comp;
20
42
  try {
21
43
  Comp = ResourceLoader.getComponent(component);
@@ -1,6 +1,7 @@
1
1
  type PageProps = {
2
2
  url?: string | null;
3
3
  fallback?: React.ReactNode;
4
+ options?: Record<string, any>;
4
5
  };
5
6
  declare const Page: (props: PageProps) => import("react/jsx-runtime").JSX.Element;
6
7
  export default Page;
package/dist/core/Page.js CHANGED
@@ -11,7 +11,7 @@ import { PageProvider } from "./PageContext";
11
11
  import { usePageViewContext } from "./PageViewContext";
12
12
  import { usePopupContext } from "./PopupContext";
13
13
  const Page = (props) => {
14
- const { url: finalUrl, fallback = _jsx("div", { children: "Loading page..." }) } = props !== null && props !== void 0 ? props : {};
14
+ const { url: finalUrl, fallback = _jsx("div", { children: "Loading page..." }), options = {} } = props !== null && props !== void 0 ? props : {};
15
15
  const [uuid] = useState(() => Math.random().toString(36).slice(2));
16
16
  const [value, setValue] = useState(null);
17
17
  const [cache, setCache] = useState({});
@@ -122,6 +122,9 @@ const Page = (props) => {
122
122
  getSelectedPath: () => {
123
123
  return originalData.selectedPath;
124
124
  },
125
+ getOptions: () => {
126
+ return options;
127
+ },
125
128
  };
126
129
  return (_jsx(PageProvider, { handle: pageHandle, children: _jsx(ChildrenSlot, { ref: slotRef }) }));
127
130
  };
@@ -34,6 +34,7 @@ export type PageContextValue = {
34
34
  export type PageHandle = {
35
35
  init: (ref: PageContextValue) => void;
36
36
  getSelectedPath: () => string | null;
37
+ getOptions: () => Record<string, any>;
37
38
  };
38
39
  export type PageProviderRef = {
39
40
  set: (name: string, value: any) => void;
@@ -37,7 +37,8 @@ export const PageProvider = ({ handle, children }) => {
37
37
  // console.log("PageContext paths", uuid, paths);
38
38
  const parentContext = useContext(PageContext);
39
39
  const pageView = usePageViewContext();
40
- const binding = useBinding();
40
+ const { action = "read" } = (handle === null || handle === void 0 ? void 0 : handle.getOptions()) || {};
41
+ const binding = useBinding({}, action);
41
42
  useLayoutEffect(() => {
42
43
  var _a;
43
44
  if (handle == null)
@@ -124,7 +125,8 @@ export const PageProvider = ({ handle, children }) => {
124
125
  };
125
126
  const setData = (value, mode) => {
126
127
  if (binding && typeof binding.setData === "function") {
127
- binding.setData(value, mode !== null && mode !== void 0 ? mode : "read");
128
+ const oldMode = binding.getMode();
129
+ binding.setData(value, mode !== null && mode !== void 0 ? mode : oldMode);
128
130
  }
129
131
  else {
130
132
  console.warn("binding.setData is not available, skipping data set");
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
3
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
4
4
  /* ------------------ Global Stack ------------------ */
5
5
  const contextStack = [];
6
6
  /* ------------------ Context ------------------ */
@@ -161,7 +161,7 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
161
161
  var _a;
162
162
  const [, selectedPath] = paths;
163
163
  const standalone = isStandalone();
164
- const chainInfo = { path: "", base: "", page: "", options: {} };
164
+ const chainInfo = { path: "", base: "", page: "", options: {}, params: {} };
165
165
  const result = { current: chainInfo, previous: null };
166
166
  if (standalone) {
167
167
  chainInfo.path = selectedPath !== null && selectedPath !== void 0 ? selectedPath : "";
@@ -182,19 +182,19 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
182
182
  useLayoutEffect(() => {
183
183
  handleRef.current = handle;
184
184
  }, [handle]);
185
- useEffect(() => {
186
- const standalone = isStandalone();
187
- // console.log("pageChain changed", uuid, standalone, pageChain);
188
- }, [pageChain]);
185
+ // useEffect(() => {
186
+ // const standalone = isStandalone();
187
+ // console.log("pageChain changed", uuid, standalone, pageChain);
188
+ // }, [pageChain]);
189
189
  const setPage = (page, options = {}) => {
190
- var _a;
190
+ var _a, _b;
191
191
  if (page == null || page.trim() === "") {
192
192
  return;
193
193
  }
194
194
  const standalone = isStandalone();
195
195
  const pageOptions = options !== null && options !== void 0 ? options : {};
196
196
  const { mode = "self" } = pageOptions;
197
- const { current: currentChain, previous: previousChain } = pageChain;
197
+ const { previous: previousChain } = pageChain;
198
198
  const newChainInfo = { path: "", base: "", page: "", options: pageOptions, params: {} };
199
199
  const newPageChain = { current: newChainInfo, previous: null };
200
200
  if (mode != null && mode === "window") {
@@ -206,7 +206,16 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
206
206
  newChainInfo.page = page;
207
207
  newChainInfo.base = locInfo.root;
208
208
  newChainInfo.path = locInfo.root + "/" + page;
209
- newPageChain.previous = { current: currentChain, previous: previousChain };
209
+ const currentFullPath = window.location.pathname + window.location.search + window.location.hash;
210
+ const currentPageName = (_a = window.location.pathname.split("/").at(-1)) !== null && _a !== void 0 ? _a : "";
211
+ const previousChainInfo = {
212
+ path: currentFullPath,
213
+ base: locInfo.root,
214
+ page: currentPageName,
215
+ options: {},
216
+ params: {},
217
+ };
218
+ newPageChain.previous = { current: previousChainInfo, previous: previousChain };
210
219
  }
211
220
  else {
212
221
  const currentUrl = getUrl();
@@ -215,11 +224,17 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
215
224
  newChainInfo.path = [currentUrl, page].filter(Boolean).join("#");
216
225
  }
217
226
  setPageChain((prev) => (Object.assign({}, newPageChain)));
218
- if (!standalone) {
219
- // console.log("setPage push state => ", { newChainInfo });
227
+ if (mode === "window") {
220
228
  history.pushState(null, "", newChainInfo.path);
221
229
  }
222
- (_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.renderPage(page);
230
+ else if (!standalone) {
231
+ const currentPathname = window.location.pathname;
232
+ const expectedPathname = newChainInfo.path.split("#")[0].split("?")[0];
233
+ if (currentPathname === expectedPathname) {
234
+ history.pushState(null, "", newChainInfo.path);
235
+ }
236
+ }
237
+ (_b = handleRef.current) === null || _b === void 0 ? void 0 : _b.renderPage(page);
223
238
  };
224
239
  const pushPage = (page) => {
225
240
  var _a;
@@ -319,9 +334,9 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
319
334
  return loc.root + "/" + paths[0];
320
335
  }
321
336
  else {
322
- const parentPath = (_b = (_a = parentContext === null || parentContext === void 0 ? void 0 : parentContext.paths) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "";
337
+ const parentUrl = (_b = (_a = parentContext === null || parentContext === void 0 ? void 0 : parentContext.getUrl) === null || _a === void 0 ? void 0 : _a.call(parentContext)) !== null && _b !== void 0 ? _b : "";
323
338
  const [currentPath] = paths;
324
- return [parentPath, currentPath].filter(Boolean).join("#");
339
+ return [parentUrl, currentPath].filter(Boolean).join("#");
325
340
  }
326
341
  };
327
342
  const getSubPaths = () => {
@@ -46,10 +46,10 @@ const renderContent = (content, orientation = "vertical") => {
46
46
  return (_jsx("div", { style: { width: "100%" }, children: renderContent(item, "vertical") }, i));
47
47
  }) }));
48
48
  }
49
- // ── 4. Object with `component` → DynamicComponent ──────────────────────
50
- if (content && typeof content === "object" && content.component) {
51
- const { component, attr } = content;
52
- return _jsx(DynamicComponent, { config: { component, attr } });
49
+ // ── 4. Object with `component` or `componentid` → DynamicComponent ──────────────────────
50
+ if (content && typeof content === "object" && (content.component || content.componentid)) {
51
+ const { component, componentid, attr } = content;
52
+ return _jsx(DynamicComponent, { config: { component, componentid, attr } });
53
53
  }
54
54
  // ── 5. Object without `component` → nested Panel ───────────────────────
55
55
  if (content && typeof content === "object") {
@@ -6,4 +6,4 @@ export type StepActionHandler = {
6
6
  next: () => void;
7
7
  back: () => void;
8
8
  };
9
- export declare const StepHandler: (items: Record<string, any>[], callback: (item: Record<string, any>) => void) => StepActionHandler;
9
+ export declare const StepHandler: (items: Record<string, any>[], callback: (item: Record<string, any>) => void, getData?: () => Record<string, any>) => StepActionHandler;
@@ -1,32 +1,69 @@
1
- export const StepHandler = (items, callback) => {
1
+ const evaluateWhen = (expr, data) => {
2
+ try {
3
+ const filled = expr.replace(/\{\{([\s\S]+?)\}\}/g, (_, path) => {
4
+ const val = path.trim().split(".").reduce((obj, key) => obj === null || obj === void 0 ? void 0 : obj[key], data);
5
+ return JSON.stringify(val !== null && val !== void 0 ? val : null);
6
+ });
7
+ // eslint-disable-next-line no-new-func
8
+ return Boolean(new Function(`return (${filled})`)());
9
+ }
10
+ catch (_a) {
11
+ return false;
12
+ }
13
+ };
14
+ const resolveNext = (current, items, currentIndex, getData) => {
15
+ const { next } = current;
16
+ // no next defined — linear
17
+ if (next == null)
18
+ return currentIndex + 1;
19
+ // next is a string id — jump directly
20
+ if (typeof next === "string") {
21
+ const idx = items.findIndex((it) => it.id === next);
22
+ return idx >= 0 ? idx : currentIndex + 1;
23
+ }
24
+ // next is a conditions array
25
+ if (Array.isArray(next)) {
26
+ const data = getData();
27
+ for (const condition of next) {
28
+ const c = condition;
29
+ if (c.when != null && evaluateWhen(c.when, data)) {
30
+ const idx = items.findIndex((it) => it.id === c.goto);
31
+ return idx >= 0 ? idx : currentIndex + 1;
32
+ }
33
+ if (c.default != null) {
34
+ const idx = items.findIndex((it) => it.id === c.default);
35
+ return idx >= 0 ? idx : currentIndex + 1;
36
+ }
37
+ }
38
+ }
39
+ return currentIndex + 1;
40
+ };
41
+ export const StepHandler = (items, callback, getData = () => ({})) => {
2
42
  let currentIndex = 0;
3
- let lastIndex = 0; // max index visited
4
- const hasPrev = () => {
5
- if (items.length == 0)
6
- return false;
7
- if (currentIndex - 1 < 0)
8
- return false;
9
- return true;
10
- };
43
+ let lastIndex = 0;
44
+ const history = []; // track visited indices for back navigation
45
+ const hasPrev = () => history.length > 0;
11
46
  const hasNext = () => {
12
- if (items.length == 0)
47
+ if (items.length === 0)
13
48
  return false;
14
- if (currentIndex + 1 >= items.length)
15
- return false;
16
- return true;
49
+ const nextIndex = resolveNext(items[currentIndex], items, currentIndex, getData);
50
+ return nextIndex < items.length;
17
51
  };
18
52
  const next = () => {
19
- currentIndex++;
20
- if (currentIndex > lastIndex) {
53
+ const nextIndex = resolveNext(items[currentIndex], items, currentIndex, getData);
54
+ if (nextIndex >= items.length)
55
+ return;
56
+ history.push(currentIndex);
57
+ currentIndex = nextIndex;
58
+ if (currentIndex > lastIndex)
21
59
  lastIndex = currentIndex;
22
- }
23
- const selected = items[currentIndex];
24
- callback(selected);
60
+ callback(items[currentIndex]);
25
61
  };
26
62
  const back = () => {
27
- currentIndex--;
28
- const selected = items[currentIndex];
29
- callback(selected);
63
+ if (history.length === 0)
64
+ return;
65
+ currentIndex = history.pop();
66
+ callback(items[currentIndex]);
30
67
  };
31
68
  return {
32
69
  getCurrentIndex: () => currentIndex,
package/dist/index.css CHANGED
@@ -700,9 +700,15 @@ video {
700
700
  .mb-2 {
701
701
  margin-bottom: 0.5rem;
702
702
  }
703
+ .mb-3 {
704
+ margin-bottom: 0.75rem;
705
+ }
703
706
  .mb-4 {
704
707
  margin-bottom: 1rem;
705
708
  }
709
+ .mb-6 {
710
+ margin-bottom: 1.5rem;
711
+ }
706
712
  .ml-2 {
707
713
  margin-left: 0.5rem;
708
714
  }
@@ -922,12 +928,18 @@ video {
922
928
  .max-w-\[90vw\] {
923
929
  max-width: 90vw;
924
930
  }
931
+ .max-w-sm {
932
+ max-width: 24rem;
933
+ }
925
934
  .flex-1 {
926
935
  flex: 1 1 0%;
927
936
  }
928
937
  .flex-shrink-0 {
929
938
  flex-shrink: 0;
930
939
  }
940
+ .shrink-0 {
941
+ flex-shrink: 0;
942
+ }
931
943
  .-translate-y-1\/2 {
932
944
  --tw-translate-y: -50%;
933
945
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -1248,6 +1260,9 @@ video {
1248
1260
  .p-4 {
1249
1261
  padding: 1rem;
1250
1262
  }
1263
+ .p-6 {
1264
+ padding: 1.5rem;
1265
+ }
1251
1266
  .\!px-3 {
1252
1267
  padding-left: 0.75rem !important;
1253
1268
  padding-right: 0.75rem !important;
@@ -1631,6 +1646,10 @@ video {
1631
1646
  --tw-bg-opacity: 1;
1632
1647
  background-color: rgb(21 128 61 / var(--tw-bg-opacity, 1));
1633
1648
  }
1649
+ .hover\:bg-red-600:hover {
1650
+ --tw-bg-opacity: 1;
1651
+ background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1));
1652
+ }
1634
1653
  .hover\:bg-red-700:hover {
1635
1654
  --tw-bg-opacity: 1;
1636
1655
  background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1));
@@ -1,6 +1,9 @@
1
+ import React from "react";
1
2
  import { StepActionHandler } from "../core/StepHandler";
2
3
  type WizardTemplateProps = {
4
+ title?: string;
3
5
  component?: string;
6
+ componentid?: string;
4
7
  attr?: Record<string, any>;
5
8
  stepHandler: StepActionHandler;
6
9
  children?: React.ReactNode;
@@ -1,22 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button } from "@ramesesinc/client";
3
- import { useEffect, useState } from "react";
3
+ import React, { useEffect, useState } from "react";
4
4
  import { useApp } from "../core/AppContext";
5
5
  import BorderLayout from "../layouts/BorderLayout";
6
+ import { ResourceLoader } from "../lib/utils/ResourceLoader";
6
7
  const WizardTemplate = (props) => {
7
- console.log(props);
8
- const { component, attr = {}, stepHandler, children } = props !== null && props !== void 0 ? props : {};
8
+ const { title, component, componentid, stepHandler, children } = props !== null && props !== void 0 ? props : {};
9
9
  const [view, setView] = useState(null);
10
10
  const { getComponentCache } = useApp();
11
11
  const loadComponent = async () => {
12
- if (component == null || component.trim() === "") {
13
- setView(null);
12
+ var _a;
13
+ if (componentid === null || componentid === void 0 ? void 0 : componentid.trim()) {
14
+ const comp = await getComponentCache(componentid);
15
+ setView(comp.template);
14
16
  return;
15
17
  }
16
- const comp = await getComponentCache(component);
17
- const Tmpl = comp.template;
18
- const el = _jsx(Tmpl, Object.assign({}, (attr !== null && attr !== void 0 ? attr : {})));
19
- setView(el);
18
+ if (component === null || component === void 0 ? void 0 : component.trim()) {
19
+ const Comp = ResourceLoader.getComponent(component);
20
+ setView(_jsx(React.Suspense, { fallback: _jsx("div", { children: "Loading..." }), children: _jsx(Comp, Object.assign({}, ((_a = props.attr) !== null && _a !== void 0 ? _a : {}))) }));
21
+ return;
22
+ }
23
+ setView(null);
20
24
  };
21
25
  const render = () => {
22
26
  if (view != null) {
@@ -25,8 +29,9 @@ const WizardTemplate = (props) => {
25
29
  return children;
26
30
  };
27
31
  useEffect(() => {
32
+ setView(null);
28
33
  loadComponent();
29
- }, []);
34
+ }, [component, componentid]);
30
35
  const handleNext = () => {
31
36
  stepHandler === null || stepHandler === void 0 ? void 0 : stepHandler.next();
32
37
  };
@@ -34,6 +39,6 @@ const WizardTemplate = (props) => {
34
39
  stepHandler === null || stepHandler === void 0 ? void 0 : stepHandler.back();
35
40
  };
36
41
  const south = (_jsxs("div", { className: "flex justify-between gap-2 p-4 font-proxima-600", children: [_jsx("div", { className: "flex gap-2", children: (stepHandler === null || stepHandler === void 0 ? void 0 : stepHandler.hasPrev()) && (_jsx(Button, { onClick: handleBack, className: "bg-transparent text-gray-500 border-none shadow-none hover:bg-gray-100", children: "Back" })) }), _jsx("div", { className: "flex gap-2", children: (stepHandler === null || stepHandler === void 0 ? void 0 : stepHandler.hasNext()) && (_jsx(Button, { onClick: handleNext, className: "bg-primary-500 text-white hover:bg-primary-600", children: "Next" })) })] }));
37
- return _jsx(BorderLayout, { south: south, children: render() });
42
+ return (_jsx(BorderLayout, { north: _jsx("div", { className: "mb-2 font-semibold text-lg", children: title }), south: south, children: render() }));
38
43
  };
39
44
  export default WizardTemplate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramesesinc/platform-core",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Platform Core Library",
5
5
  "author": "Rameses Systems Inc.",
6
6
  "license": "MIT",