@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.
Files changed (119) hide show
  1. package/package.json +24 -20
  2. package/src/data-editing/EditForm.css.js +53 -0
  3. package/src/data-editing/EditForm.mjs +82 -0
  4. package/src/data-editing/UnsavedChangesReport.css.js +27 -0
  5. package/src/data-editing/UnsavedChangesReport.mjs +41 -0
  6. package/src/data-editing/edit-rule-validation-checker.mjs +30 -0
  7. package/src/data-editing/edit-validation-rules.mjs +33 -0
  8. package/src/data-editing/form-edit-state.mjs +16 -0
  9. package/src/data-editing/get-data-item-edit-control.mjs +63 -0
  10. package/src/data-editing/index.mjs +6 -0
  11. package/src/data-editing/useEditForm.mjs +253 -0
  12. package/src/datasource-provider/VuuDataSourceProvider.mjs +20 -0
  13. package/src/datasource-provider/index.mjs +1 -0
  14. package/src/datasource-provider/useAutoLoginToVuuServer.mjs +42 -0
  15. package/src/hooks/useLookupValues.mjs +93 -0
  16. package/src/hooks/useSessionDataSource.mjs +37 -0
  17. package/src/hooks/useTypeaheadSuggestions.mjs +50 -0
  18. package/src/hooks/useVisualLinks.mjs +87 -0
  19. package/src/hooks/useVuuMenuActions.mjs +269 -0
  20. package/src/hooks/useVuuTables.mjs +32 -0
  21. package/src/index.mjs +10 -0
  22. package/src/lost-connection-indicator/LostConnectionIndicator.css.js +29 -0
  23. package/src/lost-connection-indicator/LostConnectionIndicator.mjs +30 -0
  24. package/src/lost-connection-indicator/useLostConnection.mjs +28 -0
  25. package/src/session-editing-form/SessionEditingForm.css.js +47 -0
  26. package/src/session-editing-form/SessionEditingForm.mjs +236 -0
  27. package/src/session-editing-form/index.mjs +1 -0
  28. package/cjs/data-editing/EditForm.css.js +0 -6
  29. package/cjs/data-editing/EditForm.css.js.map +0 -1
  30. package/cjs/data-editing/EditForm.js +0 -90
  31. package/cjs/data-editing/EditForm.js.map +0 -1
  32. package/cjs/data-editing/UnsavedChangesReport.css.js +0 -6
  33. package/cjs/data-editing/UnsavedChangesReport.css.js.map +0 -1
  34. package/cjs/data-editing/UnsavedChangesReport.js +0 -29
  35. package/cjs/data-editing/UnsavedChangesReport.js.map +0 -1
  36. package/cjs/data-editing/edit-rule-validation-checker.js +0 -41
  37. package/cjs/data-editing/edit-rule-validation-checker.js.map +0 -1
  38. package/cjs/data-editing/edit-validation-rules.js +0 -52
  39. package/cjs/data-editing/edit-validation-rules.js.map +0 -1
  40. package/cjs/data-editing/form-edit-state.js +0 -26
  41. package/cjs/data-editing/form-edit-state.js.map +0 -1
  42. package/cjs/data-editing/get-data-item-edit-control.js +0 -106
  43. package/cjs/data-editing/get-data-item-edit-control.js.map +0 -1
  44. package/cjs/data-editing/useEditForm.js +0 -258
  45. package/cjs/data-editing/useEditForm.js.map +0 -1
  46. package/cjs/datasource-provider/VuuDataSourceProvider.js +0 -34
  47. package/cjs/datasource-provider/VuuDataSourceProvider.js.map +0 -1
  48. package/cjs/datasource-provider/useAutoLoginToVuuServer.js +0 -52
  49. package/cjs/datasource-provider/useAutoLoginToVuuServer.js.map +0 -1
  50. package/cjs/hooks/useLookupValues.js +0 -100
  51. package/cjs/hooks/useLookupValues.js.map +0 -1
  52. package/cjs/hooks/useSessionDataSource.js +0 -58
  53. package/cjs/hooks/useSessionDataSource.js.map +0 -1
  54. package/cjs/hooks/useTypeaheadSuggestions.js +0 -57
  55. package/cjs/hooks/useTypeaheadSuggestions.js.map +0 -1
  56. package/cjs/hooks/useVisualLinks.js +0 -84
  57. package/cjs/hooks/useVisualLinks.js.map +0 -1
  58. package/cjs/hooks/useVuuMenuActions.js +0 -352
  59. package/cjs/hooks/useVuuMenuActions.js.map +0 -1
  60. package/cjs/hooks/useVuuTables.js +0 -38
  61. package/cjs/hooks/useVuuTables.js.map +0 -1
  62. package/cjs/index.js +0 -42
  63. package/cjs/index.js.map +0 -1
  64. package/cjs/lost-connection-indicator/LostConnectionIndicator.css.js +0 -6
  65. package/cjs/lost-connection-indicator/LostConnectionIndicator.css.js.map +0 -1
  66. package/cjs/lost-connection-indicator/LostConnectionIndicator.js +0 -24
  67. package/cjs/lost-connection-indicator/LostConnectionIndicator.js.map +0 -1
  68. package/cjs/lost-connection-indicator/useLostConnection.js +0 -34
  69. package/cjs/lost-connection-indicator/useLostConnection.js.map +0 -1
  70. package/cjs/session-editing-form/SessionEditingForm.css.js +0 -6
  71. package/cjs/session-editing-form/SessionEditingForm.css.js.map +0 -1
  72. package/cjs/session-editing-form/SessionEditingForm.js +0 -270
  73. package/cjs/session-editing-form/SessionEditingForm.js.map +0 -1
  74. package/esm/data-editing/EditForm.css.js +0 -4
  75. package/esm/data-editing/EditForm.css.js.map +0 -1
  76. package/esm/data-editing/EditForm.js +0 -88
  77. package/esm/data-editing/EditForm.js.map +0 -1
  78. package/esm/data-editing/UnsavedChangesReport.css.js +0 -4
  79. package/esm/data-editing/UnsavedChangesReport.css.js.map +0 -1
  80. package/esm/data-editing/UnsavedChangesReport.js +0 -27
  81. package/esm/data-editing/UnsavedChangesReport.js.map +0 -1
  82. package/esm/data-editing/edit-rule-validation-checker.js +0 -37
  83. package/esm/data-editing/edit-rule-validation-checker.js.map +0 -1
  84. package/esm/data-editing/edit-validation-rules.js +0 -50
  85. package/esm/data-editing/edit-validation-rules.js.map +0 -1
  86. package/esm/data-editing/form-edit-state.js +0 -23
  87. package/esm/data-editing/form-edit-state.js.map +0 -1
  88. package/esm/data-editing/get-data-item-edit-control.js +0 -104
  89. package/esm/data-editing/get-data-item-edit-control.js.map +0 -1
  90. package/esm/data-editing/useEditForm.js +0 -256
  91. package/esm/data-editing/useEditForm.js.map +0 -1
  92. package/esm/datasource-provider/VuuDataSourceProvider.js +0 -32
  93. package/esm/datasource-provider/VuuDataSourceProvider.js.map +0 -1
  94. package/esm/datasource-provider/useAutoLoginToVuuServer.js +0 -50
  95. package/esm/datasource-provider/useAutoLoginToVuuServer.js.map +0 -1
  96. package/esm/hooks/useLookupValues.js +0 -98
  97. package/esm/hooks/useLookupValues.js.map +0 -1
  98. package/esm/hooks/useSessionDataSource.js +0 -56
  99. package/esm/hooks/useSessionDataSource.js.map +0 -1
  100. package/esm/hooks/useTypeaheadSuggestions.js +0 -54
  101. package/esm/hooks/useTypeaheadSuggestions.js.map +0 -1
  102. package/esm/hooks/useVisualLinks.js +0 -82
  103. package/esm/hooks/useVisualLinks.js.map +0 -1
  104. package/esm/hooks/useVuuMenuActions.js +0 -348
  105. package/esm/hooks/useVuuMenuActions.js.map +0 -1
  106. package/esm/hooks/useVuuTables.js +0 -36
  107. package/esm/hooks/useVuuTables.js.map +0 -1
  108. package/esm/index.js +0 -16
  109. package/esm/index.js.map +0 -1
  110. package/esm/lost-connection-indicator/LostConnectionIndicator.css.js +0 -4
  111. package/esm/lost-connection-indicator/LostConnectionIndicator.css.js.map +0 -1
  112. package/esm/lost-connection-indicator/LostConnectionIndicator.js +0 -22
  113. package/esm/lost-connection-indicator/LostConnectionIndicator.js.map +0 -1
  114. package/esm/lost-connection-indicator/useLostConnection.js +0 -32
  115. package/esm/lost-connection-indicator/useLostConnection.js.map +0 -1
  116. package/esm/session-editing-form/SessionEditingForm.css.js +0 -4
  117. package/esm/session-editing-form/SessionEditingForm.css.js.map +0 -1
  118. package/esm/session-editing-form/SessionEditingForm.js +0 -268
  119. 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;