@xh/hoist 71.0.0-SNAPSHOT.1733262000771 → 71.0.0-SNAPSHOT.1733266596001

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 (73) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/appcontainer/AppContainerModel.ts +2 -1
  3. package/build/types/cmp/viewmanager/SaveAsDialogModel.d.ts +23 -0
  4. package/build/types/cmp/viewmanager/View.d.ts +28 -0
  5. package/build/types/cmp/viewmanager/ViewInfo.d.ts +30 -0
  6. package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +185 -0
  7. package/build/types/cmp/viewmanager/index.d.ts +4 -0
  8. package/build/types/core/XH.d.ts +2 -1
  9. package/build/types/core/persist/PersistOptions.d.ts +3 -1
  10. package/build/types/core/persist/index.d.ts +6 -5
  11. package/build/types/core/persist/{CustomProvider.d.ts → provider/CustomProvider.d.ts} +1 -1
  12. package/build/types/core/persist/{DashViewProvider.d.ts → provider/DashViewProvider.d.ts} +2 -2
  13. package/build/types/core/persist/{LocalStorageProvider.d.ts → provider/LocalStorageProvider.d.ts} +1 -1
  14. package/build/types/core/persist/{PrefProvider.d.ts → provider/PrefProvider.d.ts} +1 -2
  15. package/build/types/core/persist/provider/SessionStorageProvider.d.ts +10 -0
  16. package/build/types/core/persist/{viewmanager → provider}/ViewManagerProvider.d.ts +3 -3
  17. package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +9 -10
  18. package/build/types/desktop/cmp/viewmanager/ViewMenu.d.ts +5 -0
  19. package/build/types/desktop/cmp/viewmanager/dialog/EditForm.d.ts +5 -0
  20. package/build/types/desktop/cmp/viewmanager/dialog/EditFormModel.d.ts +18 -0
  21. package/build/types/desktop/cmp/viewmanager/dialog/ManageDialog.d.ts +5 -0
  22. package/build/types/desktop/cmp/viewmanager/dialog/ManageDialogModel.d.ts +38 -0
  23. package/build/types/desktop/cmp/viewmanager/dialog/SaveAsDialog.d.ts +5 -0
  24. package/build/types/svc/JsonBlobService.d.ts +1 -1
  25. package/build/types/svc/index.d.ts +2 -1
  26. package/build/types/svc/storage/BaseStorageService.d.ts +21 -0
  27. package/build/types/svc/storage/LocalStorageService.d.ts +12 -0
  28. package/build/types/svc/storage/SessionStorageService.d.ts +12 -0
  29. package/cmp/viewmanager/SaveAsDialogModel.ts +97 -0
  30. package/cmp/viewmanager/View.ts +56 -0
  31. package/cmp/viewmanager/ViewInfo.ts +58 -0
  32. package/cmp/viewmanager/ViewManagerModel.ts +710 -0
  33. package/cmp/viewmanager/index.ts +4 -0
  34. package/core/XH.ts +2 -0
  35. package/core/persist/PersistOptions.ts +4 -1
  36. package/core/persist/PersistenceProvider.ts +5 -0
  37. package/core/persist/index.ts +6 -5
  38. package/core/persist/{CustomProvider.ts → provider/CustomProvider.ts} +1 -1
  39. package/core/persist/{DashViewProvider.ts → provider/DashViewProvider.ts} +1 -1
  40. package/core/persist/{LocalStorageProvider.ts → provider/LocalStorageProvider.ts} +1 -1
  41. package/core/persist/{PrefProvider.ts → provider/PrefProvider.ts} +2 -2
  42. package/core/persist/provider/SessionStorageProvider.ts +35 -0
  43. package/core/persist/{viewmanager → provider}/ViewManagerProvider.ts +5 -9
  44. package/desktop/cmp/viewmanager/ViewManager.ts +47 -229
  45. package/desktop/cmp/viewmanager/ViewMenu.ts +191 -0
  46. package/desktop/cmp/viewmanager/dialog/EditForm.ts +126 -0
  47. package/desktop/cmp/viewmanager/dialog/EditFormModel.ts +125 -0
  48. package/desktop/cmp/viewmanager/dialog/ManageDialog.ts +98 -0
  49. package/desktop/cmp/viewmanager/dialog/ManageDialogModel.ts +279 -0
  50. package/desktop/cmp/viewmanager/{impl/SaveDialog.ts → dialog/SaveAsDialog.ts} +20 -12
  51. package/package.json +1 -1
  52. package/svc/JsonBlobService.ts +1 -1
  53. package/svc/index.ts +2 -1
  54. package/svc/{LocalStorageService.ts → storage/BaseStorageService.ts} +13 -23
  55. package/svc/storage/LocalStorageService.ts +23 -0
  56. package/svc/storage/SessionStorageService.ts +23 -0
  57. package/tsconfig.tsbuildinfo +1 -1
  58. package/build/types/core/persist/viewmanager/Types.d.ts +0 -48
  59. package/build/types/core/persist/viewmanager/ViewManagerModel.d.ts +0 -145
  60. package/build/types/core/persist/viewmanager/impl/BuildViewTree.d.ts +0 -8
  61. package/build/types/core/persist/viewmanager/impl/ManageDialogModel.d.ts +0 -30
  62. package/build/types/core/persist/viewmanager/impl/SaveDialogModel.d.ts +0 -23
  63. package/build/types/core/persist/viewmanager/index.d.ts +0 -2
  64. package/build/types/desktop/cmp/viewmanager/impl/ManageDialog.d.ts +0 -6
  65. package/build/types/desktop/cmp/viewmanager/impl/SaveDialog.d.ts +0 -2
  66. package/build/types/svc/LocalStorageService.d.ts +0 -24
  67. package/core/persist/viewmanager/Types.ts +0 -53
  68. package/core/persist/viewmanager/ViewManagerModel.ts +0 -481
  69. package/core/persist/viewmanager/impl/BuildViewTree.ts +0 -68
  70. package/core/persist/viewmanager/impl/ManageDialogModel.ts +0 -276
  71. package/core/persist/viewmanager/impl/SaveDialogModel.ts +0 -112
  72. package/core/persist/viewmanager/index.ts +0 -2
  73. package/desktop/cmp/viewmanager/impl/ManageDialog.ts +0 -197
