@vuu-ui/vuu-data-react 3.0.0-beta.3 → 3.1.0-beta.1
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/package.json +24 -20
- package/src/data-editing/EditForm.css.js +53 -0
- package/src/data-editing/EditForm.mjs +82 -0
- package/src/data-editing/UnsavedChangesReport.css.js +27 -0
- package/src/data-editing/UnsavedChangesReport.mjs +41 -0
- package/src/data-editing/edit-rule-validation-checker.mjs +30 -0
- package/src/data-editing/edit-validation-rules.mjs +33 -0
- package/src/data-editing/form-edit-state.mjs +16 -0
- package/src/data-editing/get-data-item-edit-control.mjs +63 -0
- package/src/data-editing/index.mjs +6 -0
- package/src/data-editing/useEditForm.mjs +253 -0
- package/src/datasource-provider/VuuDataSourceProvider.mjs +20 -0
- package/src/datasource-provider/index.mjs +1 -0
- package/src/datasource-provider/useAutoLoginToVuuServer.mjs +42 -0
- package/src/hooks/useLookupValues.mjs +93 -0
- package/src/hooks/useSessionDataSource.mjs +37 -0
- package/src/hooks/useTypeaheadSuggestions.mjs +50 -0
- package/src/hooks/useVisualLinks.mjs +87 -0
- package/src/hooks/useVuuMenuActions.mjs +269 -0
- package/src/hooks/useVuuTables.mjs +32 -0
- package/src/index.mjs +10 -0
- package/src/lost-connection-indicator/LostConnectionIndicator.css.js +29 -0
- package/src/lost-connection-indicator/LostConnectionIndicator.mjs +30 -0
- package/src/lost-connection-indicator/useLostConnection.mjs +28 -0
- package/src/session-editing-form/SessionEditingForm.css.js +47 -0
- package/src/session-editing-form/SessionEditingForm.mjs +236 -0
- package/src/session-editing-form/index.mjs +1 -0
- package/cjs/data-editing/EditForm.css.js +0 -6
- package/cjs/data-editing/EditForm.css.js.map +0 -1
- package/cjs/data-editing/EditForm.js +0 -90
- package/cjs/data-editing/EditForm.js.map +0 -1
- package/cjs/data-editing/UnsavedChangesReport.css.js +0 -6
- package/cjs/data-editing/UnsavedChangesReport.css.js.map +0 -1
- package/cjs/data-editing/UnsavedChangesReport.js +0 -29
- package/cjs/data-editing/UnsavedChangesReport.js.map +0 -1
- package/cjs/data-editing/edit-rule-validation-checker.js +0 -41
- package/cjs/data-editing/edit-rule-validation-checker.js.map +0 -1
- package/cjs/data-editing/edit-validation-rules.js +0 -52
- package/cjs/data-editing/edit-validation-rules.js.map +0 -1
- package/cjs/data-editing/form-edit-state.js +0 -26
- package/cjs/data-editing/form-edit-state.js.map +0 -1
- package/cjs/data-editing/get-data-item-edit-control.js +0 -106
- package/cjs/data-editing/get-data-item-edit-control.js.map +0 -1
- package/cjs/data-editing/useEditForm.js +0 -258
- package/cjs/data-editing/useEditForm.js.map +0 -1
- package/cjs/datasource-provider/VuuDataSourceProvider.js +0 -34
- package/cjs/datasource-provider/VuuDataSourceProvider.js.map +0 -1
- package/cjs/datasource-provider/useAutoLoginToVuuServer.js +0 -52
- package/cjs/datasource-provider/useAutoLoginToVuuServer.js.map +0 -1
- package/cjs/hooks/useLookupValues.js +0 -100
- package/cjs/hooks/useLookupValues.js.map +0 -1
- package/cjs/hooks/useSessionDataSource.js +0 -58
- package/cjs/hooks/useSessionDataSource.js.map +0 -1
- package/cjs/hooks/useTypeaheadSuggestions.js +0 -57
- package/cjs/hooks/useTypeaheadSuggestions.js.map +0 -1
- package/cjs/hooks/useVisualLinks.js +0 -84
- package/cjs/hooks/useVisualLinks.js.map +0 -1
- package/cjs/hooks/useVuuMenuActions.js +0 -352
- package/cjs/hooks/useVuuMenuActions.js.map +0 -1
- package/cjs/hooks/useVuuTables.js +0 -38
- package/cjs/hooks/useVuuTables.js.map +0 -1
- package/cjs/index.js +0 -42
- package/cjs/index.js.map +0 -1
- package/cjs/lost-connection-indicator/LostConnectionIndicator.css.js +0 -6
- package/cjs/lost-connection-indicator/LostConnectionIndicator.css.js.map +0 -1
- package/cjs/lost-connection-indicator/LostConnectionIndicator.js +0 -24
- package/cjs/lost-connection-indicator/LostConnectionIndicator.js.map +0 -1
- package/cjs/lost-connection-indicator/useLostConnection.js +0 -34
- package/cjs/lost-connection-indicator/useLostConnection.js.map +0 -1
- package/cjs/session-editing-form/SessionEditingForm.css.js +0 -6
- package/cjs/session-editing-form/SessionEditingForm.css.js.map +0 -1
- package/cjs/session-editing-form/SessionEditingForm.js +0 -270
- package/cjs/session-editing-form/SessionEditingForm.js.map +0 -1
- package/esm/data-editing/EditForm.css.js +0 -4
- package/esm/data-editing/EditForm.css.js.map +0 -1
- package/esm/data-editing/EditForm.js +0 -88
- package/esm/data-editing/EditForm.js.map +0 -1
- package/esm/data-editing/UnsavedChangesReport.css.js +0 -4
- package/esm/data-editing/UnsavedChangesReport.css.js.map +0 -1
- package/esm/data-editing/UnsavedChangesReport.js +0 -27
- package/esm/data-editing/UnsavedChangesReport.js.map +0 -1
- package/esm/data-editing/edit-rule-validation-checker.js +0 -37
- package/esm/data-editing/edit-rule-validation-checker.js.map +0 -1
- package/esm/data-editing/edit-validation-rules.js +0 -50
- package/esm/data-editing/edit-validation-rules.js.map +0 -1
- package/esm/data-editing/form-edit-state.js +0 -23
- package/esm/data-editing/form-edit-state.js.map +0 -1
- package/esm/data-editing/get-data-item-edit-control.js +0 -104
- package/esm/data-editing/get-data-item-edit-control.js.map +0 -1
- package/esm/data-editing/useEditForm.js +0 -256
- package/esm/data-editing/useEditForm.js.map +0 -1
- package/esm/datasource-provider/VuuDataSourceProvider.js +0 -32
- package/esm/datasource-provider/VuuDataSourceProvider.js.map +0 -1
- package/esm/datasource-provider/useAutoLoginToVuuServer.js +0 -50
- package/esm/datasource-provider/useAutoLoginToVuuServer.js.map +0 -1
- package/esm/hooks/useLookupValues.js +0 -98
- package/esm/hooks/useLookupValues.js.map +0 -1
- package/esm/hooks/useSessionDataSource.js +0 -56
- package/esm/hooks/useSessionDataSource.js.map +0 -1
- package/esm/hooks/useTypeaheadSuggestions.js +0 -54
- package/esm/hooks/useTypeaheadSuggestions.js.map +0 -1
- package/esm/hooks/useVisualLinks.js +0 -82
- package/esm/hooks/useVisualLinks.js.map +0 -1
- package/esm/hooks/useVuuMenuActions.js +0 -348
- package/esm/hooks/useVuuMenuActions.js.map +0 -1
- package/esm/hooks/useVuuTables.js +0 -36
- package/esm/hooks/useVuuTables.js.map +0 -1
- package/esm/index.js +0 -16
- package/esm/index.js.map +0 -1
- package/esm/lost-connection-indicator/LostConnectionIndicator.css.js +0 -4
- package/esm/lost-connection-indicator/LostConnectionIndicator.css.js.map +0 -1
- package/esm/lost-connection-indicator/LostConnectionIndicator.js +0 -22
- package/esm/lost-connection-indicator/LostConnectionIndicator.js.map +0 -1
- package/esm/lost-connection-indicator/useLostConnection.js +0 -32
- package/esm/lost-connection-indicator/useLostConnection.js.map +0 -1
- package/esm/session-editing-form/SessionEditingForm.css.js +0 -4
- package/esm/session-editing-form/SessionEditingForm.css.js.map +0 -1
- package/esm/session-editing-form/SessionEditingForm.js +0 -268
- package/esm/session-editing-form/SessionEditingForm.js.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ConnectionManager, VuuDataSource } from "@vuu-ui/vuu-data-remote";
|
|
3
|
+
import { DataProvider } from "@vuu-ui/vuu-utils";
|
|
4
|
+
import { useAutoLoginToVuuServer } from "./useAutoLoginToVuuServer.mjs";
|
|
5
|
+
const getServerAPI = ()=>ConnectionManager.serverAPI;
|
|
6
|
+
const VuuDataSourceProvider = ({ authenticate, autoConnect = false, autoLogin = false, children, websocketUrl })=>{
|
|
7
|
+
useAutoLoginToVuuServer({
|
|
8
|
+
authenticate,
|
|
9
|
+
autoConnect,
|
|
10
|
+
autoLogin,
|
|
11
|
+
websocketUrl
|
|
12
|
+
});
|
|
13
|
+
return /*#__PURE__*/ jsx(DataProvider, {
|
|
14
|
+
VuuDataSource: VuuDataSource,
|
|
15
|
+
getServerAPI: getServerAPI,
|
|
16
|
+
isLocalData: false,
|
|
17
|
+
children: children
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
export { VuuDataSourceProvider };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./VuuDataSourceProvider.mjs";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ConnectionManager, authenticate as vuu_data_remote_authenticate } from "@vuu-ui/vuu-data-remote";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
const useAutoLoginToVuuServer = ({ authenticate = true, autoConnect = true, secure = true, token: tokenProp = "no-token", user = "steve", websocketUrl } = {})=>{
|
|
5
|
+
const [errorMessage, setErrorMessage] = useState("");
|
|
6
|
+
useEffect(()=>{
|
|
7
|
+
const connect = async ()=>{
|
|
8
|
+
try {
|
|
9
|
+
let token = tokenProp;
|
|
10
|
+
if (authenticate) {
|
|
11
|
+
const response = await vuu_data_remote_authenticate(user, "xyz", "/api/authn");
|
|
12
|
+
token = response.token;
|
|
13
|
+
}
|
|
14
|
+
const url = websocketUrl ?? `${secure ? "wss" : "ws"}://localhost/8090/websocket`;
|
|
15
|
+
ConnectionManager.connect({
|
|
16
|
+
url,
|
|
17
|
+
token
|
|
18
|
+
});
|
|
19
|
+
} catch (e) {
|
|
20
|
+
if (e instanceof Error) {
|
|
21
|
+
console.error(e.message);
|
|
22
|
+
setErrorMessage(e.message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
if (autoConnect) connect();
|
|
27
|
+
return ()=>{
|
|
28
|
+
if (autoConnect) ConnectionManager.disconnect();
|
|
29
|
+
};
|
|
30
|
+
}, [
|
|
31
|
+
authenticate,
|
|
32
|
+
autoConnect,
|
|
33
|
+
secure,
|
|
34
|
+
tokenProp,
|
|
35
|
+
user,
|
|
36
|
+
websocketUrl
|
|
37
|
+
]);
|
|
38
|
+
if (errorMessage) return /*#__PURE__*/ jsx("p", {
|
|
39
|
+
children: "Unable to authenticate against Vuu Server A Vuu Server instance must be running to show this example."
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
export { useAutoLoginToVuuServer };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { VuuDataSource } from "@vuu-ui/vuu-data-remote";
|
|
2
|
+
import { Range, buildColumnMap, getSelectedOption, isLookupRenderer, isTypeDescriptor, isValueListRenderer, useShellContext } from "@vuu-ui/vuu-utils";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
const NO_VALUES = [];
|
|
5
|
+
const toListOption = (value)=>({
|
|
6
|
+
label: value,
|
|
7
|
+
value
|
|
8
|
+
});
|
|
9
|
+
const lookupValueMap = new Map();
|
|
10
|
+
const loadLookupValues = ({ labelColumn, table, valueColumn })=>{
|
|
11
|
+
const tableKey = `${table.module}:${table.table}`;
|
|
12
|
+
const lookupValues = lookupValueMap.get(tableKey);
|
|
13
|
+
if (lookupValues) return lookupValues;
|
|
14
|
+
{
|
|
15
|
+
const promise = new Promise((resolve)=>{
|
|
16
|
+
const columns = [
|
|
17
|
+
valueColumn,
|
|
18
|
+
labelColumn
|
|
19
|
+
];
|
|
20
|
+
const columnMap = buildColumnMap(columns);
|
|
21
|
+
const dataSource = new VuuDataSource({
|
|
22
|
+
bufferSize: 0,
|
|
23
|
+
table
|
|
24
|
+
});
|
|
25
|
+
dataSource.subscribe({
|
|
26
|
+
columns,
|
|
27
|
+
range: Range(0, 100)
|
|
28
|
+
}, (message)=>{
|
|
29
|
+
if ("viewport-update" === message.type) {
|
|
30
|
+
if (message.rows) {
|
|
31
|
+
const listOptions = message.rows.map((row)=>({
|
|
32
|
+
value: row[columnMap[valueColumn]],
|
|
33
|
+
label: row[columnMap[labelColumn]]
|
|
34
|
+
}));
|
|
35
|
+
resolve(listOptions);
|
|
36
|
+
dataSource.unsubscribe();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
lookupValueMap.set(tableKey, promise);
|
|
42
|
+
return promise;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const getLookupDetails = ({ name, type })=>{
|
|
46
|
+
if (isTypeDescriptor(type) && isLookupRenderer(type.renderer)) return type.renderer.lookup;
|
|
47
|
+
throw Error(`useLookupValues column ${name} is not configured to use lookup values`);
|
|
48
|
+
};
|
|
49
|
+
const useLookupValues = (column, initialValueProp)=>{
|
|
50
|
+
const { type: columnType } = column;
|
|
51
|
+
const { getLookupValues } = useShellContext();
|
|
52
|
+
const initialState = useMemo(()=>{
|
|
53
|
+
if (isTypeDescriptor(columnType) && isValueListRenderer(columnType?.renderer)) {
|
|
54
|
+
const values = columnType.renderer.values.map(toListOption);
|
|
55
|
+
return {
|
|
56
|
+
initialValue: getSelectedOption(values, initialValueProp) ?? null,
|
|
57
|
+
values
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
{
|
|
61
|
+
const lookupDetails = getLookupDetails(column);
|
|
62
|
+
const values = getLookupValues?.(lookupDetails.table) ?? NO_VALUES;
|
|
63
|
+
return {
|
|
64
|
+
initialValue: getSelectedOption(values, initialValueProp) ?? null,
|
|
65
|
+
values
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}, [
|
|
69
|
+
column,
|
|
70
|
+
columnType,
|
|
71
|
+
getLookupValues,
|
|
72
|
+
initialValueProp
|
|
73
|
+
]);
|
|
74
|
+
const [{ initialValue, values }, setLookupState] = useState(initialState);
|
|
75
|
+
useMemo(()=>{
|
|
76
|
+
if (values === NO_VALUES) {
|
|
77
|
+
const lookupDetails = getLookupDetails(column);
|
|
78
|
+
loadLookupValues(lookupDetails).then((values)=>setLookupState({
|
|
79
|
+
initialValue: getSelectedOption(values, initialValueProp) ?? null,
|
|
80
|
+
values
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
}, [
|
|
84
|
+
values,
|
|
85
|
+
column,
|
|
86
|
+
initialValueProp
|
|
87
|
+
]);
|
|
88
|
+
return {
|
|
89
|
+
initialValue,
|
|
90
|
+
values
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
export { useLookupValues };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useData } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useCallback, useRef } from "react";
|
|
3
|
+
const sessionState = new Map();
|
|
4
|
+
const useSessionDataSource = (props)=>{
|
|
5
|
+
const { VuuDataSource } = useData();
|
|
6
|
+
const dataSourceConfigRef = useRef(void 0);
|
|
7
|
+
const getDataSource = useCallback((sessionKey, dataSourceConstructorProps)=>{
|
|
8
|
+
let ds = sessionState.get(sessionKey);
|
|
9
|
+
if (ds) return ds;
|
|
10
|
+
if (dataSourceConstructorProps?.columns) {
|
|
11
|
+
ds = new VuuDataSource({
|
|
12
|
+
...dataSourceConstructorProps,
|
|
13
|
+
viewport: sessionKey,
|
|
14
|
+
...dataSourceConfigRef.current
|
|
15
|
+
});
|
|
16
|
+
if (props?.onConfigChange) ds.on("config", props.onConfigChange);
|
|
17
|
+
sessionState.set(sessionKey, ds);
|
|
18
|
+
return ds;
|
|
19
|
+
}
|
|
20
|
+
throw Error("[useSessionDataSource] unable to create new DataSource, columns have not been defined ");
|
|
21
|
+
}, [
|
|
22
|
+
VuuDataSource,
|
|
23
|
+
props?.onConfigChange
|
|
24
|
+
]);
|
|
25
|
+
const clearDataSource = useCallback((sessionKey, unsubscribe)=>{
|
|
26
|
+
const ds = sessionState.get(sessionKey);
|
|
27
|
+
if (ds) {
|
|
28
|
+
sessionState.delete(sessionKey);
|
|
29
|
+
if (true === unsubscribe) ds.unsubscribe();
|
|
30
|
+
} else console.warn(`[useSessionbDatasource] clearDataSource ${sessionKey} not found `);
|
|
31
|
+
}, []);
|
|
32
|
+
return {
|
|
33
|
+
clearDataSource,
|
|
34
|
+
getDataSource
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export { useSessionDataSource };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useDataSource } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
const getTypeaheadParams = (table, column, text = "", selectedValues = [])=>{
|
|
4
|
+
if ("" !== text && !selectedValues.includes(text.toLowerCase())) return [
|
|
5
|
+
table,
|
|
6
|
+
column,
|
|
7
|
+
text
|
|
8
|
+
];
|
|
9
|
+
return [
|
|
10
|
+
table,
|
|
11
|
+
column
|
|
12
|
+
];
|
|
13
|
+
};
|
|
14
|
+
const useTypeaheadSuggestions = ()=>{
|
|
15
|
+
const dataSource = useDataSource(false);
|
|
16
|
+
return useCallback(async ([{ module, table }, column, starts])=>{
|
|
17
|
+
if (void 0 === dataSource) {
|
|
18
|
+
console.warn("[useTypeaheadSuggestions] no dataSource provided (use DataSourceProvider)");
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const rpcMessage = void 0 === starts ? {
|
|
22
|
+
params: {
|
|
23
|
+
column,
|
|
24
|
+
module,
|
|
25
|
+
table
|
|
26
|
+
},
|
|
27
|
+
rpcName: "getUniqueFieldValues",
|
|
28
|
+
type: "RPC_REQUEST"
|
|
29
|
+
} : {
|
|
30
|
+
params: {
|
|
31
|
+
column,
|
|
32
|
+
module,
|
|
33
|
+
starts,
|
|
34
|
+
table
|
|
35
|
+
},
|
|
36
|
+
rpcName: "getUniqueFieldValuesStartingWith",
|
|
37
|
+
type: "RPC_REQUEST"
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
const response = await dataSource.rpcRequest?.(rpcMessage);
|
|
41
|
+
if (response?.type === "SUCCESS_RESULT") return response.data;
|
|
42
|
+
return false;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}, [
|
|
47
|
+
dataSource
|
|
48
|
+
]);
|
|
49
|
+
};
|
|
50
|
+
export { getTypeaheadParams, useTypeaheadSuggestions };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useViewContext } from "@vuu-ui/vuu-layout";
|
|
3
|
+
import { IconButton } from "@vuu-ui/vuu-ui-controls";
|
|
4
|
+
import { useCallback, useEffect } from "react";
|
|
5
|
+
const useVisualLinks = (dataSource)=>{
|
|
6
|
+
const { dispatch } = useViewContext();
|
|
7
|
+
const clearVisualLinkTarget = useCallback(()=>{
|
|
8
|
+
if (dataSource.visualLink) dispatch?.({
|
|
9
|
+
type: "broadcast-message",
|
|
10
|
+
message: {
|
|
11
|
+
targetId: dataSource.visualLink.parentClientVpId,
|
|
12
|
+
type: "highlight-off"
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}, [
|
|
16
|
+
dataSource,
|
|
17
|
+
dispatch
|
|
18
|
+
]);
|
|
19
|
+
const removeVisualLink = useCallback(()=>{
|
|
20
|
+
if (dataSource.visualLink) {
|
|
21
|
+
dispatch?.({
|
|
22
|
+
type: "broadcast-message",
|
|
23
|
+
message: {
|
|
24
|
+
targetId: dataSource.visualLink.parentClientVpId,
|
|
25
|
+
type: "highlight-off"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
dataSource.visualLink = void 0;
|
|
29
|
+
}
|
|
30
|
+
}, [
|
|
31
|
+
dataSource,
|
|
32
|
+
dispatch
|
|
33
|
+
]);
|
|
34
|
+
const handleLinkRemoved = useCallback(()=>{
|
|
35
|
+
dispatch?.({
|
|
36
|
+
type: "remove-toolbar-contribution",
|
|
37
|
+
location: "post-title"
|
|
38
|
+
});
|
|
39
|
+
}, [
|
|
40
|
+
dispatch
|
|
41
|
+
]);
|
|
42
|
+
const highlightVisualLinkTarget = useCallback(()=>{
|
|
43
|
+
if (dataSource.visualLink) dispatch?.({
|
|
44
|
+
type: "broadcast-message",
|
|
45
|
+
message: {
|
|
46
|
+
targetId: dataSource.visualLink.parentClientVpId,
|
|
47
|
+
type: "highlight-on"
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}, [
|
|
51
|
+
dataSource,
|
|
52
|
+
dispatch
|
|
53
|
+
]);
|
|
54
|
+
const handleLinkCreated = useCallback(()=>{
|
|
55
|
+
dispatch?.({
|
|
56
|
+
type: "add-toolbar-contribution",
|
|
57
|
+
location: "post-title",
|
|
58
|
+
content: /*#__PURE__*/ jsx(IconButton, {
|
|
59
|
+
appearance: "transparent",
|
|
60
|
+
"aria-label": "remove-link",
|
|
61
|
+
icon: "link",
|
|
62
|
+
onClick: removeVisualLink,
|
|
63
|
+
onMouseEnter: highlightVisualLinkTarget,
|
|
64
|
+
onMouseLeave: clearVisualLinkTarget,
|
|
65
|
+
sentiment: "neutral"
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
}, [
|
|
69
|
+
dispatch,
|
|
70
|
+
removeVisualLink,
|
|
71
|
+
highlightVisualLinkTarget,
|
|
72
|
+
clearVisualLinkTarget
|
|
73
|
+
]);
|
|
74
|
+
useEffect(()=>{
|
|
75
|
+
dataSource.on("visual-link-created", handleLinkCreated);
|
|
76
|
+
dataSource.on("visual-link-removed", handleLinkRemoved);
|
|
77
|
+
return ()=>{
|
|
78
|
+
dataSource.removeListener("visual-link-created", handleLinkCreated);
|
|
79
|
+
dataSource.removeListener("visual-link-removed", handleLinkRemoved);
|
|
80
|
+
};
|
|
81
|
+
}, [
|
|
82
|
+
dataSource,
|
|
83
|
+
handleLinkCreated,
|
|
84
|
+
handleLinkRemoved
|
|
85
|
+
]);
|
|
86
|
+
};
|
|
87
|
+
export { useVisualLinks };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "@salt-ds/core";
|
|
3
|
+
import { hasShowNotificationAction, isGroupMenuItemDescriptor } from "@vuu-ui/vuu-context-menu";
|
|
4
|
+
import { getFilterPredicate } from "@vuu-ui/vuu-filter-parser";
|
|
5
|
+
import { NotificationType, useNotifications } from "@vuu-ui/vuu-notifications";
|
|
6
|
+
import { BulkEditPanel, isTableLocation } from "@vuu-ui/vuu-table";
|
|
7
|
+
import { getLayoutComponent, isActionMessage, isCustomComponentActionMessage, isSessionTableActionMessage, useData } from "@vuu-ui/vuu-utils";
|
|
8
|
+
import { useCallback } from "react";
|
|
9
|
+
import { SessionEditingForm } from "../session-editing-form/index.mjs";
|
|
10
|
+
import { useModal } from "@vuu-ui/vuu-ui-controls";
|
|
11
|
+
const isRoot = (menu)=>"ROOT" === menu.name;
|
|
12
|
+
const isCellMenu = (options)=>"cell" === options.context;
|
|
13
|
+
const isRowMenu = (options)=>"row" === options.context;
|
|
14
|
+
const isSelectionMenu = (options)=>"selected-rows" === options.context;
|
|
15
|
+
const isVuuMenuItem = (menu)=>"rpcName" in menu;
|
|
16
|
+
const isGroupMenuItem = (menu)=>"menus" in menu;
|
|
17
|
+
const hasFilter = ({ filter })=>"string" == typeof filter && filter.length > 0;
|
|
18
|
+
const getMenuItemOptions = (menu, options)=>{
|
|
19
|
+
switch(menu.context){
|
|
20
|
+
case "cell":
|
|
21
|
+
return {
|
|
22
|
+
...menu,
|
|
23
|
+
field: options.column.name,
|
|
24
|
+
rowKey: options.dataRow.key,
|
|
25
|
+
value: options.dataRow[options.column.name]
|
|
26
|
+
};
|
|
27
|
+
case "row":
|
|
28
|
+
return {
|
|
29
|
+
...menu,
|
|
30
|
+
columns: options.columns,
|
|
31
|
+
row: options.dataRow,
|
|
32
|
+
rowKey: options.dataRow.key
|
|
33
|
+
};
|
|
34
|
+
case "selected-rows":
|
|
35
|
+
return {
|
|
36
|
+
...menu,
|
|
37
|
+
columns: options.columns
|
|
38
|
+
};
|
|
39
|
+
default:
|
|
40
|
+
return menu;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const vuuContextCompatibleWithTableLocation = (uiLocation, vuuContext, selectedRowCount = 0)=>{
|
|
44
|
+
switch(uiLocation){
|
|
45
|
+
case "grid":
|
|
46
|
+
if ("selected-rows" === vuuContext) return selectedRowCount > 0;
|
|
47
|
+
return true;
|
|
48
|
+
case "header":
|
|
49
|
+
return "grid" === vuuContext;
|
|
50
|
+
default:
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const gridRowMeetsFilterCriteria = (context, dataRow, selectedRows, filter)=>{
|
|
55
|
+
if ("cell" === context || "row" === context) {
|
|
56
|
+
const filterPredicate = getFilterPredicate(filter);
|
|
57
|
+
return filterPredicate(dataRow);
|
|
58
|
+
}
|
|
59
|
+
if ("selected-rows" === context) if (0 === selectedRows.length) return false;
|
|
60
|
+
else {
|
|
61
|
+
const filterPredicate = getFilterPredicate(filter);
|
|
62
|
+
return selectedRows.every(filterPredicate);
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
};
|
|
66
|
+
const menuShouldBeRenderedInThisContext = (menuItem, tableLocation, options)=>{
|
|
67
|
+
if (isGroupMenuItem(menuItem)) return menuItem.menus.some((childMenu)=>menuShouldBeRenderedInThisContext(childMenu, tableLocation, options));
|
|
68
|
+
if (!vuuContextCompatibleWithTableLocation(tableLocation, menuItem.context, options.selectedRows?.length)) return false;
|
|
69
|
+
if ("grid" === tableLocation && hasFilter(menuItem)) return gridRowMeetsFilterCriteria(menuItem.context, options.dataRow, options.selectedRows, menuItem.filter);
|
|
70
|
+
if (isCellMenu(menuItem) && "*" !== menuItem.field) return menuItem.field === options.column.name;
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
const buildMenuDescriptorFromVuuMenu = (menu, tableLocation, options)=>{
|
|
74
|
+
if (menuShouldBeRenderedInThisContext(menu, tableLocation, options)) if (isVuuMenuItem(menu)) return {
|
|
75
|
+
label: menu.name,
|
|
76
|
+
id: "MENU_RPC_CALL",
|
|
77
|
+
options: getMenuItemOptions(menu, options)
|
|
78
|
+
};
|
|
79
|
+
else {
|
|
80
|
+
const children = menu.menus.map((childMenu)=>buildMenuDescriptorFromVuuMenu(childMenu, tableLocation, options)).filter((childMenu)=>void 0 !== childMenu);
|
|
81
|
+
if (children.length > 0) return {
|
|
82
|
+
label: menu.name,
|
|
83
|
+
children
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const keyFirst = (c1, c2)=>c1.isKeyField ? -1 : c2.isKeyField ? 1 : 0;
|
|
88
|
+
const defaultFormConfig = {
|
|
89
|
+
fields: [],
|
|
90
|
+
key: "",
|
|
91
|
+
title: ""
|
|
92
|
+
};
|
|
93
|
+
const configFromSchema = (schema)=>{
|
|
94
|
+
if (schema) {
|
|
95
|
+
const { columns, key } = schema;
|
|
96
|
+
return {
|
|
97
|
+
key,
|
|
98
|
+
title: "Parameters for command",
|
|
99
|
+
fields: columns.map((col)=>({
|
|
100
|
+
description: col.name,
|
|
101
|
+
label: col.name,
|
|
102
|
+
name: col.name,
|
|
103
|
+
type: col.serverDataType,
|
|
104
|
+
isKeyField: col.name === key
|
|
105
|
+
})).sort(keyFirst)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const getFormConfig = (action)=>{
|
|
110
|
+
const { tableSchema: schema } = action;
|
|
111
|
+
const config = configFromSchema(schema) ?? defaultFormConfig;
|
|
112
|
+
return {
|
|
113
|
+
config,
|
|
114
|
+
schema
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
const useVuuMenuActions = ({ clientSideMenuActionHandler, dataSource, onRpcResponse })=>{
|
|
118
|
+
const { VuuDataSource } = useData();
|
|
119
|
+
const menuBuilder = useCallback((location, options)=>{
|
|
120
|
+
const descriptors = [];
|
|
121
|
+
if (dataSource) {
|
|
122
|
+
const { links, menu } = dataSource;
|
|
123
|
+
const { visualLink } = dataSource;
|
|
124
|
+
if ("grid" === location && links && !visualLink) links.forEach((linkDescriptor)=>{
|
|
125
|
+
const { link, label: linkLabel } = linkDescriptor;
|
|
126
|
+
const label = linkLabel ? linkLabel : link.toTable;
|
|
127
|
+
descriptors.push({
|
|
128
|
+
label: `Link to ${label}`,
|
|
129
|
+
id: "link-table",
|
|
130
|
+
options: linkDescriptor
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
if (menu && isTableLocation(location)) {
|
|
134
|
+
const menuDescriptor = buildMenuDescriptorFromVuuMenu(menu, location, options);
|
|
135
|
+
if (isRoot(menu) && isGroupMenuItemDescriptor(menuDescriptor)) descriptors.push(...menuDescriptor.children);
|
|
136
|
+
else if (menuDescriptor) descriptors.push(menuDescriptor);
|
|
137
|
+
}
|
|
138
|
+
} else throw Error("useVuuMenuActions no dataSource provided");
|
|
139
|
+
return descriptors;
|
|
140
|
+
}, [
|
|
141
|
+
dataSource
|
|
142
|
+
]);
|
|
143
|
+
const { showDialog, closeDialog, showPrompt } = useModal();
|
|
144
|
+
const { showNotification } = useNotifications();
|
|
145
|
+
const showCustomComponentInDialog = useCallback((componentId, ds, table)=>{
|
|
146
|
+
const sessionDs = new VuuDataSource({
|
|
147
|
+
columns: ds.columns,
|
|
148
|
+
table,
|
|
149
|
+
viewport: table.table
|
|
150
|
+
});
|
|
151
|
+
const handleOpenChangePrompt = (open)=>{
|
|
152
|
+
if (!open) ds.unsubscribe();
|
|
153
|
+
};
|
|
154
|
+
const Component = getLayoutComponent(componentId);
|
|
155
|
+
showPrompt(/*#__PURE__*/ jsx(Component, {
|
|
156
|
+
dataSource: sessionDs
|
|
157
|
+
}), {
|
|
158
|
+
title: "Confirm operation on selected rows",
|
|
159
|
+
onOpenChange: handleOpenChangePrompt
|
|
160
|
+
});
|
|
161
|
+
}, [
|
|
162
|
+
VuuDataSource,
|
|
163
|
+
showPrompt
|
|
164
|
+
]);
|
|
165
|
+
const showSessionEditingForm = useCallback(async (ds, action)=>{
|
|
166
|
+
const { tableSchema } = action;
|
|
167
|
+
if (tableSchema) {
|
|
168
|
+
const formConfig = getFormConfig(action);
|
|
169
|
+
showDialog(/*#__PURE__*/ jsx(SessionEditingForm, {
|
|
170
|
+
...formConfig,
|
|
171
|
+
onClose: closeDialog
|
|
172
|
+
}), "Set Parameters");
|
|
173
|
+
}
|
|
174
|
+
const sessionDs = await ds.createSessionDataSource?.('All');
|
|
175
|
+
const handleSubmit = ()=>{
|
|
176
|
+
sessionDs?.rpcRequest?.({
|
|
177
|
+
params: {
|
|
178
|
+
save: true
|
|
179
|
+
},
|
|
180
|
+
rpcName: "endEditSession",
|
|
181
|
+
type: "RPC_REQUEST"
|
|
182
|
+
});
|
|
183
|
+
closeDialog();
|
|
184
|
+
};
|
|
185
|
+
if (sessionDs) showDialog(/*#__PURE__*/ jsx(BulkEditPanel, {
|
|
186
|
+
sessionDs: sessionDs,
|
|
187
|
+
onSubmit: handleSubmit,
|
|
188
|
+
parentDs: ds
|
|
189
|
+
}), "Multi Row Edit", [
|
|
190
|
+
/*#__PURE__*/ jsx(Button, {
|
|
191
|
+
onClick: closeDialog,
|
|
192
|
+
children: "Cancel"
|
|
193
|
+
}, "cancel"),
|
|
194
|
+
/*#__PURE__*/ jsx(Button, {
|
|
195
|
+
onClick: handleSubmit,
|
|
196
|
+
children: "Save"
|
|
197
|
+
}, "submit")
|
|
198
|
+
]);
|
|
199
|
+
}, [
|
|
200
|
+
closeDialog,
|
|
201
|
+
showDialog
|
|
202
|
+
]);
|
|
203
|
+
const getMenuRpcRequest = (options)=>{
|
|
204
|
+
const { rpcName } = options;
|
|
205
|
+
if (isCellMenu(options)) return {
|
|
206
|
+
field: options.field,
|
|
207
|
+
rowKey: options.rowKey,
|
|
208
|
+
rpcName,
|
|
209
|
+
value: options.value,
|
|
210
|
+
type: "VIEW_PORT_MENU_CELL_RPC"
|
|
211
|
+
};
|
|
212
|
+
if (isRowMenu(options)) return {
|
|
213
|
+
rowKey: options.rowKey,
|
|
214
|
+
row: options.row,
|
|
215
|
+
rpcName,
|
|
216
|
+
type: "VIEW_PORT_MENU_ROW_RPC"
|
|
217
|
+
};
|
|
218
|
+
if (isSelectionMenu(options)) return {
|
|
219
|
+
rpcName,
|
|
220
|
+
type: "VIEW_PORT_MENUS_SELECT_RPC"
|
|
221
|
+
};
|
|
222
|
+
return {
|
|
223
|
+
rpcName,
|
|
224
|
+
type: "VIEW_PORT_MENU_TABLE_RPC"
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
const menuActionHandler = useCallback((menuItemId, options)=>{
|
|
228
|
+
if (clientSideMenuActionHandler?.(menuItemId, options)) return true;
|
|
229
|
+
if ("MENU_RPC_CALL" === menuItemId) {
|
|
230
|
+
const rpcRequest = getMenuRpcRequest(options);
|
|
231
|
+
dataSource?.menuRpcCall(rpcRequest).then((rpcResponse)=>{
|
|
232
|
+
if (rpcResponse) {
|
|
233
|
+
if (onRpcResponse?.(rpcResponse) === true) return true;
|
|
234
|
+
if (isActionMessage(rpcResponse)) {
|
|
235
|
+
if (hasShowNotificationAction(rpcResponse)) {
|
|
236
|
+
const { action: { message, title = "Success" } } = rpcResponse;
|
|
237
|
+
showNotification({
|
|
238
|
+
status: "success",
|
|
239
|
+
content: message,
|
|
240
|
+
header: title,
|
|
241
|
+
type: NotificationType.Toast
|
|
242
|
+
});
|
|
243
|
+
} else if (isSessionTableActionMessage(rpcResponse)) showSessionEditingForm(dataSource, rpcResponse.action);
|
|
244
|
+
else if (isCustomComponentActionMessage(rpcResponse)) showCustomComponentInDialog(rpcResponse.action.renderComponent, dataSource, rpcResponse.action.table);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
if ("link-table" === menuItemId) {
|
|
251
|
+
if (dataSource) dataSource.visualLink = options;
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
console.log(`useViewServer handleMenuAction, can't handle action type ${menuItemId}`);
|
|
255
|
+
return false;
|
|
256
|
+
}, [
|
|
257
|
+
clientSideMenuActionHandler,
|
|
258
|
+
dataSource,
|
|
259
|
+
onRpcResponse,
|
|
260
|
+
showCustomComponentInDialog,
|
|
261
|
+
showNotification,
|
|
262
|
+
showSessionEditingForm
|
|
263
|
+
]);
|
|
264
|
+
return {
|
|
265
|
+
menuBuilder,
|
|
266
|
+
menuActionHandler
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
export { isRowMenu, isSelectionMenu, useVuuMenuActions };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useData } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
const useVuuTables = ()=>{
|
|
4
|
+
const [tableSchemas, setTableSchemas] = useState();
|
|
5
|
+
const { getServerAPI } = useData();
|
|
6
|
+
const buildTables = useCallback((schemas)=>{
|
|
7
|
+
const vuuTables = new Map();
|
|
8
|
+
schemas.forEach((schema)=>{
|
|
9
|
+
const { module, table } = schema.table;
|
|
10
|
+
vuuTables.set(`${module}:${table}`, schema);
|
|
11
|
+
});
|
|
12
|
+
return vuuTables;
|
|
13
|
+
}, []);
|
|
14
|
+
useEffect(()=>{
|
|
15
|
+
async function fetchTableMetadata() {
|
|
16
|
+
try {
|
|
17
|
+
const server = await getServerAPI();
|
|
18
|
+
const { tables } = await server.getTableList();
|
|
19
|
+
const tableSchemas = await Promise.all(tables.map((vuuTable)=>server.getTableSchema(vuuTable)));
|
|
20
|
+
setTableSchemas(tableSchemas);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.warn(`useVuuTables: error fetching table metadata ${String(err)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
fetchTableMetadata();
|
|
26
|
+
}, [
|
|
27
|
+
buildTables,
|
|
28
|
+
getServerAPI
|
|
29
|
+
]);
|
|
30
|
+
return tableSchemas;
|
|
31
|
+
};
|
|
32
|
+
export { useVuuTables };
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./data-editing/index.mjs";
|
|
2
|
+
export * from "./datasource-provider/index.mjs";
|
|
3
|
+
export * from "./session-editing-form/index.mjs";
|
|
4
|
+
export { useLookupValues } from "./hooks/useLookupValues.mjs";
|
|
5
|
+
export { useSessionDataSource } from "./hooks/useSessionDataSource.mjs";
|
|
6
|
+
export { getTypeaheadParams, useTypeaheadSuggestions } from "./hooks/useTypeaheadSuggestions.mjs";
|
|
7
|
+
export { useVisualLinks } from "./hooks/useVisualLinks.mjs";
|
|
8
|
+
export { isRowMenu, isSelectionMenu, useVuuMenuActions } from "./hooks/useVuuMenuActions.mjs";
|
|
9
|
+
export { useVuuTables } from "./hooks/useVuuTables.mjs";
|
|
10
|
+
export { useLostConnection } from "./lost-connection-indicator/useLostConnection.mjs";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const css = `
|
|
2
|
+
.vuuLostConnectionIndicator {
|
|
3
|
+
--spinner-color: var(--vuuSpinner-color, #e23434);
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: var(--salt-spacing-300);
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
display: flex;
|
|
9
|
+
|
|
10
|
+
& .vuuLostConnectionIndicator-spinner {
|
|
11
|
+
background: radial-gradient(farthest-side,var(--spinner-color) 94%,#0000) top/9px 9px no-repeat,
|
|
12
|
+
conic-gradient(#0000 30%,var(--spinner-color));
|
|
13
|
+
border-radius: 50%;
|
|
14
|
+
width: 75px;
|
|
15
|
+
height: 75px;
|
|
16
|
+
animation: 1s linear infinite spinner-rounded-comet-anim;
|
|
17
|
+
mask: radial-gradient(farthest-side, #0000 calc(100% - 4px), #000 0);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@keyframes spinner-rounded-comet-anim {
|
|
22
|
+
100% {
|
|
23
|
+
transform: rotate(1turn);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
`;
|
|
29
|
+
export default css;
|