@ramesesinc/platform-core 0.1.6 → 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/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 +7 -7
- package/dist/core/PageContext.js +17 -7
- package/dist/core/PageViewContext.d.ts +13 -1
- package/dist/core/PageViewContext.js +75 -2
- 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/UpdateData.tsx +0 -49
- package/dist/components/action/UpdateState.tsx +0 -40
- 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
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { EventHandler } from ".";
|
|
2
|
+
import { ChangeEventHandler, EventHandler, VoidCallback } from ".";
|
|
3
3
|
export type PageOptions = {
|
|
4
4
|
mode?: "window" | "popup";
|
|
5
5
|
[key: string]: any;
|
|
6
6
|
};
|
|
7
|
+
export type PageViewUI = {
|
|
8
|
+
onChange: (handler: ChangeEventHandler) => VoidCallback;
|
|
9
|
+
notifyChange: (name: string | null, value: any) => void;
|
|
10
|
+
set: (name: string, value: any, autoPublish?: boolean) => void;
|
|
11
|
+
get: (name: string | null) => any;
|
|
12
|
+
getTitle: () => string | null;
|
|
13
|
+
setTitle: (value: string | null) => void;
|
|
14
|
+
};
|
|
7
15
|
export type PageViewContextValue = {
|
|
8
16
|
uuid: string;
|
|
9
17
|
parentContext: PageViewContextValue | null;
|
|
10
18
|
stack: PageViewContextValue[];
|
|
11
19
|
paths: string[];
|
|
20
|
+
getInfo: () => Record<string, any>;
|
|
21
|
+
getUI: () => PageViewUI;
|
|
22
|
+
getTitle: () => string | null;
|
|
23
|
+
setTitle: (title: string | null) => void;
|
|
12
24
|
setPage: (page: string | null, options?: PageOptions) => void;
|
|
13
25
|
setSelectedPage: (page: string | null) => void;
|
|
14
26
|
getSelectedPage: () => string | null;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { useApp } from "./AppContext";
|
|
5
4
|
/* ------------------ Global Stack ------------------ */
|
|
6
5
|
const contextStack = [];
|
|
7
6
|
/* ------------------ Context ------------------ */
|
|
@@ -10,6 +9,10 @@ const PageViewContext = createContext({
|
|
|
10
9
|
parentContext: null,
|
|
11
10
|
stack: [],
|
|
12
11
|
paths: [],
|
|
12
|
+
getInfo: () => ({}),
|
|
13
|
+
getUI: () => ({}),
|
|
14
|
+
getTitle: () => null,
|
|
15
|
+
setTitle: () => { },
|
|
13
16
|
setPage: () => { },
|
|
14
17
|
getSelectedPage: () => null,
|
|
15
18
|
pushPage: () => { },
|
|
@@ -22,10 +25,59 @@ const PageViewContext = createContext({
|
|
|
22
25
|
getOriginalLocationInfo: () => null,
|
|
23
26
|
getEventHandler: () => null,
|
|
24
27
|
});
|
|
28
|
+
const usePageViewUI = () => {
|
|
29
|
+
const propsRef = useRef({});
|
|
30
|
+
const changeHandlersRef = useRef([]);
|
|
31
|
+
const onChange = (handler) => {
|
|
32
|
+
if (handler != null && changeHandlersRef.current.indexOf(handler) < 0) {
|
|
33
|
+
changeHandlersRef.current.push(handler);
|
|
34
|
+
}
|
|
35
|
+
return () => {
|
|
36
|
+
if (handler == null)
|
|
37
|
+
return;
|
|
38
|
+
const list = changeHandlersRef.current;
|
|
39
|
+
const index = list.indexOf(handler);
|
|
40
|
+
if (index >= 0)
|
|
41
|
+
list.splice(index, 1);
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
const notifyChange = (name, value) => {
|
|
45
|
+
const e = { name, value };
|
|
46
|
+
const list = [...changeHandlersRef.current];
|
|
47
|
+
list.forEach((handler) => {
|
|
48
|
+
try {
|
|
49
|
+
handler(e);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
// do nothing: let the handler handle its own error
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const set = (name, value, autoPublish) => {
|
|
57
|
+
if (name == null)
|
|
58
|
+
return;
|
|
59
|
+
propsRef.current[name] = value;
|
|
60
|
+
if (String(autoPublish) === 'true') {
|
|
61
|
+
notifyChange(name, value);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const get = (name) => {
|
|
65
|
+
var _a;
|
|
66
|
+
const value = name == null ? null : (_a = propsRef.current[name]) !== null && _a !== void 0 ? _a : null;
|
|
67
|
+
return value;
|
|
68
|
+
};
|
|
69
|
+
const getTitle = () => {
|
|
70
|
+
return get("title");
|
|
71
|
+
};
|
|
72
|
+
const setTitle = (value) => {
|
|
73
|
+
set("title", value, false);
|
|
74
|
+
};
|
|
75
|
+
const result = { onChange, notifyChange, set, get, getTitle, setTitle };
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
25
78
|
export const PageViewProvider = ({ paths, handle, children, prefix = "page", eventHandler = {}, }) => {
|
|
26
79
|
const parentContext = useContext(PageViewContext);
|
|
27
80
|
const [uuid] = useState(() => `${prefix}-${Math.random().toString(36).slice(2)}`);
|
|
28
|
-
const app = useApp();
|
|
29
81
|
const [hasSelectionHandling, setSelectionHandling] = useState(false);
|
|
30
82
|
const isStandalone = useCallback(() => {
|
|
31
83
|
return handle == null ? true : handle.isStandalone();
|
|
@@ -111,6 +163,7 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
|
|
|
111
163
|
}
|
|
112
164
|
return result;
|
|
113
165
|
});
|
|
166
|
+
const titleRef = useRef(null);
|
|
114
167
|
const handleRef = useRef(handle);
|
|
115
168
|
useLayoutEffect(() => {
|
|
116
169
|
handleRef.current = handle;
|
|
@@ -143,6 +196,7 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
|
|
|
143
196
|
const { current: currentChain, previous: previousChain } = pageChain;
|
|
144
197
|
const newChainInfo = { path: "", base: "", page: "", options: pageOptions };
|
|
145
198
|
const newPageChain = { current: newChainInfo, previous: null };
|
|
199
|
+
// console.log("setPage mode => ", { page, pageOptions });
|
|
146
200
|
if (mode != null && mode === "window") {
|
|
147
201
|
newChainInfo.page = page;
|
|
148
202
|
newChainInfo.base = locInfo.root;
|
|
@@ -156,6 +210,7 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
|
|
|
156
210
|
}
|
|
157
211
|
setPageChain((prev) => (Object.assign({}, newPageChain)));
|
|
158
212
|
if (!standalone) {
|
|
213
|
+
// console.log("setPage push state => ", { newChainInfo });
|
|
159
214
|
history.pushState(null, "", newChainInfo.path);
|
|
160
215
|
}
|
|
161
216
|
(_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.renderPage(page);
|
|
@@ -241,11 +296,29 @@ export const PageViewProvider = ({ paths, handle, children, prefix = "page", eve
|
|
|
241
296
|
const { path } = previous.current;
|
|
242
297
|
return path != null && path !== "";
|
|
243
298
|
};
|
|
299
|
+
const getInfo = () => {
|
|
300
|
+
var _a;
|
|
301
|
+
return {
|
|
302
|
+
uuid,
|
|
303
|
+
prefix,
|
|
304
|
+
isStandalone: isStandalone(),
|
|
305
|
+
hasBackPage: hasBackPage(),
|
|
306
|
+
current: pageChain.current,
|
|
307
|
+
previous: (_a = pageChain.previous) === null || _a === void 0 ? void 0 : _a.current,
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
const ui = usePageViewUI();
|
|
244
311
|
const values = {
|
|
245
312
|
parentContext,
|
|
246
313
|
uuid,
|
|
247
314
|
stack: [...((parentContext === null || parentContext === void 0 ? void 0 : parentContext.stack) || [])],
|
|
248
315
|
paths,
|
|
316
|
+
getInfo,
|
|
317
|
+
getUI: () => ui,
|
|
318
|
+
getTitle: () => titleRef.current,
|
|
319
|
+
setTitle: (title) => {
|
|
320
|
+
titleRef.current = title;
|
|
321
|
+
},
|
|
249
322
|
setPage,
|
|
250
323
|
setSelectedPage,
|
|
251
324
|
getSelectedPage: () => {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
export type PopupOptions = {
|
|
3
|
+
modal?: boolean;
|
|
4
|
+
draggable?: boolean;
|
|
5
|
+
showCloseButton?: boolean;
|
|
6
|
+
showTitle?: boolean;
|
|
7
|
+
minWidth?: number | string;
|
|
8
|
+
minHeight?: number | string;
|
|
9
|
+
width?: number | string;
|
|
10
|
+
height?: number | string;
|
|
11
|
+
className?: string;
|
|
12
|
+
title?: string | null;
|
|
13
|
+
};
|
|
14
|
+
export type PopupState = PopupOptions & {
|
|
15
|
+
id: string;
|
|
16
|
+
counter: number;
|
|
17
|
+
visible: boolean;
|
|
18
|
+
content: ReactNode | null;
|
|
19
|
+
options: PopupOptions;
|
|
20
|
+
};
|
|
21
|
+
export interface PopupInstance {
|
|
22
|
+
id: string;
|
|
23
|
+
setContent: (content: ReactNode) => void;
|
|
24
|
+
show: () => void;
|
|
25
|
+
hide: () => void;
|
|
26
|
+
close: () => void;
|
|
27
|
+
}
|
|
28
|
+
export interface PopupManager {
|
|
29
|
+
create: (options?: Partial<PopupOptions>) => PopupInstance;
|
|
30
|
+
closeAll: () => void;
|
|
31
|
+
}
|
|
32
|
+
declare const popupMgr: PopupManager;
|
|
33
|
+
export declare function PopupManagerSlot(): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
export declare function PopupManagerProvider({ children }: {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
export declare function usePopupManager(): PopupManager;
|
|
38
|
+
export type PopupSlotContextValue = {
|
|
39
|
+
getId: () => string;
|
|
40
|
+
close: () => void;
|
|
41
|
+
setTitle: (title: string | null) => void;
|
|
42
|
+
};
|
|
43
|
+
export declare function usePopupContext(): PopupSlotContextValue | null;
|
|
44
|
+
type PopupSlotProviderProps = {
|
|
45
|
+
data: PopupState;
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
export declare function PopupSlotProvider(props: PopupSlotProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
export { popupMgr };
|
|
@@ -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
|
+
};
|