@ramesesinc/platform-core 0.1.9 → 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.
- package/dist/components/action/DeleteData.d.ts +1 -0
- package/dist/components/action/DeleteData.js +16 -5
- package/dist/components/action/LookupPage.d.ts +2 -1
- package/dist/components/action/LookupPage.js +4 -3
- package/dist/components/action/Play.d.ts +6 -0
- package/dist/components/action/Play.js +40 -0
- package/dist/components/action/ProgressBar.d.ts +8 -0
- package/dist/components/action/ProgressBar.js +146 -0
- package/dist/components/action/ViewPage.d.ts +3 -1
- package/dist/components/action/ViewPage.js +22 -10
- package/dist/components/common/UIMenu.d.ts +1 -0
- package/dist/components/common/UIMenu.js +5 -3
- package/dist/components/index.d.ts +4 -1
- package/dist/components/index.js +4 -1
- package/dist/components/input/Combo.d.ts +21 -0
- package/dist/components/input/Combo.js +137 -0
- package/dist/components/input/DateField.js +7 -14
- package/dist/components/input/Text.d.ts +5 -0
- package/dist/components/input/Text.js +42 -7
- package/dist/components/input/YearPicker.js +3 -2
- package/dist/components/list/EditableMenu.d.ts +2 -0
- package/dist/components/list/EditableMenu.js +128 -0
- package/dist/components/list/TabMenu.js +2 -2
- package/dist/components/list/TreeMenu.js +35 -13
- package/dist/components/table/DataList.d.ts +1 -1
- package/dist/components/table/DataList.js +56 -29
- package/dist/components/table/DataTable.d.ts +2 -0
- package/dist/components/table/DataTable.js +31 -22
- package/dist/components/view/FilterView.js +1 -1
- package/dist/components/view/HtmlForm.js +12 -9
- package/dist/components/view/HtmlView.js +1 -1
- package/dist/components/view/PageView.d.ts +1 -0
- package/dist/components/view/PageView.js +38 -11
- package/dist/components/view/PopupView.d.ts +1 -0
- package/dist/components/view/PopupView.js +4 -4
- package/dist/components/view/RootView.d.ts +1 -0
- package/dist/components/view/RootView.js +18 -18
- package/dist/components/view/WizardView.d.ts +1 -1
- package/dist/components/view/WizardView.js +7 -25
- package/dist/core/AuthContext.js +1 -1
- package/dist/core/DynamicComponent.d.ts +2 -1
- package/dist/core/DynamicComponent.js +24 -2
- package/dist/core/Page.d.ts +1 -0
- package/dist/core/Page.js +6 -5
- package/dist/core/PageCache.d.ts +0 -2
- package/dist/core/PageCache.js +3 -8
- package/dist/core/PageContext.d.ts +1 -0
- package/dist/core/PageContext.js +16 -2
- package/dist/core/PageViewContext.d.ts +8 -2
- package/dist/core/PageViewContext.js +155 -86
- package/dist/core/Panel.js +34 -12
- package/dist/core/StepHandler.d.ts +1 -1
- package/dist/core/StepHandler.js +58 -21
- package/dist/index.css +98 -0
- package/dist/layouts/CardLayout.d.ts +2 -2
- package/dist/layouts/CardLayout.js +3 -4
- package/dist/layouts/HPanel.d.ts +2 -2
- package/dist/layouts/HPanel.js +1 -2
- package/dist/layouts/VPanel.d.ts +2 -2
- package/dist/layouts/VPanel.js +1 -2
- package/dist/layouts/index.d.ts +2 -3
- package/dist/layouts/index.js +2 -3
- package/dist/lib/utils/ExprUtil.js +18 -29
- package/dist/lib/utils/ResourceLoader.js +19 -7
- package/dist/lib/utils/SectionProvider.js +1 -1
- package/dist/lib/utils/initResourceLoader.d.ts +2 -0
- package/dist/lib/utils/initResourceLoader.js +64 -95
- package/dist/lib/utils/nunjucks.d.ts +2 -0
- package/dist/lib/utils/nunjucks.js +8 -0
- package/dist/templates/CrudFormTemplate.js +2 -3
- package/dist/templates/DataListTemplate.js +1 -1
- package/dist/templates/WizardTemplate.d.ts +3 -0
- package/dist/templates/WizardTemplate.js +17 -10
- package/package.json +1 -1
- package/dist/components/input/Select.d.ts +0 -14
- package/dist/components/input/Select.js +0 -40
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown } from "lucide-react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { usePageContext } from "../../core/PageContext";
|
|
5
|
+
import UIComponent from "../common/UIComponent";
|
|
6
|
+
import useUIInput from "../common/UIInput";
|
|
7
|
+
const RADIUS_STYLES = {
|
|
8
|
+
none: "rounded-none",
|
|
9
|
+
md: "rounded-md",
|
|
10
|
+
lg: "rounded-lg",
|
|
11
|
+
xl: "rounded-xl",
|
|
12
|
+
"2xl": "rounded-2xl",
|
|
13
|
+
"3xl": "rounded-3xl",
|
|
14
|
+
full: "rounded-full",
|
|
15
|
+
};
|
|
16
|
+
const variantStyles = {
|
|
17
|
+
primary: "border bg-blue-500 text-blue-50",
|
|
18
|
+
secondary: "border bg-white text-gray-700",
|
|
19
|
+
danger: "border bg-red-600 text-white",
|
|
20
|
+
text: "border bg-transparent text-blue-600",
|
|
21
|
+
contained: "border bg-blue-500 text-blue-50",
|
|
22
|
+
outlined: "border border-1 bg-transparent text-black",
|
|
23
|
+
};
|
|
24
|
+
const Combo = (props) => {
|
|
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 : {};
|
|
27
|
+
const { params } = data !== null && data !== void 0 ? data : {};
|
|
28
|
+
const { projection } = params !== null && params !== void 0 ? params : {};
|
|
29
|
+
const selectRef = useRef(null);
|
|
30
|
+
const pageContext = usePageContext();
|
|
31
|
+
const [listItems, setListItems] = useState(items);
|
|
32
|
+
const [rawItems, setRawItems] = useState([]);
|
|
33
|
+
const [selectValue, setSelectValue] = useState("");
|
|
34
|
+
// derive label/value keys from projection
|
|
35
|
+
const projectedKeys = Object.keys(projection !== null && projection !== void 0 ? projection : {});
|
|
36
|
+
const labelKey = (_b = (_a = data.labelKey) !== null && _a !== void 0 ? _a : projectedKeys[0]) !== null && _b !== void 0 ? _b : "label";
|
|
37
|
+
const valueKey = (_d = (_c = data.valueKey) !== null && _c !== void 0 ? _c : projectedKeys[0]) !== null && _d !== void 0 ? _d : "value";
|
|
38
|
+
const isSpecificField = (_e = props.name) === null || _e === void 0 ? void 0 : _e.includes(".");
|
|
39
|
+
const buildValue = (rawItem) => {
|
|
40
|
+
if (isSpecificField) {
|
|
41
|
+
return rawItem[valueKey];
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return Object.keys(projection !== null && projection !== void 0 ? projection : {}).reduce((acc, key) => {
|
|
45
|
+
acc[key] = rawItem[key];
|
|
46
|
+
return acc;
|
|
47
|
+
}, {});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const onRefresh = () => {
|
|
51
|
+
var _a;
|
|
52
|
+
const val = getValue();
|
|
53
|
+
if (val != null && typeof val === "object") {
|
|
54
|
+
setSelectValue(String((_a = val[valueKey]) !== null && _a !== void 0 ? _a : ""));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
setSelectValue(val !== null && val !== void 0 ? val : "");
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const { getValue, setValue } = useUIInput(Object.assign(Object.assign({}, props), { onRefresh }));
|
|
61
|
+
const fetchItems = async () => {
|
|
62
|
+
var _a, _b, _c, _d;
|
|
63
|
+
if (data === null || data === void 0 ? void 0 : data.api) {
|
|
64
|
+
const res = await (pageContext === null || pageContext === void 0 ? void 0 : pageContext.execService(data.api, (_a = data.params) !== null && _a !== void 0 ? _a : {}));
|
|
65
|
+
const raw = (_c = (_b = res === null || res === void 0 ? void 0 : res.data) !== null && _b !== void 0 ? _b : res) !== null && _c !== void 0 ? _c : [];
|
|
66
|
+
setRawItems(raw);
|
|
67
|
+
const mapped = raw
|
|
68
|
+
.map((item) => {
|
|
69
|
+
var _a, _b, _c;
|
|
70
|
+
return ({
|
|
71
|
+
label: String((_a = item[labelKey]) !== null && _a !== void 0 ? _a : ""),
|
|
72
|
+
value: String((_c = (_b = item[valueKey]) !== null && _b !== void 0 ? _b : item["_id"]) !== null && _c !== void 0 ? _c : ""),
|
|
73
|
+
});
|
|
74
|
+
})
|
|
75
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
76
|
+
setListItems(mapped);
|
|
77
|
+
// auto-select first item
|
|
78
|
+
if (mapped.length > 0) {
|
|
79
|
+
const currentVal = getValue();
|
|
80
|
+
if (currentVal != null && currentVal !== "" && currentVal !== undefined) {
|
|
81
|
+
// value already set, just sync display
|
|
82
|
+
if (typeof currentVal === "object") {
|
|
83
|
+
setSelectValue(String((_d = currentVal[valueKey]) !== null && _d !== void 0 ? _d : ""));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
setSelectValue(String(currentVal));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// no value set, auto-select first
|
|
91
|
+
const firstMapped = mapped[0];
|
|
92
|
+
const firstItem = raw.find((item) => { var _a, _b; return String((_b = (_a = item[valueKey]) !== null && _a !== void 0 ? _a : item["_id"]) !== null && _b !== void 0 ? _b : "") === firstMapped.value; });
|
|
93
|
+
if (firstItem) {
|
|
94
|
+
setSelectValue(firstMapped.value);
|
|
95
|
+
setValue(buildValue(firstItem));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
setListItems([...items].sort((a, b) => a.label.localeCompare(b.label)));
|
|
102
|
+
};
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
fetchItems();
|
|
105
|
+
}, []);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (!(data === null || data === void 0 ? void 0 : data.api)) {
|
|
108
|
+
setListItems([...items].sort((a, b) => a.label.localeCompare(b.label)));
|
|
109
|
+
}
|
|
110
|
+
}, [items]);
|
|
111
|
+
const handleBlur = () => {
|
|
112
|
+
if (!immediate) {
|
|
113
|
+
setValue(selectValue);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const onChange = (e) => {
|
|
117
|
+
var _a, _b;
|
|
118
|
+
const text = (_a = e.target.value) !== null && _a !== void 0 ? _a : "";
|
|
119
|
+
setSelectValue(text);
|
|
120
|
+
if (immediate) {
|
|
121
|
+
const rawItem = rawItems.find((item) => { var _a, _b; return String((_b = (_a = item[valueKey]) !== null && _a !== void 0 ? _a : item["_id"]) !== null && _b !== void 0 ? _b : "") === text; });
|
|
122
|
+
if (rawItem) {
|
|
123
|
+
setValue(buildValue(rawItem));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
setValue(text);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
(_b = props.onChange) === null || _b === void 0 ? void 0 : _b.call(props, text);
|
|
130
|
+
};
|
|
131
|
+
const baseStyles = "w-full appearance-none cursor-pointer pl-4 pr-10 py-[4px] text-md font-medium transition-all duration-150 ease-in-out bg-transparent";
|
|
132
|
+
const focusStyles = "focus:outline-none focus:ring-0 focus:shadow-none";
|
|
133
|
+
const roundingStyle = (_f = RADIUS_STYLES[radius]) !== null && _f !== void 0 ? _f : RADIUS_STYLES["md"];
|
|
134
|
+
const finalClassName = [baseStyles, focusStyles, roundingStyle].filter(Boolean).join(" ");
|
|
135
|
+
return (_jsx(UIComponent, Object.assign({}, (props !== null && props !== void 0 ? props : {}), { children: _jsxs("div", { className: `relative inline-flex items-center overflow-hidden ${roundingStyle} ${variantStyles[variant]} ${className}`, children: [_jsxs("select", { ref: selectRef, onChange: onChange, value: selectValue, onBlur: handleBlur, className: finalClassName, children: [_jsx("option", { value: "", children: placeholder !== null && placeholder !== void 0 ? placeholder : "Select..." }), listItems.map((item) => (_jsx("option", { value: item.value, children: item.label }, item.value)))] }), _jsx("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none flex items-center", children: _jsx(ChevronDown, { size: 16 }) })] }) })));
|
|
136
|
+
};
|
|
137
|
+
export default Combo;
|
|
@@ -29,7 +29,7 @@ const DateField = (props) => {
|
|
|
29
29
|
const getCurrentDate = () => {
|
|
30
30
|
if (inputValue && inputValue.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
31
31
|
// Parse ISO date string as local date to avoid timezone issues
|
|
32
|
-
const parts = inputValue.split(
|
|
32
|
+
const parts = inputValue.split("-");
|
|
33
33
|
const year = parseInt(parts[0], 10);
|
|
34
34
|
const month = parseInt(parts[1], 10) - 1; // Month is 0-indexed
|
|
35
35
|
const day = parseInt(parts[2], 10);
|
|
@@ -46,7 +46,7 @@ const DateField = (props) => {
|
|
|
46
46
|
setInputValue(text);
|
|
47
47
|
// Update calendar date when user types a valid date
|
|
48
48
|
if (text && text.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
49
|
-
const parts = text.split(
|
|
49
|
+
const parts = text.split("-");
|
|
50
50
|
const year = parseInt(parts[0], 10);
|
|
51
51
|
const month = parseInt(parts[1], 10) - 1;
|
|
52
52
|
const day = parseInt(parts[2], 10);
|
|
@@ -103,27 +103,20 @@ const DateField = (props) => {
|
|
|
103
103
|
if (!inputValue || !inputValue.match(/^\d{4}-\d{2}-\d{2}$/))
|
|
104
104
|
return false;
|
|
105
105
|
// Parse ISO date string as local date
|
|
106
|
-
const parts = inputValue.split(
|
|
106
|
+
const parts = inputValue.split("-");
|
|
107
107
|
const selectedYear = parseInt(parts[0], 10);
|
|
108
108
|
const selectedMonth = parseInt(parts[1], 10) - 1;
|
|
109
109
|
const selectedDay = parseInt(parts[2], 10);
|
|
110
|
-
return
|
|
111
|
-
date.getMonth() === selectedMonth &&
|
|
112
|
-
date.getFullYear() === selectedYear);
|
|
110
|
+
return date.getDate() === selectedDay && date.getMonth() === selectedMonth && date.getFullYear() === selectedYear;
|
|
113
111
|
};
|
|
114
112
|
const isToday = (date) => {
|
|
115
113
|
const today = new globalThis.Date();
|
|
116
|
-
return
|
|
117
|
-
date.getMonth() === today.getMonth() &&
|
|
118
|
-
date.getFullYear() === today.getFullYear());
|
|
114
|
+
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
119
115
|
};
|
|
120
|
-
const monthNames = [
|
|
121
|
-
"January", "February", "March", "April", "May", "June",
|
|
122
|
-
"July", "August", "September", "October", "November", "December"
|
|
123
|
-
];
|
|
116
|
+
const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
124
117
|
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
125
118
|
const calendarDays = generateCalendarDays();
|
|
126
|
-
return (_jsx(UIComponent, Object.assign({}, (props !== null && props !== void 0 ? props : {}), { children: _jsxs("div", { className: "relative", children: [
|
|
119
|
+
return (_jsx(UIComponent, Object.assign({}, (props !== null && props !== void 0 ? props : {}), { children: _jsxs("div", { className: "relative", children: [_jsx("div", { className: "flex gap-1", children: _jsx("input", { type: "date", ref: inputRef, onChange: onChange, value: inputValue, onFocus: handleFocus, onBlur: handleBlur, className: className, min: min, max: max }) }), showCalendar && (_jsxs("div", { ref: calendarRef, className: "absolute z-10 mt-1 bg-white border rounded shadow-lg p-4", style: { minWidth: "280px" }, children: [_jsxs("div", { className: "flex justify-between items-center mb-4", children: [_jsx("button", { type: "button", onClick: previousMonth, className: "px-2 py-1 hover:bg-gray-100 rounded", children: "\u25C0" }), _jsxs("div", { className: "font-semibold", children: [monthNames[calendarDate.getMonth()], " ", calendarDate.getFullYear()] }), _jsx("button", { type: "button", onClick: nextMonth, className: "px-2 py-1 hover:bg-gray-100 rounded", children: "\u25B6" })] }), _jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: dayNames.map((day) => (_jsx("div", { className: "text-center text-xs font-medium text-gray-600 py-1", children: day }, day))) }), _jsx("div", { className: "grid grid-cols-7 gap-1", children: calendarDays.map((date, index) => {
|
|
127
120
|
if (!date) {
|
|
128
121
|
return _jsx("div", { className: "aspect-square" }, index);
|
|
129
122
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { UIInputProps } from "../common/UIInput";
|
|
2
|
+
export type TextCase = "upper" | "lower" | "capitalize" | "none";
|
|
3
|
+
export declare const applyTextCase: (value: string, mode: TextCase) => string;
|
|
2
4
|
type TextFieldProps = UIInputProps & {
|
|
3
5
|
required?: boolean;
|
|
4
6
|
immediate?: boolean;
|
|
7
|
+
align?: "left" | "center" | "right";
|
|
8
|
+
noSpace?: boolean;
|
|
9
|
+
placeholder?: string;
|
|
5
10
|
};
|
|
6
11
|
declare const TextField: (props: TextFieldProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
12
|
export default TextField;
|
|
@@ -2,20 +2,38 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useRef, useState } from "react";
|
|
3
3
|
import UIComponent from "../common/UIComponent";
|
|
4
4
|
import useUIInput from "../common/UIInput";
|
|
5
|
+
class TextCaseFormatter {
|
|
6
|
+
apply(value, mode) {
|
|
7
|
+
switch (mode) {
|
|
8
|
+
case "upper":
|
|
9
|
+
return value.toUpperCase();
|
|
10
|
+
case "lower":
|
|
11
|
+
return value.toLowerCase();
|
|
12
|
+
case "capitalize":
|
|
13
|
+
return value.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
14
|
+
case "none":
|
|
15
|
+
default:
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const applyTextCase = (value, mode) => {
|
|
21
|
+
return new TextCaseFormatter().apply(value, mode);
|
|
22
|
+
};
|
|
5
23
|
const TextField = (props) => {
|
|
6
|
-
|
|
7
|
-
const
|
|
24
|
+
var _a, _b;
|
|
25
|
+
const { immediate = true, align = "left", noSpace = false, required = false, placeholder } = props !== null && props !== void 0 ? props : {};
|
|
26
|
+
// const [focused, setFocused] = useState(false);
|
|
8
27
|
const inputRef = useRef(null);
|
|
9
28
|
const valueRef = useRef("");
|
|
10
|
-
const className = "border rounded px-2 py-1 w-full";
|
|
11
29
|
const handleFocus = () => {
|
|
12
|
-
setFocused(true);
|
|
30
|
+
// setFocused(true);
|
|
13
31
|
};
|
|
14
32
|
const handleBlur = () => {
|
|
15
33
|
if (!immediate) {
|
|
16
34
|
setValue(inputValue);
|
|
17
35
|
}
|
|
18
|
-
setFocused(false);
|
|
36
|
+
// setFocused(false);
|
|
19
37
|
};
|
|
20
38
|
const onRefresh = () => {
|
|
21
39
|
setInputValue(getValue());
|
|
@@ -25,7 +43,10 @@ const TextField = (props) => {
|
|
|
25
43
|
const [inputValue, setInputValue] = useState(valueRef.current);
|
|
26
44
|
const onChange = (e) => {
|
|
27
45
|
var _a;
|
|
28
|
-
|
|
46
|
+
let text = (_a = e.target.value) !== null && _a !== void 0 ? _a : "";
|
|
47
|
+
if (noSpace) {
|
|
48
|
+
text = text.replace(/\s+/g, "");
|
|
49
|
+
}
|
|
29
50
|
if (text !== inputValue) {
|
|
30
51
|
valueRef.current = text;
|
|
31
52
|
setInputValue(valueRef.current);
|
|
@@ -34,6 +55,20 @@ const TextField = (props) => {
|
|
|
34
55
|
setValue(text);
|
|
35
56
|
}
|
|
36
57
|
};
|
|
37
|
-
|
|
58
|
+
const handleKeyDown = (e) => {
|
|
59
|
+
if (noSpace && e.key === " ") {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const alignClass = {
|
|
64
|
+
left: "text-left",
|
|
65
|
+
center: "text-center",
|
|
66
|
+
right: "text-right",
|
|
67
|
+
}[align];
|
|
68
|
+
const inReadMode = (_b = (_a = binding === null || binding === void 0 ? void 0 : binding.isReadMode) === null || _a === void 0 ? void 0 : _a.call(binding)) !== null && _b !== void 0 ? _b : false;
|
|
69
|
+
const defaultStyleClass = "text-sm px-2 py-2 border rounded-md shadow-sm transition-colors duration-150 ease-in-out w-full";
|
|
70
|
+
const focusStyleClass = `focus:outline-none ${inReadMode ? "bg-gray-50" : "focus:bg-yellow-50"} focus:ring-1 focus:ring-blue-400`;
|
|
71
|
+
const borderClass = "border-gray-300";
|
|
72
|
+
return (_jsx(UIComponent, Object.assign({}, (props !== null && props !== void 0 ? props : {}), { children: _jsx("input", { type: "text", ref: inputRef, onChange: onChange, onKeyDown: handleKeyDown, value: inputValue, onFocus: handleFocus, onBlur: handleBlur, required: required, disabled: inReadMode, placeholder: inReadMode ? "" : placeholder, className: `${defaultStyleClass} ${focusStyleClass} ${alignClass} ${borderClass}` }) })));
|
|
38
73
|
};
|
|
39
74
|
export default TextField;
|
|
@@ -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
|
|
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) {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Eye } from "lucide-react";
|
|
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
|
+
import ViewPage from "../action/ViewPage";
|
|
11
|
+
import useUIMenu from "../common/UIMenu";
|
|
12
|
+
const EditableMenu = (props) => {
|
|
13
|
+
const { name, depends, title, menugroup, items: itemsProp, data, addPage, viewPage } = props !== null && props !== void 0 ? props : {};
|
|
14
|
+
const [activeItem, setActiveItem] = useState(null);
|
|
15
|
+
const [resolvedData, setResolvedData] = useState(data);
|
|
16
|
+
const [sortedItems, setSortedItems] = useState([]);
|
|
17
|
+
const pageContext = usePageContext();
|
|
18
|
+
const pageView = usePageViewContext();
|
|
19
|
+
const uuid = Math.random().toString(36).slice(2);
|
|
20
|
+
const dragHandler = useDragHandler({
|
|
21
|
+
items: sortedItems,
|
|
22
|
+
onSort: setSortedItems,
|
|
23
|
+
onDrop: async (draggedItem, updatedItems) => {
|
|
24
|
+
const reordered = updatedItems.map((item, idx) => ({
|
|
25
|
+
id: item.id,
|
|
26
|
+
sortorder: idx + 1, // ← 1-based sortorder
|
|
27
|
+
}));
|
|
28
|
+
console.log("resolvedData", JSON.stringify(resolvedData, null, 2));
|
|
29
|
+
console.log("reordered", JSON.stringify(reordered, null, 2));
|
|
30
|
+
// const status = await pageContext?.postMgmt("menus", "reorder", {
|
|
31
|
+
// type: resolvedData?.params?.type,
|
|
32
|
+
// groupid: resolvedData?.params?.groupid,
|
|
33
|
+
// items: reordered,
|
|
34
|
+
// });
|
|
35
|
+
// console.log("status", status);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const { items } = useUIMenu({ menugroup, items: itemsProp, data: resolvedData });
|
|
39
|
+
const handleItemClick = (item) => {
|
|
40
|
+
// console.log("Item clicked:", item);
|
|
41
|
+
pageContext === null || pageContext === void 0 ? void 0 : pageContext.set(name, item);
|
|
42
|
+
setActiveItem(item);
|
|
43
|
+
};
|
|
44
|
+
const handleAdd = () => {
|
|
45
|
+
console.log("Add clicked", addPage);
|
|
46
|
+
const { url, mode } = addPage !== null && addPage !== void 0 ? addPage : {};
|
|
47
|
+
const idx = ["window", "popup"].indexOf(String(mode).toLowerCase());
|
|
48
|
+
console.log(idx);
|
|
49
|
+
if (idx >= 0) {
|
|
50
|
+
// pageView.setPage(url, { mode });
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// pageView.setSelectedPage(url);
|
|
54
|
+
pageContext.set(name, url);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const handleView = (item) => {
|
|
58
|
+
const { url, mode } = viewPage !== null && viewPage !== void 0 ? viewPage : {};
|
|
59
|
+
console.log("View clicked", url, mode, item);
|
|
60
|
+
};
|
|
61
|
+
const handleDelete = (item) => console.log("Delete clicked", item);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (items.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
const firstItem = items[0];
|
|
66
|
+
setActiveItem(firstItem);
|
|
67
|
+
setSortedItems(items);
|
|
68
|
+
pageContext === null || pageContext === void 0 ? void 0 : pageContext.set(name, items[0]);
|
|
69
|
+
}, [items]);
|
|
70
|
+
const resolveData = () => {
|
|
71
|
+
var _a;
|
|
72
|
+
if (!(data === null || data === void 0 ? void 0 : data.params))
|
|
73
|
+
return data;
|
|
74
|
+
const dependsValue = (_a = pageContext === null || pageContext === void 0 ? void 0 : pageContext.get(depends)) !== null && _a !== void 0 ? _a : {};
|
|
75
|
+
const resolvedParams = replaceValues(data.params, dependsValue);
|
|
76
|
+
return Object.assign(Object.assign({}, data), { params: resolvedParams });
|
|
77
|
+
};
|
|
78
|
+
const onRefresh = () => {
|
|
79
|
+
// when depends changes → re-resolve params
|
|
80
|
+
setResolvedData(resolveData());
|
|
81
|
+
// setActiveItem(null); // reset active item
|
|
82
|
+
};
|
|
83
|
+
useDependHandler({ name: depends, onRefresh });
|
|
84
|
+
return (_jsxs("div", { className: "p-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-lg font-semibold", children: title }), _jsx(ViewPage, { url: addPage === null || addPage === void 0 ? void 0 : addPage.url, mode: addPage === null || addPage === void 0 ? void 0 : addPage.mode, iconOnly: true, icon: _jsx(DynamicIcon, { icon: "Plus", size: 18 }), title: title })] }), _jsx("div", { className: "border-b border-gray-200 my-2" }), _jsx("div", { className: "flex flex-col gap-2", children: sortedItems.map((item, index) => {
|
|
85
|
+
var _a;
|
|
86
|
+
const isActive = (activeItem === null || activeItem === void 0 ? void 0 : activeItem.id) === item.id; // ← use activeItem
|
|
87
|
+
return (_jsx(DataBindingProvider, { data: { data: item }, children: _jsxs("div", { draggable: true, onDragStart: () => dragHandler.handleDragStart(index), onDragOver: (e) => dragHandler.handleDragOver(e, index), onDrop: dragHandler.handleDrop, onDragEnd: dragHandler.handleDragEnd, className: `flex items-center justify-between border-b p-2 cursor-pointer transition-colors
|
|
88
|
+
${isActive ? "bg-gray-200" : "bg-gray-50 border-gray-200 hover:bg-gray-200"}`, onClick: () => handleItemClick(item), children: [_jsx("span", { className: "text-gray-400 cursor-grab mr-2", children: _jsx(DynamicIcon, { icon: "GripVertical", size: 16 }) }), _jsx("span", { className: `text-base flex-1 ${isActive ? "font-medium" : ""}`, children: item.title }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ViewPage, { url: viewPage === null || viewPage === void 0 ? void 0 : viewPage.url, mode: viewPage === null || viewPage === void 0 ? void 0 : viewPage.mode, popupClassName: viewPage === null || viewPage === void 0 ? void 0 : viewPage.popupClassName, iconOnly: true, icon: _jsx(Eye, { size: 16, color: "blue" }) }), renderButton((_a = item.deleteIcon) !== null && _a !== void 0 ? _a : "Trash", () => handleDelete(item), "text-red-500")] })] }) }, `${uuid}-${index}`));
|
|
89
|
+
}) })] }));
|
|
90
|
+
};
|
|
91
|
+
export default EditableMenu;
|
|
92
|
+
/* ------------------------------------------------------------------ */
|
|
93
|
+
/* Render Button */
|
|
94
|
+
/* ------------------------------------------------------------------ */
|
|
95
|
+
const renderButton = (icon, onClick, color = "text-gray-500") => (_jsx("span", { className: `${color} cursor-pointer hover:opacity-70 transition-opacity`, onClick: (e) => {
|
|
96
|
+
e.stopPropagation();
|
|
97
|
+
onClick();
|
|
98
|
+
}, children: _jsx(DynamicIcon, { icon: icon, size: 18 }) }));
|
|
99
|
+
const useDragHandler = ({ items, onSort, onDrop }) => {
|
|
100
|
+
const dragIndexRef = useRef(null);
|
|
101
|
+
const dragOverIndexRef = useRef(null);
|
|
102
|
+
const handleDragStart = (index) => {
|
|
103
|
+
dragIndexRef.current = index;
|
|
104
|
+
};
|
|
105
|
+
const handleDragOver = (e, index) => {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
dragOverIndexRef.current = index;
|
|
108
|
+
};
|
|
109
|
+
const handleDrop = () => {
|
|
110
|
+
const from = dragIndexRef.current;
|
|
111
|
+
const to = dragOverIndexRef.current;
|
|
112
|
+
if (from == null || to == null || from === to)
|
|
113
|
+
return;
|
|
114
|
+
const draggedItem = items[from];
|
|
115
|
+
const updated = [...items];
|
|
116
|
+
const [moved] = updated.splice(from, 1);
|
|
117
|
+
updated.splice(to, 0, moved);
|
|
118
|
+
onSort(updated);
|
|
119
|
+
onDrop === null || onDrop === void 0 ? void 0 : onDrop(draggedItem, updated);
|
|
120
|
+
dragIndexRef.current = null;
|
|
121
|
+
dragOverIndexRef.current = null;
|
|
122
|
+
};
|
|
123
|
+
const handleDragEnd = () => {
|
|
124
|
+
dragIndexRef.current = null;
|
|
125
|
+
dragOverIndexRef.current = null;
|
|
126
|
+
};
|
|
127
|
+
return { handleDragStart, handleDragOver, handleDrop, handleDragEnd };
|
|
128
|
+
};
|
|
@@ -4,8 +4,8 @@ import { useEffect, useState } from "react";
|
|
|
4
4
|
import { usePageContext } from "../../core/PageContext";
|
|
5
5
|
import { usePageViewContext } from "../../core/PageViewContext";
|
|
6
6
|
import useDependHandler from "../../core/UIDependHandler";
|
|
7
|
-
import HPanel from "../../layouts/HPanel";
|
|
8
|
-
import VPanel from "../../layouts/VPanel";
|
|
7
|
+
import { HPanel } from "../../layouts/HPanel";
|
|
8
|
+
import { VPanel } from "../../layouts/VPanel";
|
|
9
9
|
const TabMenu = (props) => {
|
|
10
10
|
var _a;
|
|
11
11
|
const { name, depends, items = [], data, menugroup, orientation = "horizontal" } = props !== null && props !== void 0 ? 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
|
-
|
|
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);
|
|
@@ -84,6 +101,7 @@ const TreeMenu = (props) => {
|
|
|
84
101
|
const [openGroups, setOpenGroups] = useState([]);
|
|
85
102
|
const [activeItem, setActiveItem] = useState({});
|
|
86
103
|
const [items, setItems] = useState([]);
|
|
104
|
+
const [hashPage, setHashPage] = useState(null);
|
|
87
105
|
const initializedRef = useRef(false);
|
|
88
106
|
const { items: rawItems } = useUIMenu({
|
|
89
107
|
items: itemsProp,
|
|
@@ -95,7 +113,9 @@ const TreeMenu = (props) => {
|
|
|
95
113
|
const { path = "" } = (_a = pageView.getOriginalLocationInfo()) !== null && _a !== void 0 ? _a : {};
|
|
96
114
|
const [, ...anchors] = path.split("#");
|
|
97
115
|
const [selectedPath] = anchors;
|
|
98
|
-
|
|
116
|
+
if (selectedPath) {
|
|
117
|
+
pageContext.set(contextKey, selectedPath);
|
|
118
|
+
}
|
|
99
119
|
}, []);
|
|
100
120
|
/* ---------------------- Sync active item from page view ---------------------- */
|
|
101
121
|
useLayoutEffect(() => {
|
|
@@ -115,23 +135,27 @@ const TreeMenu = (props) => {
|
|
|
115
135
|
}, [rawItems]);
|
|
116
136
|
/* ---------------------- Auto-select first item on init ---------------------- */
|
|
117
137
|
useEffect(() => {
|
|
138
|
+
var _a;
|
|
118
139
|
if (initializedRef.current || items.length === 0)
|
|
119
140
|
return;
|
|
120
141
|
const firstItem = findFirstItem(items);
|
|
121
142
|
if (!(firstItem === null || firstItem === void 0 ? void 0 : firstItem.page))
|
|
122
143
|
return;
|
|
123
144
|
let selectedPage = pageContext.get(contextKey);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
if (selectedPage == null) {
|
|
146
|
+
// fallback to window.location.hash
|
|
147
|
+
const hash = (_a = window.location.hash) !== null && _a !== void 0 ? _a : "";
|
|
148
|
+
const [, firstAnchor] = hash.split("#");
|
|
149
|
+
if (firstAnchor) {
|
|
150
|
+
selectedPage = firstAnchor; // strip query params
|
|
151
|
+
setHashPage(selectedPage);
|
|
152
|
+
}
|
|
128
153
|
}
|
|
129
|
-
// console.log("treemenu selected page final", contextKey, selectedPage);
|
|
130
154
|
if (selectedPage != null) {
|
|
131
155
|
const selectedItem = findItemByPage(items, selectedPage);
|
|
132
|
-
// console.log("treemenu findItemByPage", selectedPage, selectedItem);
|
|
133
156
|
if (selectedItem) {
|
|
134
|
-
|
|
157
|
+
const groupPath = findGroupPath(items, selectedPage);
|
|
158
|
+
setOpenGroups(groupPath);
|
|
135
159
|
setActiveItem(selectedItem);
|
|
136
160
|
}
|
|
137
161
|
else {
|
|
@@ -144,7 +168,7 @@ const TreeMenu = (props) => {
|
|
|
144
168
|
setActiveItem(firstItem);
|
|
145
169
|
}
|
|
146
170
|
initializedRef.current = true;
|
|
147
|
-
}, [items, contextKey]);
|
|
171
|
+
}, [items, contextKey, hashPage]);
|
|
148
172
|
/* ---------------------- Sync page context on active item change ---------------------- */
|
|
149
173
|
useEffect(() => {
|
|
150
174
|
const { mode, page } = (activeItem !== null && activeItem !== void 0 ? activeItem : {});
|
|
@@ -155,7 +179,6 @@ const TreeMenu = (props) => {
|
|
|
155
179
|
pageView.setPage(page, { mode });
|
|
156
180
|
}
|
|
157
181
|
else {
|
|
158
|
-
pageView.setSelectedPage(page);
|
|
159
182
|
pageContext.set(contextKey, page);
|
|
160
183
|
}
|
|
161
184
|
}, [activeItem]);
|
|
@@ -182,8 +205,7 @@ const TreeMenu = (props) => {
|
|
|
182
205
|
};
|
|
183
206
|
/* ---------------------- Events ---------------------- */
|
|
184
207
|
const handleSubItemClick = (item) => {
|
|
185
|
-
if (!item.page || item.page === activeItem.page)
|
|
186
|
-
return;
|
|
208
|
+
// if (!item.page || item.page === activeItem.page) return;
|
|
187
209
|
setActiveItem(item);
|
|
188
210
|
};
|
|
189
211
|
/* ---------------------- Recursive render ---------------------- */
|
|
@@ -50,7 +50,7 @@ export interface DataListAttr {
|
|
|
50
50
|
searchDebounce?: number;
|
|
51
51
|
onSearchChange?: (text: string) => void;
|
|
52
52
|
filters?: FilterDefinition[] | FilterDefinition[][];
|
|
53
|
-
|
|
53
|
+
filterPage?: Record<string, any>;
|
|
54
54
|
onFilterChange?: (filters: Record<string, any>) => void;
|
|
55
55
|
sortable?: boolean;
|
|
56
56
|
defaultSort?: {
|