@xh/hoist 71.0.0-SNAPSHOT.1731709792477 → 71.0.0-SNAPSHOT.1731971865033
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/CHANGELOG.md +5 -0
- package/build/types/core/persist/viewmanager/Types.d.ts +3 -1
- package/build/types/core/persist/viewmanager/ViewManagerModel.d.ts +10 -19
- package/build/types/core/persist/viewmanager/impl/BuildViewTree.d.ts +8 -0
- package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +16 -2
- package/build/types/desktop/cmp/viewmanager/index.d.ts +0 -2
- package/build/types/svc/JsonBlobService.d.ts +2 -2
- package/core/persist/viewmanager/Types.ts +3 -1
- package/core/persist/viewmanager/ViewManagerModel.ts +110 -156
- package/core/persist/viewmanager/impl/BuildViewTree.ts +68 -0
- package/core/persist/viewmanager/impl/ManageDialogModel.ts +2 -0
- package/core/persist/viewmanager/impl/SaveDialogModel.ts +1 -1
- package/desktop/cmp/viewmanager/ViewManager.ts +97 -50
- package/desktop/cmp/viewmanager/{cmp → impl}/ManageDialog.ts +1 -1
- package/desktop/cmp/viewmanager/index.ts +0 -2
- package/package.json +1 -1
- package/svc/JsonBlobService.ts +3 -6
- package/tsconfig.tsbuildinfo +1 -1
- /package/build/types/desktop/cmp/viewmanager/{cmp → impl}/ManageDialog.d.ts +0 -0
- /package/build/types/desktop/cmp/viewmanager/{cmp → impl}/SaveDialog.d.ts +0 -0
- /package/desktop/cmp/viewmanager/{cmp → impl}/SaveDialog.ts +0 -0
|
@@ -5,19 +5,36 @@ import {ViewTree} from '@xh/hoist/core/persist/viewmanager';
|
|
|
5
5
|
import {ViewManagerModel} from '@xh/hoist/core/persist/viewmanager/ViewManagerModel';
|
|
6
6
|
import {button, ButtonProps} from '@xh/hoist/desktop/cmp/button';
|
|
7
7
|
import {switchInput} from '@xh/hoist/desktop/cmp/input';
|
|
8
|
-
import {manageDialog} from '
|
|
9
|
-
import {saveDialog} from '
|
|
8
|
+
import {manageDialog} from './impl/ManageDialog';
|
|
9
|
+
import {saveDialog} from './impl/SaveDialog';
|
|
10
10
|
import {Icon} from '@xh/hoist/icon';
|
|
11
11
|
import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
|
|
12
12
|
import {consumeEvent, pluralize} from '@xh/hoist/utils/js';
|
|
13
13
|
import {isEmpty} from 'lodash';
|
|
14
14
|
import {ReactNode} from 'react';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Visibility options for save/revert button.
|
|
18
|
+
*
|
|
19
|
+
* 'never' to hide button.
|
|
20
|
+
* '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.
|
|
25
|
+
*/
|
|
26
|
+
export type ViewManagerStateButtonMode = 'whenDirty' | 'always' | 'never';
|
|
27
|
+
|
|
16
28
|
export interface ViewManagerProps extends HoistProps<ViewManagerModel> {
|
|
17
29
|
menuButtonProps?: Partial<ButtonProps>;
|
|
18
30
|
saveButtonProps?: Partial<ButtonProps>;
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
revertButtonProps?: Partial<ButtonProps>;
|
|
32
|
+
|
|
33
|
+
/** Default 'whenDirty' */
|
|
34
|
+
showSaveButton?: ViewManagerStateButtonMode;
|
|
35
|
+
/** Default 'never' */
|
|
36
|
+
showRevertButton?: ViewManagerStateButtonMode;
|
|
37
|
+
|
|
21
38
|
/** True to render private views in sub-menu (Default false)*/
|
|
22
39
|
showPrivateViewsInSubMenu?: boolean;
|
|
23
40
|
/** True to render shared views in sub-menu (Default false)*/
|
|
@@ -40,7 +57,9 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
|
|
|
40
57
|
className,
|
|
41
58
|
menuButtonProps,
|
|
42
59
|
saveButtonProps,
|
|
60
|
+
revertButtonProps,
|
|
43
61
|
showSaveButton = 'whenDirty',
|
|
62
|
+
showRevertButton = 'never',
|
|
44
63
|
showPrivateViewsInSubMenu = false,
|
|
45
64
|
showSharedViewsInSubMenu = false
|
|
46
65
|
}: ViewManagerProps) {
|
|
@@ -55,8 +74,12 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
|
|
|
55
74
|
popoverClassName: 'xh-view-manager__popover'
|
|
56
75
|
}),
|
|
57
76
|
saveButton({
|
|
58
|
-
showSaveButton,
|
|
77
|
+
mode: showSaveButton,
|
|
59
78
|
...saveButtonProps
|
|
79
|
+
}),
|
|
80
|
+
revertButton({
|
|
81
|
+
mode: showRevertButton,
|
|
82
|
+
...revertButtonProps
|
|
60
83
|
})
|
|
61
84
|
]
|
|
62
85
|
}),
|
|
@@ -74,7 +97,7 @@ const menuButton = hoistCmp.factory<ViewManagerModel>({
|
|
|
74
97
|
const {selectedView, DisplayName} = model;
|
|
75
98
|
return button({
|
|
76
99
|
className: 'xh-view-manager__menu-button',
|
|
77
|
-
text:
|
|
100
|
+
text: selectedView?.shortName ?? `Default ${DisplayName}`,
|
|
78
101
|
icon: Icon.bookmark(),
|
|
79
102
|
rightIcon: Icon.chevronDown(),
|
|
80
103
|
outlined: true,
|
|
@@ -84,86 +107,109 @@ const menuButton = hoistCmp.factory<ViewManagerModel>({
|
|
|
84
107
|
});
|
|
85
108
|
|
|
86
109
|
const saveButton = hoistCmp.factory<ViewManagerModel>({
|
|
87
|
-
render({model,
|
|
88
|
-
if (
|
|
89
|
-
!model.canShowSaveButton ||
|
|
90
|
-
showSaveButton === 'never' ||
|
|
91
|
-
(showSaveButton === 'whenDirty' && !model.isDirty)
|
|
92
|
-
) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
|
|
110
|
+
render({model, mode, ...rest}) {
|
|
111
|
+
if (hideStateButton(model, mode)) return null;
|
|
96
112
|
return button({
|
|
97
113
|
className: 'xh-view-manager__save-button',
|
|
98
114
|
icon: Icon.save(),
|
|
99
115
|
tooltip: `Save changes to this ${model.displayName}`,
|
|
100
116
|
intent: 'primary',
|
|
101
|
-
disabled: !model.
|
|
102
|
-
onClick: () =>
|
|
117
|
+
disabled: !model.isDirty,
|
|
118
|
+
onClick: () => {
|
|
119
|
+
model.canSave ? model.saveAsync() : model.saveAsAsync();
|
|
120
|
+
},
|
|
121
|
+
...rest
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const revertButton = hoistCmp.factory<ViewManagerModel>({
|
|
127
|
+
render({model, mode, ...rest}) {
|
|
128
|
+
if (hideStateButton(model, mode)) return null;
|
|
129
|
+
return button({
|
|
130
|
+
className: 'xh-view-manager__revert-button',
|
|
131
|
+
icon: Icon.reset(),
|
|
132
|
+
tooltip: `Revert changes to this ${model.displayName}`,
|
|
133
|
+
intent: 'danger',
|
|
134
|
+
disabled: !model.isDirty,
|
|
135
|
+
onClick: () => model.resetAsync(),
|
|
103
136
|
...rest
|
|
104
137
|
});
|
|
105
138
|
}
|
|
106
139
|
});
|
|
107
140
|
|
|
141
|
+
function hideStateButton(model: ViewManagerModel, mode: ViewManagerStateButtonMode): boolean {
|
|
142
|
+
return mode === 'never' || (mode === 'whenDirty' && !model.isDirty) || model.canAutoSave;
|
|
143
|
+
}
|
|
144
|
+
|
|
108
145
|
const viewMenu = hoistCmp.factory<ViewManagerProps>({
|
|
109
146
|
render({model, showPrivateViewsInSubMenu, showSharedViewsInSubMenu}) {
|
|
110
|
-
const {
|
|
111
|
-
|
|
112
|
-
|
|
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;
|
|
113
161
|
|
|
114
|
-
|
|
162
|
+
const pluralDisp = pluralize(DisplayName),
|
|
163
|
+
items = [];
|
|
164
|
+
if (!isEmpty(favoriteViews)) {
|
|
115
165
|
items.push(
|
|
116
166
|
menuDivider({title: 'Favorites'}),
|
|
117
|
-
...
|
|
167
|
+
...favoriteViews.map(it => {
|
|
118
168
|
return menuItem({
|
|
119
169
|
key: `${it.token}-favorite`,
|
|
120
170
|
icon: model.selectedToken === it.token ? Icon.check() : Icon.placeholder(),
|
|
121
171
|
text: menuItemTextAndFaveToggle({
|
|
122
|
-
view: {...it, text:
|
|
172
|
+
view: {...it, text: it.shortName}
|
|
123
173
|
}),
|
|
124
|
-
onClick: () => model.selectViewAsync(it.token)
|
|
174
|
+
onClick: () => model.selectViewAsync(it.token),
|
|
125
175
|
title: it.description
|
|
126
176
|
});
|
|
127
177
|
})
|
|
128
178
|
);
|
|
129
179
|
}
|
|
130
180
|
|
|
131
|
-
if (!isEmpty(
|
|
181
|
+
if (!isEmpty(privateViewTree)) {
|
|
132
182
|
if (showPrivateViewsInSubMenu) {
|
|
133
183
|
items.push(
|
|
134
184
|
menuDivider({omit: isEmpty(items)}),
|
|
135
185
|
menuItem({
|
|
136
186
|
text: `My ${pluralDisp}`,
|
|
137
187
|
shouldDismissPopover: false,
|
|
138
|
-
|
|
139
|
-
return buildMenuItem(it, model);
|
|
140
|
-
})
|
|
188
|
+
items: privateViewTree.map(it => buildMenuItem(it, model))
|
|
141
189
|
})
|
|
142
190
|
);
|
|
143
191
|
} else {
|
|
144
192
|
items.push(
|
|
145
193
|
menuDivider({title: `My ${pluralDisp}`}),
|
|
146
|
-
...
|
|
194
|
+
...privateViewTree.map(it => buildMenuItem(it, model))
|
|
147
195
|
);
|
|
148
196
|
}
|
|
149
197
|
}
|
|
150
198
|
|
|
151
|
-
if (!isEmpty(
|
|
199
|
+
if (!isEmpty(sharedViewTree)) {
|
|
152
200
|
if (showSharedViewsInSubMenu) {
|
|
153
201
|
items.push(
|
|
154
202
|
menuDivider({omit: isEmpty(items)}),
|
|
155
203
|
menuItem({
|
|
156
204
|
text: `Shared ${pluralDisp}`,
|
|
157
205
|
shouldDismissPopover: false,
|
|
158
|
-
|
|
159
|
-
return buildMenuItem(it, model);
|
|
160
|
-
})
|
|
206
|
+
items: sharedViewTree.map(it => buildMenuItem(it, model))
|
|
161
207
|
})
|
|
162
208
|
);
|
|
163
209
|
} else {
|
|
164
210
|
items.push(
|
|
165
211
|
menuDivider({title: `Shared ${pluralDisp}`}),
|
|
166
|
-
...
|
|
212
|
+
...sharedViewTree.map(it => buildMenuItem(it, model))
|
|
167
213
|
);
|
|
168
214
|
}
|
|
169
215
|
}
|
|
@@ -172,19 +218,19 @@ const viewMenu = hoistCmp.factory<ViewManagerProps>({
|
|
|
172
218
|
className: 'xh-view-manager__menu',
|
|
173
219
|
items: [
|
|
174
220
|
...items,
|
|
175
|
-
menuDivider({omit: !
|
|
221
|
+
menuDivider({omit: !enableDefault || isEmpty(items)}),
|
|
176
222
|
menuItem({
|
|
177
|
-
icon:
|
|
223
|
+
icon: selectedToken ? Icon.placeholder() : Icon.check(),
|
|
178
224
|
text: `Default ${DisplayName}`,
|
|
179
|
-
omit: !
|
|
225
|
+
omit: !enableDefault,
|
|
180
226
|
onClick: () => model.selectViewAsync(null)
|
|
181
227
|
}),
|
|
182
228
|
menuDivider(),
|
|
183
229
|
menuItem({
|
|
184
230
|
icon: Icon.save(),
|
|
185
231
|
text: 'Save',
|
|
186
|
-
disabled: !
|
|
187
|
-
onClick: () => model.saveAsync(
|
|
232
|
+
disabled: !canSave || !isDirty,
|
|
233
|
+
onClick: () => model.saveAsync()
|
|
188
234
|
}),
|
|
189
235
|
menuItem({
|
|
190
236
|
icon: Icon.copy(),
|
|
@@ -193,26 +239,27 @@ const viewMenu = hoistCmp.factory<ViewManagerProps>({
|
|
|
193
239
|
}),
|
|
194
240
|
menuItem({
|
|
195
241
|
icon: Icon.reset(),
|
|
196
|
-
text: `Revert
|
|
197
|
-
disabled: !
|
|
242
|
+
text: `Revert`,
|
|
243
|
+
disabled: !isDirty,
|
|
198
244
|
onClick: () => model.resetAsync()
|
|
199
245
|
}),
|
|
200
|
-
menuDivider({omit: !
|
|
246
|
+
menuDivider({omit: !enableAutoSave}),
|
|
201
247
|
menuItem({
|
|
202
|
-
omit: !
|
|
248
|
+
omit: !enableAutoSave,
|
|
203
249
|
text: switchInput({
|
|
204
250
|
label: 'Auto Save',
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
251
|
+
value: !autoSaveUnavailableReason && autoSave,
|
|
252
|
+
disabled: !!autoSaveUnavailableReason,
|
|
253
|
+
onChange: v => (model.autoSave = v),
|
|
254
|
+
inline: true
|
|
208
255
|
}),
|
|
209
|
-
title:
|
|
256
|
+
title: autoSaveUnavailableReason,
|
|
210
257
|
shouldDismissPopover: false
|
|
211
258
|
}),
|
|
212
259
|
menuDivider(),
|
|
213
260
|
menuItem({
|
|
214
261
|
icon: Icon.gear(),
|
|
215
|
-
disabled: isEmpty(
|
|
262
|
+
disabled: isEmpty(views),
|
|
216
263
|
text: `Manage ${pluralDisp}...`,
|
|
217
264
|
onClick: () => model.openManageDialog()
|
|
218
265
|
})
|
|
@@ -231,7 +278,7 @@ function buildMenuItem(viewOrFolder: ViewTree, model: ViewManagerModel): ReactNo
|
|
|
231
278
|
text,
|
|
232
279
|
icon,
|
|
233
280
|
shouldDismissPopover: false,
|
|
234
|
-
|
|
281
|
+
items: viewOrFolder.items
|
|
235
282
|
? viewOrFolder.items.map(child => buildMenuItem(child, model))
|
|
236
283
|
: []
|
|
237
284
|
});
|
|
@@ -242,7 +289,7 @@ function buildMenuItem(viewOrFolder: ViewTree, model: ViewManagerModel): ReactNo
|
|
|
242
289
|
icon,
|
|
243
290
|
text: menuItemTextAndFaveToggle({model, view: viewOrFolder}),
|
|
244
291
|
title: viewOrFolder.description,
|
|
245
|
-
onClick: () => model.selectViewAsync(viewOrFolder.token)
|
|
292
|
+
onClick: () => model.selectViewAsync(viewOrFolder.token)
|
|
246
293
|
});
|
|
247
294
|
}
|
|
248
295
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "71.0.0-SNAPSHOT.
|
|
3
|
+
"version": "71.0.0-SNAPSHOT.1731971865033",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|
package/svc/JsonBlobService.ts
CHANGED
|
@@ -58,15 +58,12 @@ export class JsonBlobService extends HoistService {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/** Retrieve all blobs of a particular type that are visible to the current user. */
|
|
61
|
-
async listAsync({
|
|
62
|
-
type,
|
|
63
|
-
includeValue,
|
|
64
|
-
loadSpec
|
|
65
|
-
}: {
|
|
61
|
+
async listAsync(spec: {
|
|
66
62
|
type: string;
|
|
67
63
|
includeValue?: boolean;
|
|
68
64
|
loadSpec?: LoadSpec;
|
|
69
|
-
}) {
|
|
65
|
+
}): Promise<JsonBlob[]> {
|
|
66
|
+
const {type, includeValue, loadSpec} = spec;
|
|
70
67
|
return XH.fetchJson({
|
|
71
68
|
url: 'xh/listJsonBlobs',
|
|
72
69
|
params: {type, includeValue},
|