@@ -0,0 +1,4 @@
1
+ export * from './ViewManagerModel';
2
+ export * from './ViewInfo';
3
+ export * from './View';
4
+ export * from './SaveAsDialogModel';
package/core/XH.ts CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  JsonBlobService,
27
27
  LocalStorageService,
28
28
  PrefService,
29
+ SessionStorageService,
29
30
  TrackService,
30
31
  WebSocketService
31
32
  } from '@xh/hoist/svc';
@@ -140,6 +141,7 @@ export class XHApi {
140
141
  jsonBlobService: JsonBlobService;
141
142
  localStorageService: LocalStorageService;
142
143
  prefService: PrefService;
144
+ sessionStorageService: SessionStorageService;
143
145
  trackService: TrackService;
144
146
  webSocketService: WebSocketService;
145
147
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  import {DebounceSpec} from '../';
9
9
  import type {DashViewModel} from '@xh/hoist/desktop/cmp/dash'; // Import type only
10
- import type {ViewManagerModel} from '@xh/hoist/core/persist/viewmanager'; // Import type only
10
+ import type {ViewManagerModel} from '@xh/hoist/cmp/viewmanager'; // Import type only
11
11
 
12
12
  export interface PersistOptions {
13
13
  /** Dot delimited path to store state. */
@@ -28,6 +28,9 @@ export interface PersistOptions {
28
28
  /** Browser local storage key used to store state. */
29
29
  localStorageKey?: string;
30
30
 
31
+ /** Session (tab-specific) storage key used to store state. */
32
+ sessionStorageKey?: string;
33
+
31
34
  /** DashViewModel used to read / write view state. */
32
35
  dashViewModel?: DashViewModel;
33
36
 
@@ -22,6 +22,7 @@ import {
22
22
  CustomProvider,
23
23
  DashViewProvider,
24
24
  LocalStorageProvider,
25
+ SessionStorageProvider,
25
26
  PersistOptions,
26
27
  PrefProvider,
27
28
  ViewManagerProvider
@@ -91,6 +92,7 @@ export abstract class PersistenceProvider<S> {
91
92
  if (!type) {
92
93
  if (rest.prefKey) type = 'pref';
93
94
  if (rest.localStorageKey) type = 'localStorage';
95
+ if (rest.sessionStorageKey) type = 'sessionStorage';
94
96
  if (rest.dashViewModel) type = 'dashView';
95
97
  if (rest.viewManagerModel) type = 'viewManager';
96
98
  if (rest.getData || rest.setData) type = 'custom';
@@ -103,6 +105,9 @@ export abstract class PersistenceProvider<S> {
103
105
  case 'localStorage':
104
106
  ret = new LocalStorageProvider(cfg);
105
107
  break;
108
+ case 'sessionStorage':
109
+ ret = new SessionStorageProvider(cfg);
110
+ break;
106
111
  case `dashView`:
107
112
  ret = new DashViewProvider(cfg);
108
113
  break;
@@ -1,8 +1,9 @@
1
1
  export * from './Persistable';
2
2
  export * from './PersistOptions';
3
3
  export * from './PersistenceProvider';
4
- export * from './LocalStorageProvider';
5
- export * from './DashViewProvider';
6
- export * from './PrefProvider';
7
- export * from './CustomProvider';
8
- export * from './viewmanager/ViewManagerProvider';
4
+ export * from './provider/LocalStorageProvider';
5
+ export * from './provider/SessionStorageProvider';
6
+ export * from './provider/DashViewProvider';
7
+ export * from './provider/PrefProvider';
8
+ export * from './provider/CustomProvider';
9
+ export * from './provider/ViewManagerProvider';
@@ -5,7 +5,7 @@
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
7
 
8
- import {PersistenceProvider, PersistenceProviderConfig} from './';
8
+ import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
9
9
  import {throwIf} from '@xh/hoist/utils/js';
10
10
 
11
11
  /**
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type {DashViewModel} from '@xh/hoist/desktop/cmp/dash'; // Import type only
9
9
  import {throwIf} from '@xh/hoist/utils/js';
10
- import {PersistenceProvider, PersistenceProviderConfig} from './';
10
+ import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
11
11
 
12
12
  /**
13
13
  * PersistenceProvider that stores state within a DashViewModel.
@@ -7,7 +7,7 @@
7
7
 
8
8
  import {XH} from '@xh/hoist/core';
9
9
  import {throwIf} from '@xh/hoist/utils/js';
10
- import {PersistenceProvider, PersistenceProviderConfig} from './';
10
+ import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
11
11
 
12
12
  /**
13
13
  * PersistenceProvider that stores state within the Browser's LocalStorage.
@@ -5,8 +5,8 @@
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
7
 
8
- import {PersistenceProviderConfig, XH} from '../';
9
- import {PersistenceProvider} from './';
8
+ import {XH} from '@xh/hoist/core/';
9
+ import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
10
10
  import {throwIf} from '@xh/hoist/utils/js';
11
11
 
12
12
  /**
@@ -0,0 +1,35 @@
1
+ /*
2
+ * This file belongs to Hoist, an application development toolkit
3
+ * developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
4
+ *
5
+ * Copyright © 2024 Extremely Heavy Industries Inc.
6
+ */
7
+
8
+ import {XH} from '@xh/hoist/core';
9
+ import {throwIf} from '@xh/hoist/utils/js';
10
+ import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
11
+
12
+ /**
13
+ * PersistenceProvider that stores state within the Browser's SessionStorage.
14
+ */
15
+ export class SessionStorageProvider<S> extends PersistenceProvider<S> {
16
+ readonly key: string;
17
+
18
+ constructor(cfg: PersistenceProviderConfig<S>) {
19
+ super(cfg);
20
+ const {sessionStorageKey} = cfg.persistOptions;
21
+ throwIf(!sessionStorageKey, `SessionStorageProvider requires a 'sessionStorageKey'.`);
22
+ this.key = sessionStorageKey;
23
+ }
24
+
25
+ //----------------
26
+ // Implementation
27
+ //----------------
28
+ override readRaw() {
29
+ return XH.sessionStorageService.get(this.key, {});
30
+ }
31
+
32
+ override writeRaw(data) {
33
+ XH.sessionStorageService.set(this.key, data);
34
+ }
35
+ }
@@ -7,8 +7,8 @@
7
7
 
8
8
  import {throwIf} from '@xh/hoist/utils/js';
9
9
  import {pull} from 'lodash';
10
- import {PersistenceProvider, PersistenceProviderConfig} from '../index';
11
- import type {ViewManagerModel} from './index';
10
+ import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
11
+ import type {ViewManagerModel} from '@xh/hoist/cmp/viewmanager/ViewManagerModel';
12
12
 
13
13
  export class ViewManagerProvider<S> extends PersistenceProvider<S> {
14
14
  readonly viewManagerModel: ViewManagerModel;
@@ -23,22 +23,18 @@ export class ViewManagerProvider<S> extends PersistenceProvider<S> {
23
23
 
24
24
  pushStateToTarget() {
25
25
  const state = this.read();
26
- if (state) {
27
- this.target.setPersistableState(state);
28
- } else {
29
- this.target.setPersistableState(this.defaultState);
30
- }
26
+ this.target.setPersistableState(state ? state : this.defaultState);
31
27
  }
32
28
 
33
29
  //----------------
34
30
  // Implementation
35
31
  //----------------
36
32
  override readRaw() {
37
- return this.viewManagerModel.pendingValue;
33
+ return this.viewManagerModel.getValue();
38
34
  }
39
35
 
40
36
  override writeRaw(data: Record<typeof this.path, S>) {
41
- this.viewManagerModel.setPendingValue(data);
37
+ this.viewManagerModel.setValue(data);
42
38
  }
43
39
 
44
40
  override destroy() {
@@ -1,27 +1,29 @@
1
- import {div, filler, fragment, hbox, span} from '@xh/hoist/cmp/layout';
1
+ /*
2
+ * This file belongs to Hoist, an application development toolkit
3
+ * developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
4
+ *
5
+ * Copyright © 2024 Extremely Heavy Industries Inc.
6
+ */
7
+
8
+ import {fragment, hbox} from '@xh/hoist/cmp/layout';
2
9
  import {hoistCmp, HoistProps, uses} from '@xh/hoist/core';
3
- import './ViewManager.scss';
4
- import {ViewTree} from '@xh/hoist/core/persist/viewmanager';
5
- import {ViewManagerModel} from '@xh/hoist/core/persist/viewmanager/ViewManagerModel';
10
+ import {ViewManagerModel} from '@xh/hoist/cmp/viewmanager';
6
11
  import {button, ButtonProps} from '@xh/hoist/desktop/cmp/button';
7
- import {switchInput} from '@xh/hoist/desktop/cmp/input';
8
- import {manageDialog} from './impl/ManageDialog';
9
- import {saveDialog} from './impl/SaveDialog';
10
12
  import {Icon} from '@xh/hoist/icon';
11
- import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
12
- import {consumeEvent, pluralize} from '@xh/hoist/utils/js';
13
- import {isEmpty} from 'lodash';
14
- import {ReactNode} from 'react';
13
+ import {popover} from '@xh/hoist/kit/blueprint';
14
+ import {startCase} from 'lodash';
15
+ import {viewMenu} from './ViewMenu';
16
+ import {manageDialog} from './dialog/ManageDialog';
17
+ import {saveAsDialog} from './dialog/SaveAsDialog';
18
+
19
+ import './ViewManager.scss';
15
20
 
16
21
  /**
17
22
  * Visibility options for save/revert button.
18
23
  *
19
24
  * 'never' to hide button.
20
25
  * 'whenDirty' to only show when persistence state is dirty and button is therefore enabled.
21
- * 'always' will always show button, unless autoSave is active.
22
- *
23
- * Note that we never show the button when 'autoSave' is active because it would never be enabled
24
- * for more than a flash.
26
+ * 'always' will always show button.
25
27
  */
26
28
  export type ViewManagerStateButtonMode = 'whenDirty' | 'always' | 'never';
27
29
 
@@ -34,16 +36,17 @@ export interface ViewManagerProps extends HoistProps<ViewManagerModel> {
34
36
  showSaveButton?: ViewManagerStateButtonMode;
35
37
  /** Default 'never' */
36
38
  showRevertButton?: ViewManagerStateButtonMode;
37
-
38
- /** True to render private views in sub-menu (Default false)*/
39
+ /** Side the save and revert buttons should appear on (default 'right') */
40
+ buttonSide?: 'left' | 'right';
41
+ /** True to render private views in sub-menu (Default false) */
39
42
  showPrivateViewsInSubMenu?: boolean;
40
- /** True to render shared views in sub-menu (Default false)*/
41
- showSharedViewsInSubMenu?: boolean;
43
+ /** True to render global views in sub-menu (Default false) */
44
+ showGlobalViewsInSubMenu?: boolean;
42
45
  }
43
46
 
44
47
  /**
45
48
  * Desktop ViewManager component - a button-based menu for saving and swapping between named
46
- * bundles of persisted component state (eg grid views, dashboards, and similar).
49
+ * bundles of persisted component state (e.g. grid views, dashboards, and similar).
47
50
  *
48
51
  * See {@link ViewManagerModel} for additional details and configuration options.
49
52
  */
@@ -60,45 +63,36 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
60
63
  revertButtonProps,
61
64
  showSaveButton = 'whenDirty',
62
65
  showRevertButton = 'never',
66
+ buttonSide = 'right',
63
67
  showPrivateViewsInSubMenu = false,
64
- showSharedViewsInSubMenu = false
68
+ showGlobalViewsInSubMenu = false
65
69
  }: ViewManagerProps) {
70
+ const save = saveButton({mode: showSaveButton, ...saveButtonProps}),
71
+ revert = revertButton({mode: showRevertButton, ...revertButtonProps}),
72
+ menu = popover({
73
+ item: menuButton(menuButtonProps),
74
+ content: viewMenu({showPrivateViewsInSubMenu, showGlobalViewsInSubMenu}),
75
+ placement: 'bottom-start',
76
+ popoverClassName: 'xh-view-manager__popover'
77
+ });
66
78
  return fragment(
67
79
  hbox({
68
80
  className,
69
- items: [
70
- popover({
71
- item: menuButton(menuButtonProps),
72
- content: viewMenu({showPrivateViewsInSubMenu, showSharedViewsInSubMenu}),
73
- placement: 'bottom-start',
74
- popoverClassName: 'xh-view-manager__popover'
75
- }),
76
- saveButton({
77
- mode: showSaveButton,
78
- ...saveButtonProps
79
- }),
80
- revertButton({
81
- mode: showRevertButton,
82
- ...revertButtonProps
83
- })
84
- ]
81
+ items: buttonSide == 'left' ? [revert, save, menu] : [menu, save, revert]
85
82
  }),
86
- manageDialog({
87
- omit: !model.manageDialogOpen,
88
- onClose: () => model.closeManageDialog()
89
- }),
90
- saveDialog()
83
+ manageDialog({omit: !model.manageDialogOpen}),
84
+ saveAsDialog()
91
85
  );
92
86
  }
93
87
  });
94
88
 
95
89
  const menuButton = hoistCmp.factory<ViewManagerModel>({
96
90
  render({model, ...rest}) {
97
- const {selectedView, DisplayName} = model;
91
+ const {view, typeDisplayName, isLoading} = model;
98
92
  return button({
99
93
  className: 'xh-view-manager__menu-button',
100
- text: selectedView?.shortName ?? `Default ${DisplayName}`,
101
- icon: Icon.bookmark(),
94
+ text: view.info?.name ?? `Default ${startCase(typeDisplayName)}`,
95
+ icon: !isLoading ? Icon.bookmark() : Icon.spinner({className: 'fa-spin'}),
102
96
  rightIcon: Icon.chevronDown(),
103
97
  outlined: true,
104
98
  ...rest
@@ -112,11 +106,11 @@ const saveButton = hoistCmp.factory<ViewManagerModel>({
112
106
  return button({
113
107
  className: 'xh-view-manager__save-button',
114
108
  icon: Icon.save(),
115
- tooltip: `Save changes to this ${model.displayName}`,
109
+ tooltip: `Save changes to this ${model.typeDisplayName}`,
116
110
  intent: 'primary',
117
- disabled: !model.isDirty,
111
+ disabled: !model.isValueDirty || model.isLoading,
118
112
  onClick: () => {
119
- model.canSave ? model.saveAsync() : model.saveAsAsync();
113
+ model.isViewSavable ? model.saveAsync() : model.saveAsAsync();
120
114
  },
121
115
  ...rest
122
116
  });
@@ -129,9 +123,9 @@ const revertButton = hoistCmp.factory<ViewManagerModel>({
129
123
  return button({
130
124
  className: 'xh-view-manager__revert-button',
131
125
  icon: Icon.reset(),
132
- tooltip: `Revert changes to this ${model.displayName}`,
126
+ tooltip: `Revert changes to this ${model.typeDisplayName}`,
133
127
  intent: 'danger',
134
- disabled: !model.isDirty,
128
+ disabled: !model.isValueDirty || model.isLoading,
135
129
  onClick: () => model.resetAsync(),
136
130
  ...rest
137
131
  });
@@ -139,183 +133,7 @@ const revertButton = hoistCmp.factory<ViewManagerModel>({
139
133
  });
140
134
 
141
135
  function hideStateButton(model: ViewManagerModel, mode: ViewManagerStateButtonMode): boolean {
142
- return mode === 'never' || (mode === 'whenDirty' && !model.isDirty) || model.canAutoSave;
143
- }
144
-
145
- const viewMenu = hoistCmp.factory<ViewManagerProps>({
146
- render({model, showPrivateViewsInSubMenu, showSharedViewsInSubMenu}) {
147
- const {
148
- autoSaveUnavailableReason,
149
- enableDefault,
150
- canSave,
151
- selectedToken,
152
- enableAutoSave,
153
- DisplayName,
154
- autoSave,
155
- privateViewTree,
156
- sharedViewTree,
157
- favoriteViews,
158
- views,
159
- isDirty
160
- } = model;
161
-
162
- const pluralDisp = pluralize(DisplayName),
163
- items = [];
164
- if (!isEmpty(favoriteViews)) {
165
- items.push(
166
- menuDivider({title: 'Favorites'}),
167
- ...favoriteViews.map(it => {
168
- return menuItem({
169
- key: `${it.token}-favorite`,
170
- icon: model.selectedToken === it.token ? Icon.check() : Icon.placeholder(),
171
- text: menuItemTextAndFaveToggle({
172
- view: {...it, text: it.shortName}
173
- }),
174
- onClick: () => model.selectViewAsync(it.token),
175
- title: it.description
176
- });
177
- })
178
- );
179
- }
180
-
181
- if (!isEmpty(privateViewTree)) {
182
- if (showPrivateViewsInSubMenu) {
183
- items.push(
184
- menuDivider({omit: isEmpty(items)}),
185
- menuItem({
186
- text: `My ${pluralDisp}`,
187
- shouldDismissPopover: false,
188
- items: privateViewTree.map(it => buildMenuItem(it, model))
189
- })
190
- );
191
- } else {
192
- items.push(
193
- menuDivider({title: `My ${pluralDisp}`}),
194
- ...privateViewTree.map(it => buildMenuItem(it, model))
195
- );
196
- }
197
- }
198
-
199
- if (!isEmpty(sharedViewTree)) {
200
- if (showSharedViewsInSubMenu) {
201
- items.push(
202
- menuDivider({omit: isEmpty(items)}),
203
- menuItem({
204
- text: `Shared ${pluralDisp}`,
205
- shouldDismissPopover: false,
206
- items: sharedViewTree.map(it => buildMenuItem(it, model))
207
- })
208
- );
209
- } else {
210
- items.push(
211
- menuDivider({title: `Shared ${pluralDisp}`}),
212
- ...sharedViewTree.map(it => buildMenuItem(it, model))
213
- );
214
- }
215
- }
216
-
217
- return menu({
218
- className: 'xh-view-manager__menu',
219
- items: [
220
- ...items,
221
- menuDivider({omit: !enableDefault || isEmpty(items)}),
222
- menuItem({
223
- icon: selectedToken ? Icon.placeholder() : Icon.check(),
224
- text: `Default ${DisplayName}`,
225
- omit: !enableDefault,
226
- onClick: () => model.selectViewAsync(null)
227
- }),
228
- menuDivider(),
229
- menuItem({
230
- icon: Icon.save(),
231
- text: 'Save',
232
- disabled: !canSave || !isDirty,
233
- onClick: () => model.saveAsync()
234
- }),
235
- menuItem({
236
- icon: Icon.copy(),
237
- text: 'Save As...',
238
- onClick: () => model.saveAsAsync()
239
- }),
240
- menuItem({
241
- icon: Icon.reset(),
242
- text: `Revert`,
243
- disabled: !isDirty,
244
- onClick: () => model.resetAsync()
245
- }),
246
- menuDivider({omit: !enableAutoSave}),
247
- menuItem({
248
- omit: !enableAutoSave,
249
- text: switchInput({
250
- label: 'Auto Save',
251
- value: !autoSaveUnavailableReason && autoSave,
252
- disabled: !!autoSaveUnavailableReason,
253
- onChange: v => (model.autoSave = v),
254
- inline: true
255
- }),
256
- title: autoSaveUnavailableReason,
257
- shouldDismissPopover: false
258
- }),
259
- menuDivider(),
260
- menuItem({
261
- icon: Icon.gear(),
262
- disabled: isEmpty(views),
263
- text: `Manage ${pluralDisp}...`,
264
- onClick: () => model.openManageDialog()
265
- })
266
- ]
267
- });
268
- }
269
- });
270
-
271
- function buildMenuItem(viewOrFolder: ViewTree, model: ViewManagerModel): ReactNode {
272
- const {type, text, selected} = viewOrFolder,
273
- icon = selected ? Icon.check() : Icon.placeholder();
274
-
275
- switch (type) {
276
- case 'folder':
277
- return menuItem({
278
- text,
279
- icon,
280
- shouldDismissPopover: false,
281
- items: viewOrFolder.items
282
- ? viewOrFolder.items.map(child => buildMenuItem(child, model))
283
- : []
284
- });
285
- case 'view':
286
- return menuItem({
287
- className: 'xh-view-manager__menu-item',
288
- key: viewOrFolder.token,
289
- icon,
290
- text: menuItemTextAndFaveToggle({model, view: viewOrFolder}),
291
- title: viewOrFolder.description,
292
- onClick: () => model.selectViewAsync(viewOrFolder.token)
293
- });
294
- }
136
+ return (
137
+ mode === 'never' || (mode === 'whenDirty' && !model.isValueDirty) || model.isViewAutoSavable
138
+ );
295
139
  }
296
-
297
- const menuItemTextAndFaveToggle = hoistCmp.factory<ViewManagerModel>({
298
- render({model, view}) {
299
- const isFavorite = model.isFavorite(view.token);
300
- return hbox({
301
- alignItems: 'center',
302
- items: [
303
- span({style: {paddingRight: 5}, item: view.text}),
304
- fragment({
305
- omit: !model.enableFavorites,
306
- items: [
307
- filler(),
308
- div({
309
- className: `xh-view-manager__menu-item__fave-toggle ${isFavorite ? 'xh-view-manager__menu-item__fave-toggle--active' : ''}`,
310
- item: Icon.favorite({prefix: isFavorite ? 'fas' : 'far'}),
311
- onClick: e => {
312
- consumeEvent(e);
313
- model.toggleFavorite(view.token);
314
- }
315
- })
316
- ]
317
- })
318
- ]
319
- });
320
- }
321
- });