@ramesesinc/platform-core 0.1.5 → 0.1.8
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/LookupPage.js +9 -31
- package/dist/components/action/ViewPage.d.ts +2 -0
- package/dist/components/action/ViewPage.js +25 -31
- package/dist/components/common/UIComponent.js +4 -3
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/table/DataList.js +2 -2
- package/dist/components/view/PopupView.d.ts +13 -0
- package/dist/components/view/PopupView.js +25 -20
- package/dist/core/DataContext.d.ts +7 -4
- package/dist/core/DataContext.js +16 -4
- package/dist/core/Page.js +25 -26
- package/dist/core/PageCache.js +16 -3
- package/dist/core/PageContext.js +90 -18
- package/dist/core/PageViewContext.d.ts +13 -1
- package/dist/core/PageViewContext.js +89 -5
- package/dist/core/PopupContext.d.ts +49 -0
- package/dist/core/PopupContext.js +380 -0
- package/dist/core/RowContext.js +1 -1
- package/dist/core/WindowContext.d.ts +15 -0
- package/dist/core/WindowContext.js +28 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/index.css +25 -7
- package/dist/lib/utils/BeanUtils.js +7 -7
- package/dist/templates/DataListTemplate.js +7 -2
- package/dist/templates/ExplorerTemplate.js +1 -1
- package/package.json +5 -5
- package/dist/components/action/AlertMessage.tsx +0 -38
- package/dist/components/action/Button.tsx +0 -230
- package/dist/components/action/CancelEdit.tsx +0 -40
- package/dist/components/action/DeleteData.tsx +0 -73
- package/dist/components/action/Edit.tsx +0 -40
- package/dist/components/action/LookupPage.tsx +0 -113
- package/dist/components/action/ProcessRunner.tsx +0 -337
- package/dist/components/action/Refresh.tsx +0 -35
- package/dist/components/action/SaveData.tsx +0 -74
- package/dist/components/action/SelectData.tsx +0 -47
- package/dist/components/action/Undo.tsx +0 -50
- package/dist/components/action/UpdateContext.tsx +0 -40
- package/dist/components/action/UpdateData.tsx +0 -49
- package/dist/components/action/ViewBackPage.tsx +0 -46
- package/dist/components/action/ViewPage.tsx +0 -141
- package/dist/components/common/UIComponent.tsx +0 -86
- package/dist/components/common/UIInput.tsx +0 -49
- package/dist/components/common/UIMenu.tsx +0 -91
- package/dist/components/index.ts +0 -51
- package/dist/components/input/CodeEditor.tsx +0 -188
- package/dist/components/input/DateField.tsx +0 -274
- package/dist/components/input/DayPicker.tsx +0 -5
- package/dist/components/input/HtmlCode.tsx +0 -203
- package/dist/components/input/JsonCode.tsx +0 -205
- package/dist/components/input/MonthPicker.tsx +0 -5
- package/dist/components/input/ScriptCode.tsx +0 -195
- package/dist/components/input/Select.tsx +0 -78
- package/dist/components/input/SqlCode.tsx +0 -162
- package/dist/components/input/StringDecision.tsx +0 -64
- package/dist/components/input/Text.tsx +0 -57
- package/dist/components/input/YearPicker.tsx +0 -81
- package/dist/components/list/IconMenu.tsx +0 -115
- package/dist/components/list/TabMenu.tsx +0 -127
- package/dist/components/list/TreeMenu.tsx +0 -279
- package/dist/components/list/TxnTaskList.tsx +0 -198
- package/dist/components/output/Label.tsx +0 -50
- package/dist/components/table/DataList.tsx +0 -820
- package/dist/components/table/DataTable.tsx +0 -572
- package/dist/components/table/ListHandler.ts +0 -276
- package/dist/components/table/TableContext.tsx +0 -122
- package/dist/components/view/ComponentView.tsx +0 -102
- package/dist/components/view/FilterView.tsx +0 -21
- package/dist/components/view/HtmlForm.tsx +0 -176
- package/dist/components/view/HtmlView.tsx +0 -98
- package/dist/components/view/IFrameView.tsx +0 -48
- package/dist/components/view/Modal.tsx +0 -72
- package/dist/components/view/PageView.tsx +0 -131
- package/dist/components/view/PopupView.tsx +0 -160
- package/dist/components/view/RootView.tsx +0 -109
- package/dist/components/view/WizardView.tsx +0 -48
- package/dist/lib/layouts/BorderLayout.tsx +0 -31
- package/dist/lib/layouts/CardLayout.tsx +0 -73
- package/dist/lib/layouts/CenterLayout.tsx +0 -20
- package/dist/lib/layouts/GridLayout.tsx +0 -20
- package/dist/lib/layouts/HPanel.tsx +0 -31
- package/dist/lib/layouts/HorizontalLayout.tsx +0 -29
- package/dist/lib/layouts/MainLayout.tsx +0 -16
- package/dist/lib/layouts/PageLayout.tsx +0 -29
- package/dist/lib/layouts/VPanel.tsx +0 -27
- package/dist/lib/layouts/XLayout.tsx +0 -29
- package/dist/lib/layouts/YLayout.tsx +0 -29
- package/dist/lib/layouts/index.ts +0 -13
- /package/dist/components/action/{UpdateContext.d.ts → UpdateState.d.ts} +0 -0
- /package/dist/components/action/{UpdateContext.js → UpdateState.js} +0 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// src/core/PageContext.tsx
|
|
3
|
+
import { mergeCN } from "@ramesesinc/client";
|
|
4
|
+
import { createContext, useContext, useRef, useEffect, useLayoutEffect, useState, useSyncExternalStore, useMemo, } from "react";
|
|
5
|
+
//
|
|
6
|
+
// CONTEXT
|
|
7
|
+
//
|
|
8
|
+
const PopupManagerContext = createContext(null);
|
|
9
|
+
function createPopupStore() {
|
|
10
|
+
let popups = [];
|
|
11
|
+
const listeners = new Set();
|
|
12
|
+
function emit() {
|
|
13
|
+
listeners.forEach((l) => {
|
|
14
|
+
// console.log("emitting listener =>", l);
|
|
15
|
+
l();
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function subscribe(listener) {
|
|
19
|
+
listeners.add(listener);
|
|
20
|
+
// console.log("subscribers", listeners);
|
|
21
|
+
return () => listeners.delete(listener);
|
|
22
|
+
}
|
|
23
|
+
function getSnapshot() {
|
|
24
|
+
return popups;
|
|
25
|
+
}
|
|
26
|
+
function addPopup(state) {
|
|
27
|
+
popups = [...popups, state];
|
|
28
|
+
emit();
|
|
29
|
+
}
|
|
30
|
+
function updatePopup(id, patch) {
|
|
31
|
+
if (patch == null || Object.keys(patch).length === 0)
|
|
32
|
+
return;
|
|
33
|
+
let state_names = ["visible", "content"];
|
|
34
|
+
const popupState_patch = {};
|
|
35
|
+
Object.keys(patch).forEach((key) => {
|
|
36
|
+
if (state_names.indexOf(key) >= 0) {
|
|
37
|
+
popupState_patch[key] = patch[key];
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
state_names = [
|
|
41
|
+
"modal", "draggable", "showCloseButton",
|
|
42
|
+
"minWidth", "minHeight", "className", "title",
|
|
43
|
+
"width", "height"
|
|
44
|
+
];
|
|
45
|
+
const popupOptions_patch = {};
|
|
46
|
+
Object.keys(patch).forEach((key) => {
|
|
47
|
+
if (state_names.indexOf(key) >= 0) {
|
|
48
|
+
popupOptions_patch[key] = patch[key];
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// console.log("updatePopup", id, popupState_patch, popupOptions_patch);
|
|
52
|
+
popups = popups.map((p) => {
|
|
53
|
+
var _a;
|
|
54
|
+
if (p.id === id) {
|
|
55
|
+
return Object.assign(Object.assign(Object.assign({}, p), popupState_patch), { options: Object.assign(Object.assign({}, ((_a = p.options) !== null && _a !== void 0 ? _a : {})), popupOptions_patch) });
|
|
56
|
+
}
|
|
57
|
+
return p;
|
|
58
|
+
});
|
|
59
|
+
emit();
|
|
60
|
+
}
|
|
61
|
+
function removePopup(id) {
|
|
62
|
+
popups = popups.filter((p) => p.id !== id);
|
|
63
|
+
emit();
|
|
64
|
+
}
|
|
65
|
+
function removeAll() {
|
|
66
|
+
popups = [];
|
|
67
|
+
emit();
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
subscribe,
|
|
71
|
+
getSnapshot,
|
|
72
|
+
addPopup,
|
|
73
|
+
updatePopup,
|
|
74
|
+
removePopup,
|
|
75
|
+
removeAll,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Singleton store
|
|
79
|
+
const store = createPopupStore();
|
|
80
|
+
//
|
|
81
|
+
// POPUP MANAGER
|
|
82
|
+
//
|
|
83
|
+
const counterRef = { current: 0 };
|
|
84
|
+
function createPopupManager() {
|
|
85
|
+
const create = (initialOptions) => {
|
|
86
|
+
// console.log("create initialOptions => ", initialOptions);
|
|
87
|
+
const counter = ++counterRef.current;
|
|
88
|
+
const id = `popup-${counter}`;
|
|
89
|
+
const options = Object.assign({ modal: true, draggable: true, showCloseButton: true, minWidth: 400, minHeight: 200, className: "" }, (initialOptions !== null && initialOptions !== void 0 ? initialOptions : {}));
|
|
90
|
+
const state = {
|
|
91
|
+
id,
|
|
92
|
+
counter,
|
|
93
|
+
content: null,
|
|
94
|
+
visible: false,
|
|
95
|
+
options
|
|
96
|
+
};
|
|
97
|
+
store.addPopup(state);
|
|
98
|
+
return {
|
|
99
|
+
id,
|
|
100
|
+
setContent(content) {
|
|
101
|
+
store.updatePopup(id, { content });
|
|
102
|
+
},
|
|
103
|
+
show() {
|
|
104
|
+
store.updatePopup(id, { visible: true });
|
|
105
|
+
},
|
|
106
|
+
hide() {
|
|
107
|
+
// store.updatePopup(id, { visible: false });
|
|
108
|
+
store.removePopup(id);
|
|
109
|
+
},
|
|
110
|
+
close() {
|
|
111
|
+
store.removePopup(id);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
const closeAll = () => {
|
|
116
|
+
store.removeAll();
|
|
117
|
+
};
|
|
118
|
+
return { create, closeAll };
|
|
119
|
+
}
|
|
120
|
+
// Singleton manager
|
|
121
|
+
const popupMgr = createPopupManager();
|
|
122
|
+
//
|
|
123
|
+
// POPUP OVERLAY COMPONENT
|
|
124
|
+
//
|
|
125
|
+
function PopupContentLayout({ data, style = null, children, }) {
|
|
126
|
+
const { options = {} } = data !== null && data !== void 0 ? data : {};
|
|
127
|
+
const { showCloseButton = true, showTitle = false, title, className = "" } = options;
|
|
128
|
+
const preferredStyles = Object.assign({}, (style !== null && style !== void 0 ? style : {}));
|
|
129
|
+
const layoutContentClass = mergeCN("min-w-[200px] min-h-[100px] overflow-auto p-0", className);
|
|
130
|
+
const getTitleStyles = () => {
|
|
131
|
+
const cssProps = {};
|
|
132
|
+
if ((title !== null && title !== void 0 ? title : "").trim() === "" && !showCloseButton) {
|
|
133
|
+
cssProps.paddingTop = '8px';
|
|
134
|
+
}
|
|
135
|
+
return cssProps;
|
|
136
|
+
};
|
|
137
|
+
const handleClose = () => {
|
|
138
|
+
store.removePopup(String(data.id));
|
|
139
|
+
};
|
|
140
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-row gap-2", children: [_jsx("label", { id: "layout-title", className: "popup-drag-handle flex-1 p-0 text-sm font-semibold text-gray-800 truncate min-w-[100px]", style: Object.assign({ paddingLeft: "8px", cursor: 'grab', userSelect: 'none' }, getTitleStyles()), children: showTitle ? (title !== null && title !== void 0 ? title : "") : "" }), showCloseButton && (_jsx("button", { id: "layout-button", onClick: handleClose, className: "flex flex-shrink-1 justify-center items-center p-0 m-0 w-6 h-6 rounded-md font-bold text-gray-400 hover:text-gray-700 hover:bg-gray-100", style: { fontSize: "14px" }, children: "\u2715" }))] }), _jsx("div", { id: "layout-content", className: layoutContentClass, style: Object.assign({ padding: "10px", paddingTop: "4px" }, preferredStyles), children: children !== null && children !== void 0 ? children : null })] }));
|
|
141
|
+
}
|
|
142
|
+
function PopupOverlay({ popup }) {
|
|
143
|
+
var _a;
|
|
144
|
+
const modalRef = useRef(null);
|
|
145
|
+
const [layout, setLayout] = useState({ opacity: 0, top: 0, left: 0 });
|
|
146
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
147
|
+
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
|
148
|
+
const [windowStart, setWindowStart] = useState({ x: 0, y: 0 });
|
|
149
|
+
const { visible: isVisible = false, options: popupOptions = {} } = popup;
|
|
150
|
+
const { modal: isModal = true, draggable: isDraggable = true, showCloseButton: allowShowCloseButton = true, minWidth = 300, minHeight = 150, width, height, className = "", } = popupOptions;
|
|
151
|
+
// Get all visible popups to determine backdrop count
|
|
152
|
+
const allPopups = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
153
|
+
const visibleModalPopups = allPopups.filter(p => p.visible && isModal);
|
|
154
|
+
const isLastModalPopup = ((_a = visibleModalPopups.at(-1)) === null || _a === void 0 ? void 0 : _a.id) === popup.id;
|
|
155
|
+
// --- LOGIC: DRAG FUNCTIONALITY ---
|
|
156
|
+
// Handle mouse events for dragging the popup window
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (!isVisible)
|
|
159
|
+
return;
|
|
160
|
+
const handleMouseMove = (e) => {
|
|
161
|
+
if (!isDraggable)
|
|
162
|
+
return;
|
|
163
|
+
if (!isDragging)
|
|
164
|
+
return;
|
|
165
|
+
const deltaX = e.clientX - dragStart.x;
|
|
166
|
+
const deltaY = e.clientY - dragStart.y;
|
|
167
|
+
setLayout(prev => (Object.assign(Object.assign({}, prev), { top: Math.max(0, windowStart.y + deltaY), left: Math.max(0, windowStart.x + deltaX) })));
|
|
168
|
+
};
|
|
169
|
+
const handleMouseUp = () => {
|
|
170
|
+
setIsDragging(false);
|
|
171
|
+
};
|
|
172
|
+
if (isDragging) {
|
|
173
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
174
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
175
|
+
document.body.style.userSelect = 'none';
|
|
176
|
+
document.body.style.cursor = 'grabbing';
|
|
177
|
+
}
|
|
178
|
+
return () => {
|
|
179
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
180
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
181
|
+
document.body.style.userSelect = '';
|
|
182
|
+
document.body.style.cursor = '';
|
|
183
|
+
};
|
|
184
|
+
}, [isDragging, dragStart, windowStart, isVisible]);
|
|
185
|
+
const handleMouseDown = (e) => {
|
|
186
|
+
if (!isDraggable)
|
|
187
|
+
return;
|
|
188
|
+
// Allow dragging when clicking on the popup container or drag handle
|
|
189
|
+
const target = e.target;
|
|
190
|
+
const isDragHandle = target.classList.contains('popup-drag-handle') ||
|
|
191
|
+
target.closest('.popup-drag-handle');
|
|
192
|
+
if (target !== modalRef.current && !isDragHandle)
|
|
193
|
+
return;
|
|
194
|
+
setIsDragging(true);
|
|
195
|
+
setDragStart({ x: e.clientX, y: e.clientY });
|
|
196
|
+
setWindowStart({ x: layout.left, y: layout.top });
|
|
197
|
+
};
|
|
198
|
+
// --- LOGIC: SCROLL LOCK ---
|
|
199
|
+
// Prevents background body from scrolling while a MODAL is open
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (isVisible && isModal) {
|
|
202
|
+
const originalStyle = window.getComputedStyle(document.body).overflow;
|
|
203
|
+
document.body.style.overflow = "hidden";
|
|
204
|
+
return () => {
|
|
205
|
+
document.body.style.overflow = originalStyle;
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}, [isVisible, isModal]);
|
|
209
|
+
// --- LOGIC: FOCUS TRAP ---
|
|
210
|
+
// Keeps keyboard navigation inside the modal
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!isVisible || !isModal)
|
|
213
|
+
return;
|
|
214
|
+
const element = modalRef.current;
|
|
215
|
+
if (!element)
|
|
216
|
+
return;
|
|
217
|
+
// A. Focus the modal container immediately
|
|
218
|
+
element.focus();
|
|
219
|
+
// B. Handle Tab Key & Escape Key
|
|
220
|
+
const handleKeyDown = (e) => {
|
|
221
|
+
if (e.key === "Tab") {
|
|
222
|
+
const focusable = element.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
223
|
+
if (focusable.length === 0) {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const first = focusable[0];
|
|
228
|
+
const last = focusable[focusable.length - 1];
|
|
229
|
+
// Shift + Tab: Loop to last
|
|
230
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
231
|
+
e.preventDefault();
|
|
232
|
+
last.focus();
|
|
233
|
+
}
|
|
234
|
+
// Tab: Loop to first
|
|
235
|
+
else if (!e.shiftKey && document.activeElement === last) {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
first.focus();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Escape Key: Don't close automatically - let user handle via close button
|
|
241
|
+
// if (e.key === "Escape") {
|
|
242
|
+
// store.removePopup(popup.id);
|
|
243
|
+
// }
|
|
244
|
+
};
|
|
245
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
246
|
+
return () => {
|
|
247
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
248
|
+
};
|
|
249
|
+
}, [isVisible, isModal, popup.id]);
|
|
250
|
+
const padding = { top: 4, right: 4, bottom: 4, left: 4 };
|
|
251
|
+
// --- LOGIC: POSITIONING ---
|
|
252
|
+
// Measures content size before showing to center it perfectly
|
|
253
|
+
useLayoutEffect(() => {
|
|
254
|
+
if (!isVisible)
|
|
255
|
+
return;
|
|
256
|
+
const element = modalRef.current;
|
|
257
|
+
if (!element)
|
|
258
|
+
return;
|
|
259
|
+
const measureAndCenter = () => {
|
|
260
|
+
var _a, _b, _c, _d;
|
|
261
|
+
let { width, height } = element.getBoundingClientRect();
|
|
262
|
+
width = Math.trunc(width);
|
|
263
|
+
height = Math.trunc(height);
|
|
264
|
+
const newlayout = { opacity: 1, top: 0, left: 0 };
|
|
265
|
+
const winWidth = Math.trunc(window.innerWidth);
|
|
266
|
+
const winHeight = Math.trunc(window.innerHeight);
|
|
267
|
+
if (width > winWidth) {
|
|
268
|
+
width = winWidth;
|
|
269
|
+
newlayout.width = width;
|
|
270
|
+
}
|
|
271
|
+
if (height > winHeight) {
|
|
272
|
+
height = winHeight;
|
|
273
|
+
newlayout.height = height;
|
|
274
|
+
}
|
|
275
|
+
newlayout.top = Math.max(0, (winHeight - height) / 2);
|
|
276
|
+
newlayout.left = Math.max(0, (winWidth - width) / 2);
|
|
277
|
+
newlayout.top = Math.max(0, newlayout.top - padding.top);
|
|
278
|
+
newlayout.left = Math.max(0, newlayout.left - padding.left);
|
|
279
|
+
// console.log("measure dimensions", { width, height, winWidth, winHeight, newlayout });
|
|
280
|
+
const oldlayoutStr = `top=${layout.top}, left=${layout.left}, width=${(_a = layout.width) !== null && _a !== void 0 ? _a : null}, height=${(_b = layout.height) !== null && _b !== void 0 ? _b : null}`;
|
|
281
|
+
const newlayoutStr = `top=${newlayout.top}, left=${newlayout.left}, width=${(_c = newlayout.width) !== null && _c !== void 0 ? _c : null}, height=${(_d = newlayout.height) !== null && _d !== void 0 ? _d : null}`;
|
|
282
|
+
const hasChanged = (oldlayoutStr !== newlayoutStr);
|
|
283
|
+
// console.log("layout change", { hasChanged, old: oldlayoutStr, new: newlayoutStr });
|
|
284
|
+
if (hasChanged)
|
|
285
|
+
setLayout(newlayout);
|
|
286
|
+
};
|
|
287
|
+
// initial computation
|
|
288
|
+
measureAndCenter();
|
|
289
|
+
// Watch for size changes
|
|
290
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
291
|
+
measureAndCenter();
|
|
292
|
+
});
|
|
293
|
+
resizeObserver.observe(element);
|
|
294
|
+
return () => {
|
|
295
|
+
resizeObserver.disconnect();
|
|
296
|
+
};
|
|
297
|
+
}, [isVisible]);
|
|
298
|
+
// Reset layout when hidden
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
if (!isVisible) {
|
|
301
|
+
setLayout({ opacity: 0, top: 0, left: 0, width: 0, height: 0 });
|
|
302
|
+
}
|
|
303
|
+
}, [isVisible]);
|
|
304
|
+
// --- RENDER ---
|
|
305
|
+
// If hidden, render nothing (unmounts children)
|
|
306
|
+
if (!isVisible)
|
|
307
|
+
return null;
|
|
308
|
+
const zIndex = 10000 + (popup.counter * 25);
|
|
309
|
+
const layoutStyles = { top: layout.top, left: layout.left };
|
|
310
|
+
if (width != null && String(width).trim() !== '') {
|
|
311
|
+
let sval = String(width).trim().toLowerCase();
|
|
312
|
+
if (!sval.endsWith("%") && !sval.endsWith("px")) {
|
|
313
|
+
sval = `${sval}px`;
|
|
314
|
+
}
|
|
315
|
+
layoutStyles.width = sval;
|
|
316
|
+
}
|
|
317
|
+
if (height != null && String(height).trim() !== '') {
|
|
318
|
+
let sval = String(height).trim().toLowerCase();
|
|
319
|
+
if (!sval.endsWith("%") && !sval.endsWith("px")) {
|
|
320
|
+
sval = `${sval}px`;
|
|
321
|
+
}
|
|
322
|
+
layoutStyles.height = sval;
|
|
323
|
+
}
|
|
324
|
+
return (_jsxs(_Fragment, { children: [isModal && (_jsx("div", {
|
|
325
|
+
// Backdrop click handler removed - don't close automatically
|
|
326
|
+
style: {
|
|
327
|
+
position: "fixed",
|
|
328
|
+
inset: 0,
|
|
329
|
+
backgroundColor: isLastModalPopup ? "rgba(0,0,0,0.5)" : "transparent",
|
|
330
|
+
zIndex: zIndex - 4,
|
|
331
|
+
pointerEvents: "auto",
|
|
332
|
+
cursor: "default",
|
|
333
|
+
}, "aria-hidden": "true" })), _jsx("div", { ref: modalRef, className: `popup-manager-window ${className}`, "data-popup-id": popup.id, role: "dialog", "aria-modal": isModal ? "true" : "false", tabIndex: -1, onMouseDown: handleMouseDown, style: Object.assign({ position: "fixed", zIndex: zIndex, minWidth,
|
|
334
|
+
minHeight, opacity: layout.opacity, backgroundColor: "white", boxShadow: "0 4px 20px rgba(0,0,0,0.3)", borderRadius: "8px", padding: "4px", boxSizing: "border-box", outline: "none", pointerEvents: "auto" }, layoutStyles), children: _jsx(PopupSlotProvider, { data: popup, children: _jsx(PopupContentLayout, { data: popup, children: popup.content }) }) })] }));
|
|
335
|
+
}
|
|
336
|
+
//
|
|
337
|
+
// PUBLIC COMPONENTS
|
|
338
|
+
//
|
|
339
|
+
export function PopupManagerSlot() {
|
|
340
|
+
const popups = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
341
|
+
return (_jsx(PopupManagerContext.Provider, { value: popupMgr, children: popups.map((p) => {
|
|
342
|
+
return _jsx(PopupOverlay, { popup: p }, p.id);
|
|
343
|
+
}) }));
|
|
344
|
+
}
|
|
345
|
+
export function PopupManagerProvider({ children }) {
|
|
346
|
+
return (_jsx(PopupManagerContext.Provider, { value: popupMgr, children: children }));
|
|
347
|
+
}
|
|
348
|
+
//
|
|
349
|
+
// HOOK
|
|
350
|
+
//
|
|
351
|
+
export function usePopupManager() {
|
|
352
|
+
const ctx = useContext(PopupManagerContext);
|
|
353
|
+
if (!ctx) {
|
|
354
|
+
throw new Error("usePopupManager must be used within PopupManagerProvider or PopupManagerSlot");
|
|
355
|
+
}
|
|
356
|
+
return ctx;
|
|
357
|
+
}
|
|
358
|
+
const PopupSlotContext = createContext(null);
|
|
359
|
+
export function usePopupContext() {
|
|
360
|
+
const ctx = useContext(PopupSlotContext);
|
|
361
|
+
return ctx;
|
|
362
|
+
}
|
|
363
|
+
export function PopupSlotProvider(props) {
|
|
364
|
+
const { data, children } = props !== null && props !== void 0 ? props : {};
|
|
365
|
+
const { id: popupId } = data !== null && data !== void 0 ? data : {};
|
|
366
|
+
const value = useMemo(() => ({
|
|
367
|
+
getId: () => String(popupId),
|
|
368
|
+
close: () => {
|
|
369
|
+
store.removePopup(String(popupId));
|
|
370
|
+
},
|
|
371
|
+
setTitle: (title) => {
|
|
372
|
+
store.updatePopup(String(popupId), { title });
|
|
373
|
+
}
|
|
374
|
+
}), [data]);
|
|
375
|
+
return (_jsx(PopupSlotContext.Provider, { value: value, children: children }));
|
|
376
|
+
}
|
|
377
|
+
//
|
|
378
|
+
// EXPORTS
|
|
379
|
+
//
|
|
380
|
+
export { popupMgr };
|
package/dist/core/RowContext.js
CHANGED
|
@@ -7,7 +7,7 @@ export const useRowContext = () => {
|
|
|
7
7
|
return context;
|
|
8
8
|
};
|
|
9
9
|
export const RowProvider = ({ data, children }) => {
|
|
10
|
-
const dataWrapper = useCreateContextValue(data !== null && data !== void 0 ? data : {});
|
|
10
|
+
const dataWrapper = useCreateContextValue(data !== null && data !== void 0 ? data : {}, "row");
|
|
11
11
|
const values = {
|
|
12
12
|
getData: () => dataWrapper.getData(),
|
|
13
13
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PageDef } from ".";
|
|
2
|
+
export type WindowContextValue = {
|
|
3
|
+
getPageDef: () => PageDef | null;
|
|
4
|
+
setPageDef: (def: PageDef | null) => void;
|
|
5
|
+
getTitle: () => string | null;
|
|
6
|
+
setTitle: (title: string | null) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare const useWindowContext: () => WindowContextValue;
|
|
9
|
+
export type WindowHandleRef = {};
|
|
10
|
+
type WindowContextProviderProps = {
|
|
11
|
+
handle?: WindowHandleRef;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
};
|
|
14
|
+
export declare const WindowContextProvider: (props: WindowContextProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo, useRef } from "react";
|
|
3
|
+
const createDefaultValue = () => ({
|
|
4
|
+
getPageDef: () => null,
|
|
5
|
+
setPageDef: () => { },
|
|
6
|
+
getTitle: () => null,
|
|
7
|
+
setTitle: () => { },
|
|
8
|
+
});
|
|
9
|
+
const WindowContext = createContext(createDefaultValue());
|
|
10
|
+
export const useWindowContext = () => {
|
|
11
|
+
const context = useContext(WindowContext);
|
|
12
|
+
return context;
|
|
13
|
+
};
|
|
14
|
+
export const WindowContextProvider = (props) => {
|
|
15
|
+
const { handle, children } = props !== null && props !== void 0 ? props : {};
|
|
16
|
+
const dataRef = useRef({ def: null, title: null });
|
|
17
|
+
const value = useMemo(() => ({
|
|
18
|
+
getPageDef: () => dataRef.current.def,
|
|
19
|
+
setPageDef: (def) => {
|
|
20
|
+
dataRef.current.def = def;
|
|
21
|
+
},
|
|
22
|
+
getTitle: () => { var _a, _b; return (_b = (_a = dataRef.current) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : null; },
|
|
23
|
+
setTitle: (title) => {
|
|
24
|
+
dataRef.current.title = title;
|
|
25
|
+
},
|
|
26
|
+
}), [handle]);
|
|
27
|
+
return (_jsx(WindowContext.Provider, { value: value, children: children !== null && children !== void 0 ? children : null }));
|
|
28
|
+
};
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
export * from "./auth";
|
|
2
2
|
export { verifyToken, getUser, buildSessionHeader } from "./auth/session";
|
|
3
|
+
export type ChangeEvent = Record<string, any> & {
|
|
4
|
+
name: string | null;
|
|
5
|
+
value: any;
|
|
6
|
+
};
|
|
7
|
+
export type ChangeEventHandler = (e: ChangeEvent) => void;
|
|
8
|
+
export type VoidCallback = () => void;
|
|
3
9
|
export type EventHandler = {
|
|
4
10
|
[key: string]: (...args: any[]) => any;
|
|
5
11
|
};
|
|
12
|
+
export type PageDef = {
|
|
13
|
+
_id: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
template: string;
|
|
16
|
+
data?: {
|
|
17
|
+
api: string;
|
|
18
|
+
params?: Record<string, any>;
|
|
19
|
+
};
|
|
20
|
+
[key: string]: any;
|
|
21
|
+
};
|
package/dist/index.css
CHANGED
|
@@ -681,6 +681,9 @@ video {
|
|
|
681
681
|
.z-\[9999\] {
|
|
682
682
|
z-index: 9999;
|
|
683
683
|
}
|
|
684
|
+
.m-0 {
|
|
685
|
+
margin: 0px;
|
|
686
|
+
}
|
|
684
687
|
.mb-2 {
|
|
685
688
|
margin-bottom: 0.5rem;
|
|
686
689
|
}
|
|
@@ -813,6 +816,9 @@ video {
|
|
|
813
816
|
.max-h-\[60px\] {
|
|
814
817
|
max-height: 60px;
|
|
815
818
|
}
|
|
819
|
+
.min-h-\[100px\] {
|
|
820
|
+
min-height: 100px;
|
|
821
|
+
}
|
|
816
822
|
.min-h-\[60px\] {
|
|
817
823
|
min-height: 60px;
|
|
818
824
|
}
|
|
@@ -879,9 +885,18 @@ video {
|
|
|
879
885
|
.w-full {
|
|
880
886
|
width: 100%;
|
|
881
887
|
}
|
|
888
|
+
.min-w-\[100px\] {
|
|
889
|
+
min-width: 100px;
|
|
890
|
+
}
|
|
882
891
|
.min-w-\[180px\] {
|
|
883
892
|
min-width: 180px;
|
|
884
893
|
}
|
|
894
|
+
.min-w-\[200px\] {
|
|
895
|
+
min-width: 200px;
|
|
896
|
+
}
|
|
897
|
+
.min-w-\[300px\] {
|
|
898
|
+
min-width: 300px;
|
|
899
|
+
}
|
|
885
900
|
.min-w-\[50px\] {
|
|
886
901
|
min-width: 50px;
|
|
887
902
|
}
|
|
@@ -1112,10 +1127,6 @@ video {
|
|
|
1112
1127
|
--tw-bg-opacity: 1;
|
|
1113
1128
|
background-color: rgb(232 240 254 / var(--tw-bg-opacity, 1));
|
|
1114
1129
|
}
|
|
1115
|
-
.bg-black {
|
|
1116
|
-
--tw-bg-opacity: 1;
|
|
1117
|
-
background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1));
|
|
1118
|
-
}
|
|
1119
1130
|
.bg-black\/40 {
|
|
1120
1131
|
background-color: rgb(0 0 0 / 0.4);
|
|
1121
1132
|
}
|
|
@@ -1185,9 +1196,6 @@ video {
|
|
|
1185
1196
|
--tw-bg-opacity: 1;
|
|
1186
1197
|
background-color: rgb(250 204 21 / var(--tw-bg-opacity, 1));
|
|
1187
1198
|
}
|
|
1188
|
-
.bg-opacity-50 {
|
|
1189
|
-
--tw-bg-opacity: 0.5;
|
|
1190
|
-
}
|
|
1191
1199
|
.object-contain {
|
|
1192
1200
|
-o-object-fit: contain;
|
|
1193
1201
|
object-fit: contain;
|
|
@@ -1196,6 +1204,9 @@ video {
|
|
|
1196
1204
|
-o-object-fit: cover;
|
|
1197
1205
|
object-fit: cover;
|
|
1198
1206
|
}
|
|
1207
|
+
.p-0 {
|
|
1208
|
+
padding: 0px;
|
|
1209
|
+
}
|
|
1199
1210
|
.p-1 {
|
|
1200
1211
|
padding: 0.25rem;
|
|
1201
1212
|
}
|
|
@@ -1450,6 +1461,9 @@ video {
|
|
|
1450
1461
|
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
|
|
1451
1462
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
|
1452
1463
|
}
|
|
1464
|
+
.outline {
|
|
1465
|
+
outline-style: solid;
|
|
1466
|
+
}
|
|
1453
1467
|
.filter {
|
|
1454
1468
|
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
|
1455
1469
|
}
|
|
@@ -1566,6 +1580,10 @@ video {
|
|
|
1566
1580
|
--tw-text-opacity: 1;
|
|
1567
1581
|
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
|
|
1568
1582
|
}
|
|
1583
|
+
.hover\:text-gray-700:hover {
|
|
1584
|
+
--tw-text-opacity: 1;
|
|
1585
|
+
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
|
|
1586
|
+
}
|
|
1569
1587
|
.hover\:text-gray-800:hover {
|
|
1570
1588
|
--tw-text-opacity: 1;
|
|
1571
1589
|
color: rgb(31 41 55 / var(--tw-text-opacity, 1));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const getValue = (path, data) => {
|
|
2
2
|
if (path == null)
|
|
3
|
-
return
|
|
3
|
+
return null;
|
|
4
4
|
// If path is an object, recursively resolve all its values
|
|
5
5
|
if (typeof path === "object") {
|
|
6
6
|
const resolved = {};
|
|
@@ -10,7 +10,7 @@ export const getValue = (path, data) => {
|
|
|
10
10
|
return resolved;
|
|
11
11
|
}
|
|
12
12
|
if (typeof path !== "string")
|
|
13
|
-
return
|
|
13
|
+
return null;
|
|
14
14
|
if (!path.includes("{"))
|
|
15
15
|
return path;
|
|
16
16
|
// If the entire string is a single placeholder e.g. "{vdata}" or "{vdata.company.name}"
|
|
@@ -21,10 +21,10 @@ export const getValue = (path, data) => {
|
|
|
21
21
|
let current = data;
|
|
22
22
|
for (const token of tokens) {
|
|
23
23
|
if (current == null)
|
|
24
|
-
return
|
|
24
|
+
return null;
|
|
25
25
|
current = current[token];
|
|
26
26
|
}
|
|
27
|
-
return current
|
|
27
|
+
return current == null ? null : current;
|
|
28
28
|
}
|
|
29
29
|
// If the string contains placeholders mixed with other text e.g. "Hello {name}!"
|
|
30
30
|
// fall back to string replacement
|
|
@@ -36,13 +36,13 @@ export const getValue = (path, data) => {
|
|
|
36
36
|
return match;
|
|
37
37
|
current = current[token];
|
|
38
38
|
}
|
|
39
|
-
return current
|
|
39
|
+
return current == null ? null : current;
|
|
40
40
|
});
|
|
41
41
|
};
|
|
42
42
|
export const replaceValues = (obj, source) => {
|
|
43
43
|
// Handle null or undefined
|
|
44
|
-
if (obj
|
|
45
|
-
return
|
|
44
|
+
if (obj == null) {
|
|
45
|
+
return null;
|
|
46
46
|
}
|
|
47
47
|
// Handle primitives (numbers, booleans)
|
|
48
48
|
if (typeof obj !== 'object' && typeof obj !== 'string') {
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import DataList from "../components/table/DataList";
|
|
3
|
+
import { usePageViewContext } from "../core/PageViewContext";
|
|
3
4
|
import Panel from "../core/Panel";
|
|
4
5
|
import BorderLayout from "../layouts/BorderLayout";
|
|
5
6
|
const DataListTemplate = (props) => {
|
|
6
7
|
const { title, cols, data, rowsPerPage, rowActions, bulkActions, toolbarActions, header, footer, right, left, commonActions, filters, disableTotalCount, emptyMessage, errorMessage, striped, bordered, hover, dense, showPagination, paginationPosition, showPageInfo, showTotalCount, showRowsPerPage, rowsPerPageOptions, searchable, searchPlaceholder, searchDebounce, onSearchChange, showFilterPanel, onFilterChange, sortable, defaultSort, showSortIndicator, selectable, selectionMode, onSelectionChange, selectOnRowClick, onRowClick, onRowDoubleClick, toolbarTitle, showRefreshButton, showExportButton, onError, onRefresh, onExport, className, rowClassName, } = props !== null && props !== void 0 ? props : {};
|
|
7
|
-
const
|
|
8
|
+
const pageView = usePageViewContext();
|
|
9
|
+
const getPreferredTitle = () => {
|
|
10
|
+
return title !== null && title !== void 0 ? title : pageView.getUI().getTitle();
|
|
11
|
+
};
|
|
12
|
+
const attr = { title: getPreferredTitle(), cols, data, rowsPerPage, commonActions, rowActions, toolbarActions, bulkActions, filters };
|
|
8
13
|
const loadPanel = (section) => {
|
|
9
14
|
return _jsx(Panel, { content: section });
|
|
10
15
|
};
|
|
11
|
-
return (_jsx(BorderLayout, { className: "
|
|
16
|
+
return (_jsx(BorderLayout, { className: "p-0", north: loadPanel(header), south: loadPanel(footer), east: loadPanel(right), west: loadPanel(left), children: _jsx(DataList, { attr: attr }) }));
|
|
12
17
|
};
|
|
13
18
|
export default DataListTemplate;
|
|
@@ -11,7 +11,7 @@ const ExplorerTemplate = (props) => {
|
|
|
11
11
|
width: "230px",
|
|
12
12
|
opacity: open ? 1 : 0,
|
|
13
13
|
pointerEvents: open ? "auto" : "none",
|
|
14
|
-
}, children: _jsx(TreeMenu, { name: name, menugroup: menugroup, items: items, data: data }) }) }), _jsx("button", { onClick: () => setOpen((prev) => !prev), className: "absolute top-5 -right-4 w-8 h-8 bg-white border rounded-full flex items-center justify-center shadow z-10", children: open ? _jsx(ChevronLeft, { size: 18 }) : _jsx(ChevronRight, { size: 18 }) })] }), children: _jsx("div", { className: "px-6 py-
|
|
14
|
+
}, children: _jsx(TreeMenu, { name: name, menugroup: menugroup, items: items, data: data }) }) }), _jsx("button", { onClick: () => setOpen((prev) => !prev), className: "absolute top-5 -right-4 w-8 h-8 bg-white border rounded-full flex items-center justify-center shadow z-10", children: open ? _jsx(ChevronLeft, { size: 18 }) : _jsx(ChevronRight, { size: 18 }) })] }), children: _jsx("div", { className: "px-6 py-1", children: _jsx(PageView, { name: name, depends: name }) }) }) }));
|
|
15
15
|
};
|
|
16
16
|
ExplorerTemplate.hasSelectionHandling = true;
|
|
17
17
|
export default ExplorerTemplate;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ramesesinc/platform-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Platform Core Library",
|
|
5
5
|
"author": "Rameses Systems Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"./index.css": "./dist/index.css"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"build": "rm -rf dist && tsc && postcss src/styles/index.css -o dist/index.css
|
|
23
|
+
"build": "rm -rf dist && tsc && postcss src/styles/index.css -o dist/index.css",
|
|
24
24
|
"build2": "rm -rf dist && tsc",
|
|
25
25
|
"clean": "rm -rf dist",
|
|
26
26
|
"dev": "tsc --watch",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"react-dom": ">=18.2.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@ramesesinc/app-forms": "
|
|
40
|
-
"@ramesesinc/client": "
|
|
41
|
-
"@ramesesinc/lib": "
|
|
39
|
+
"@ramesesinc/app-forms": "*",
|
|
40
|
+
"@ramesesinc/client": "*",
|
|
41
|
+
"@ramesesinc/lib": "*",
|
|
42
42
|
"@types/crypto-js": "^4.2.2",
|
|
43
43
|
"@types/js-cookie": "^3.0.6",
|
|
44
44
|
"@types/node": "^20",
|