@xh/hoist 71.0.0-SNAPSHOT.1733791818708 → 71.0.0-SNAPSHOT.1734118787755
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/build/types/cmp/viewmanager/View.d.ts +5 -0
- package/build/types/cmp/viewmanager/ViewInfo.d.ts +32 -7
- package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +34 -31
- package/build/types/cmp/viewmanager/ViewToBlobApi.d.ts +28 -6
- package/build/types/cmp/viewmanager/index.d.ts +1 -1
- package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +0 -4
- package/build/types/desktop/cmp/viewmanager/ViewManagerLocalModel.d.ts +10 -0
- package/build/types/desktop/cmp/viewmanager/ViewMenu.d.ts +2 -2
- package/build/types/desktop/cmp/viewmanager/dialog/ManageDialog.d.ts +4 -3
- package/build/types/desktop/cmp/viewmanager/dialog/ManageDialogModel.d.ts +19 -10
- package/build/types/desktop/cmp/viewmanager/dialog/SaveAsDialog.d.ts +1 -1
- package/build/types/{cmp/viewmanager → desktop/cmp/viewmanager/dialog}/SaveAsDialogModel.d.ts +3 -9
- package/build/types/desktop/cmp/viewmanager/dialog/Utils.d.ts +3 -0
- package/build/types/desktop/cmp/viewmanager/dialog/ViewMultiPanel.d.ts +1 -0
- package/build/types/desktop/cmp/viewmanager/dialog/ViewPanel.d.ts +5 -0
- package/build/types/desktop/cmp/viewmanager/dialog/{EditFormModel.d.ts → ViewPanelModel.d.ts} +2 -4
- package/build/types/svc/JsonBlobService.d.ts +1 -1
- package/cmp/viewmanager/View.ts +21 -1
- package/cmp/viewmanager/ViewInfo.ts +58 -11
- package/cmp/viewmanager/ViewManagerModel.ts +86 -81
- package/cmp/viewmanager/ViewToBlobApi.ts +91 -35
- package/cmp/viewmanager/index.ts +1 -1
- package/desktop/cmp/dash/container/DashContainerModel.ts +17 -5
- package/desktop/cmp/viewmanager/ViewManager.scss +25 -28
- package/desktop/cmp/viewmanager/ViewManager.ts +28 -26
- package/desktop/cmp/viewmanager/ViewManagerLocalModel.ts +28 -0
- package/desktop/cmp/viewmanager/ViewMenu.ts +162 -169
- package/desktop/cmp/viewmanager/dialog/ManageDialog.ts +67 -40
- package/desktop/cmp/viewmanager/dialog/ManageDialogModel.ts +238 -127
- package/desktop/cmp/viewmanager/dialog/SaveAsDialog.ts +30 -9
- package/{cmp/viewmanager → desktop/cmp/viewmanager/dialog}/SaveAsDialogModel.ts +35 -40
- package/desktop/cmp/viewmanager/dialog/Utils.ts +18 -0
- package/desktop/cmp/viewmanager/dialog/ViewMultiPanel.ts +70 -0
- package/desktop/cmp/viewmanager/dialog/ViewPanel.ts +161 -0
- package/desktop/cmp/viewmanager/dialog/ViewPanelModel.ts +116 -0
- package/package.json +1 -1
- package/svc/JsonBlobService.ts +3 -3
- package/svc/storage/BaseStorageService.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/build/types/desktop/cmp/viewmanager/dialog/EditForm.d.ts +0 -5
- package/desktop/cmp/viewmanager/dialog/EditForm.ts +0 -126
- package/desktop/cmp/viewmanager/dialog/EditFormModel.ts +0 -125
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {form} from '@xh/hoist/cmp/form';
|
|
9
|
-
import {filler, vframe} from '@xh/hoist/cmp/layout';
|
|
9
|
+
import {filler, hbox, vframe} from '@xh/hoist/cmp/layout';
|
|
10
10
|
import {hoistCmp, uses} from '@xh/hoist/core';
|
|
11
|
-
import {SaveAsDialogModel} from '@xh/hoist/cmp/viewmanager/';
|
|
12
11
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
13
12
|
import {formField} from '@xh/hoist/desktop/cmp/form';
|
|
14
|
-
import {textArea, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
13
|
+
import {select, switchInput, textArea, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
15
14
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
16
15
|
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
17
16
|
import {dialog} from '@xh/hoist/kit/blueprint';
|
|
18
17
|
import {startCase} from 'lodash';
|
|
18
|
+
import {SaveAsDialogModel} from './SaveAsDialogModel';
|
|
19
|
+
import {getGroupOptions} from './Utils';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Default Save As dialog used by ViewManager.
|
|
@@ -34,7 +35,7 @@ export const saveAsDialog = hoistCmp.factory<SaveAsDialogModel>({
|
|
|
34
35
|
isOpen: true,
|
|
35
36
|
style: {width: 500},
|
|
36
37
|
canOutsideClickClose: false,
|
|
37
|
-
onClose: () => model.
|
|
38
|
+
onClose: () => model.close(),
|
|
38
39
|
item: formPanel()
|
|
39
40
|
});
|
|
40
41
|
}
|
|
@@ -45,7 +46,9 @@ const formPanel = hoistCmp.factory<SaveAsDialogModel>({
|
|
|
45
46
|
return panel({
|
|
46
47
|
item: form({
|
|
47
48
|
fieldDefaults: {
|
|
48
|
-
commitOnChange: true
|
|
49
|
+
commitOnChange: true,
|
|
50
|
+
inline: true,
|
|
51
|
+
minimal: true
|
|
49
52
|
},
|
|
50
53
|
item: vframe({
|
|
51
54
|
className: 'xh-view-manager__save-dialog__form',
|
|
@@ -60,13 +63,31 @@ const formPanel = hoistCmp.factory<SaveAsDialogModel>({
|
|
|
60
63
|
}
|
|
61
64
|
})
|
|
62
65
|
}),
|
|
66
|
+
formField({
|
|
67
|
+
field: 'group',
|
|
68
|
+
item: select({
|
|
69
|
+
enableCreate: true,
|
|
70
|
+
enableClear: true,
|
|
71
|
+
placeholder: 'Select optional group....',
|
|
72
|
+
options: getGroupOptions(model.parent, 'owned')
|
|
73
|
+
})
|
|
74
|
+
}),
|
|
63
75
|
formField({
|
|
64
76
|
field: 'description',
|
|
65
77
|
item: textArea({
|
|
66
78
|
selectOnFocus: true,
|
|
67
|
-
height:
|
|
79
|
+
height: 70
|
|
80
|
+
})
|
|
81
|
+
}),
|
|
82
|
+
hbox(
|
|
83
|
+
formField({
|
|
84
|
+
field: 'isShared',
|
|
85
|
+
label: 'Share?',
|
|
86
|
+
labelTextAlign: 'left',
|
|
87
|
+
omit: !model.parent.enableSharing,
|
|
88
|
+
item: switchInput()
|
|
68
89
|
})
|
|
69
|
-
|
|
90
|
+
)
|
|
70
91
|
]
|
|
71
92
|
})
|
|
72
93
|
}),
|
|
@@ -78,12 +99,12 @@ const formPanel = hoistCmp.factory<SaveAsDialogModel>({
|
|
|
78
99
|
|
|
79
100
|
const bbar = hoistCmp.factory<SaveAsDialogModel>({
|
|
80
101
|
render({model}) {
|
|
81
|
-
const {typeDisplayName} = model;
|
|
102
|
+
const {typeDisplayName} = model.parent;
|
|
82
103
|
return toolbar(
|
|
83
104
|
filler(),
|
|
84
105
|
button({
|
|
85
106
|
text: 'Cancel',
|
|
86
|
-
onClick: () => model.
|
|
107
|
+
onClick: () => model.close()
|
|
87
108
|
}),
|
|
88
109
|
button({
|
|
89
110
|
text: `Save as new ${startCase(typeDisplayName)}`,
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
import {FormModel} from '@xh/hoist/cmp/form';
|
|
9
9
|
import {HoistModel, managed, XH} from '@xh/hoist/core';
|
|
10
10
|
import {makeObservable, action, observable} from '@xh/hoist/mobx';
|
|
11
|
-
import {
|
|
12
|
-
import {ViewManagerModel} from './ViewManagerModel';
|
|
11
|
+
import {ViewManagerModel} from '@xh/hoist/cmp/viewmanager';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Backing model for ViewManagerModel's SaveAs
|
|
@@ -20,20 +19,6 @@ export class SaveAsDialogModel extends HoistModel {
|
|
|
20
19
|
@managed readonly formModel: FormModel;
|
|
21
20
|
@observable isOpen: boolean = false;
|
|
22
21
|
|
|
23
|
-
private resolveOpen: (value: View) => void;
|
|
24
|
-
|
|
25
|
-
get type(): string {
|
|
26
|
-
return this.parent.type;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get typeDisplayName(): string {
|
|
30
|
-
return this.parent.typeDisplayName;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get globalDisplayName(): string {
|
|
34
|
-
return this.parent.globalDisplayName;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
22
|
constructor(parent: ViewManagerModel) {
|
|
38
23
|
super();
|
|
39
24
|
makeObservable(this);
|
|
@@ -42,20 +27,35 @@ export class SaveAsDialogModel extends HoistModel {
|
|
|
42
27
|
}
|
|
43
28
|
|
|
44
29
|
@action
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
open() {
|
|
31
|
+
const {parent, formModel} = this,
|
|
32
|
+
src = parent.view,
|
|
33
|
+
name = parent.ownedViews.some(it => it.name === src.name)
|
|
34
|
+
? `Copy of ${src.name}`
|
|
35
|
+
: src.name;
|
|
36
|
+
|
|
37
|
+
formModel.init({
|
|
38
|
+
name,
|
|
39
|
+
group: src.group,
|
|
40
|
+
description: src.description,
|
|
41
|
+
isShared: false
|
|
42
|
+
});
|
|
48
43
|
|
|
49
|
-
|
|
44
|
+
this.isOpen = true;
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.
|
|
47
|
+
@action
|
|
48
|
+
close() {
|
|
49
|
+
this.isOpen = false;
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
async saveAsAsync() {
|
|
58
|
-
|
|
53
|
+
try {
|
|
54
|
+
await this.doSaveAsAsync().linkTo(this.parent.saveTask);
|
|
55
|
+
this.close();
|
|
56
|
+
} catch (e) {
|
|
57
|
+
XH.handleException(e);
|
|
58
|
+
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
//------------------------
|
|
@@ -68,30 +68,25 @@ export class SaveAsDialogModel extends HoistModel {
|
|
|
68
68
|
name: 'name',
|
|
69
69
|
rules: [({value}) => this.parent.validateViewNameAsync(value)]
|
|
70
70
|
},
|
|
71
|
-
{name: '
|
|
71
|
+
{name: 'group'},
|
|
72
|
+
{name: 'description'},
|
|
73
|
+
{name: 'isShared'}
|
|
72
74
|
]
|
|
73
75
|
});
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
private async doSaveAsAsync() {
|
|
77
|
-
|
|
78
|
-
{name, description} = formModel.getData(),
|
|
79
|
+
let {formModel, parent} = this,
|
|
80
|
+
{name, group, description, isShared} = formModel.getData(),
|
|
79
81
|
isValid = await formModel.validateAsync();
|
|
80
82
|
|
|
81
83
|
if (!isValid) return;
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
@action
|
|
93
|
-
private close() {
|
|
94
|
-
this.isOpen = false;
|
|
95
|
-
this.formModel.init();
|
|
85
|
+
return parent.saveAsAsync({
|
|
86
|
+
name: name.trim(),
|
|
87
|
+
group: group?.trim(),
|
|
88
|
+
description: description?.trim(),
|
|
89
|
+
isShared
|
|
90
|
+
});
|
|
96
91
|
}
|
|
97
92
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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 {ViewManagerModel} from '@xh/hoist/cmp/viewmanager';
|
|
9
|
+
import {SelectOption} from '@xh/hoist/core';
|
|
10
|
+
import {map, uniq} from 'lodash';
|
|
11
|
+
|
|
12
|
+
export function getGroupOptions(model: ViewManagerModel, type: 'owned' | 'global'): SelectOption[] {
|
|
13
|
+
const views = type == 'owned' ? model.ownedViews : model.globalViews;
|
|
14
|
+
return uniq(map(views, 'group'))
|
|
15
|
+
.sort()
|
|
16
|
+
.filter(g => g != null)
|
|
17
|
+
.map(g => ({label: g, value: g}));
|
|
18
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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 {placeholder, vbox, vframe, vspacer} from '@xh/hoist/cmp/layout';
|
|
9
|
+
import {hoistCmp, uses} from '@xh/hoist/core';
|
|
10
|
+
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
11
|
+
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
12
|
+
import {Icon} from '@xh/hoist/icon';
|
|
13
|
+
import {pluralize} from '@xh/hoist/utils/js';
|
|
14
|
+
import {every, isEmpty, some} from 'lodash';
|
|
15
|
+
import {ManageDialogModel} from './ManageDialogModel';
|
|
16
|
+
|
|
17
|
+
export const viewMultiPanel = hoistCmp.factory({
|
|
18
|
+
model: uses(() => ManageDialogModel),
|
|
19
|
+
render({model}) {
|
|
20
|
+
const views = model.selectedViews;
|
|
21
|
+
if (isEmpty(views)) return null;
|
|
22
|
+
|
|
23
|
+
return panel({
|
|
24
|
+
item: vframe({
|
|
25
|
+
className: 'xh-view-manager__manage-dialog__form',
|
|
26
|
+
item: placeholder(
|
|
27
|
+
Icon.gears(),
|
|
28
|
+
`${views.length} selected ${pluralize(model.typeDisplayName)}`,
|
|
29
|
+
vspacer(),
|
|
30
|
+
buttons()
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const buttons = hoistCmp.factory<ManageDialogModel>({
|
|
38
|
+
render({model}) {
|
|
39
|
+
const views = model.selectedViews,
|
|
40
|
+
allEditable = every(views, 'isEditable'),
|
|
41
|
+
allPinned = every(views, 'isPinned'),
|
|
42
|
+
allUnpinned = !some(views, 'isPinned');
|
|
43
|
+
|
|
44
|
+
return vbox({
|
|
45
|
+
style: {gap: 10, alignItems: 'center'},
|
|
46
|
+
items: [
|
|
47
|
+
button({
|
|
48
|
+
text: allPinned ? 'Unpin from your Menu' : 'Pin to your Menu',
|
|
49
|
+
icon: Icon.pin({
|
|
50
|
+
prefix: allPinned ? 'fas' : 'far',
|
|
51
|
+
className: allPinned ? 'xh-yellow' : ''
|
|
52
|
+
}),
|
|
53
|
+
width: 200,
|
|
54
|
+
outlined: true,
|
|
55
|
+
omit: !(allPinned || allUnpinned),
|
|
56
|
+
onClick: () => model.togglePinned(views)
|
|
57
|
+
}),
|
|
58
|
+
button({
|
|
59
|
+
text: 'Delete',
|
|
60
|
+
icon: Icon.delete(),
|
|
61
|
+
width: 200,
|
|
62
|
+
outlined: true,
|
|
63
|
+
intent: 'danger',
|
|
64
|
+
omit: !allEditable,
|
|
65
|
+
onClick: () => model.deleteAsync(views)
|
|
66
|
+
})
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
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 {form} from '@xh/hoist/cmp/form';
|
|
9
|
+
import {div, filler, hbox, hspacer, span, vbox, vframe, vspacer} from '@xh/hoist/cmp/layout';
|
|
10
|
+
import {hoistCmp, uses, XH} from '@xh/hoist/core';
|
|
11
|
+
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
12
|
+
import {formField} from '@xh/hoist/desktop/cmp/form';
|
|
13
|
+
import {select, switchInput, textArea, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
14
|
+
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
15
|
+
import {ViewPanelModel} from '@xh/hoist/desktop/cmp/viewmanager/dialog/ViewPanelModel';
|
|
16
|
+
import {getGroupOptions} from '@xh/hoist/desktop/cmp/viewmanager/dialog/Utils';
|
|
17
|
+
import {fmtDateTime} from '@xh/hoist/format';
|
|
18
|
+
import {Icon} from '@xh/hoist/icon';
|
|
19
|
+
import {capitalize} from 'lodash';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Form to edit or view details on a single saved view within the ViewManager manage dialog.
|
|
23
|
+
*/
|
|
24
|
+
export const viewPanel = hoistCmp.factory({
|
|
25
|
+
model: uses(ViewPanelModel),
|
|
26
|
+
render({model}) {
|
|
27
|
+
const {view} = model;
|
|
28
|
+
if (!view) return null;
|
|
29
|
+
|
|
30
|
+
const {isGlobal, lastUpdated, lastUpdatedBy, isEditable} = view;
|
|
31
|
+
|
|
32
|
+
return panel({
|
|
33
|
+
item: form({
|
|
34
|
+
fieldDefaults: {
|
|
35
|
+
commitOnChange: true,
|
|
36
|
+
minimal: true
|
|
37
|
+
},
|
|
38
|
+
item: vframe({
|
|
39
|
+
className: 'xh-view-manager__manage-dialog__form',
|
|
40
|
+
items: [
|
|
41
|
+
formField({
|
|
42
|
+
field: 'name',
|
|
43
|
+
item: textInput()
|
|
44
|
+
}),
|
|
45
|
+
formField({
|
|
46
|
+
field: 'owner',
|
|
47
|
+
omit: isEditable
|
|
48
|
+
}),
|
|
49
|
+
formField({
|
|
50
|
+
field: 'group',
|
|
51
|
+
item: select({
|
|
52
|
+
enableCreate: true,
|
|
53
|
+
enableClear: true,
|
|
54
|
+
options: getGroupOptions(
|
|
55
|
+
model.parent.viewManagerModel,
|
|
56
|
+
view.isOwned ? 'owned' : 'global'
|
|
57
|
+
)
|
|
58
|
+
}),
|
|
59
|
+
readonlyRenderer: v =>
|
|
60
|
+
v || span({item: 'None provided', className: 'xh-text-color-muted'})
|
|
61
|
+
}),
|
|
62
|
+
formField({
|
|
63
|
+
field: 'description',
|
|
64
|
+
item: textArea({
|
|
65
|
+
selectOnFocus: true,
|
|
66
|
+
height: 70
|
|
67
|
+
}),
|
|
68
|
+
readonlyRenderer: v =>
|
|
69
|
+
v || span({item: 'None provided', className: 'xh-text-color-muted'})
|
|
70
|
+
}),
|
|
71
|
+
formField({
|
|
72
|
+
field: 'isShared',
|
|
73
|
+
label: 'Shared?',
|
|
74
|
+
inline: true,
|
|
75
|
+
item: switchInput(),
|
|
76
|
+
readonlyRenderer: v => (v ? 'Yes' : 'No'),
|
|
77
|
+
omit: isGlobal || !isEditable
|
|
78
|
+
}),
|
|
79
|
+
formField({
|
|
80
|
+
field: 'isDefaultPinned',
|
|
81
|
+
label: 'Pin by default?',
|
|
82
|
+
labelWidth: 110,
|
|
83
|
+
inline: true,
|
|
84
|
+
item: switchInput(),
|
|
85
|
+
omit: !isGlobal || !isEditable
|
|
86
|
+
}),
|
|
87
|
+
vspacer(),
|
|
88
|
+
formButtons(),
|
|
89
|
+
filler(),
|
|
90
|
+
div({
|
|
91
|
+
className: 'xh-view-manager__manage-dialog__metadata',
|
|
92
|
+
item: `Last Updated: ${fmtDateTime(lastUpdated)} by ${lastUpdatedBy === XH.getUsername() ? 'you' : lastUpdatedBy}`
|
|
93
|
+
})
|
|
94
|
+
]
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const formButtons = hoistCmp.factory<ViewPanelModel>({
|
|
102
|
+
render({model}) {
|
|
103
|
+
const {formModel, parent, view} = model,
|
|
104
|
+
{readonly} = formModel,
|
|
105
|
+
{isPinned} = view;
|
|
106
|
+
|
|
107
|
+
return formModel.isDirty
|
|
108
|
+
? hbox({
|
|
109
|
+
justifyContent: 'center',
|
|
110
|
+
items: [
|
|
111
|
+
button({
|
|
112
|
+
text: 'Save Changes',
|
|
113
|
+
icon: Icon.check(),
|
|
114
|
+
intent: 'success',
|
|
115
|
+
minimal: false,
|
|
116
|
+
disabled: !formModel.isValid,
|
|
117
|
+
onClick: () => model.saveAsync()
|
|
118
|
+
}),
|
|
119
|
+
hspacer(),
|
|
120
|
+
button({
|
|
121
|
+
icon: Icon.reset(),
|
|
122
|
+
tooltip: 'Revert changes',
|
|
123
|
+
minimal: false,
|
|
124
|
+
onClick: () => formModel.reset()
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
})
|
|
128
|
+
: vbox({
|
|
129
|
+
style: {gap: 10, alignItems: 'center'},
|
|
130
|
+
items: [
|
|
131
|
+
button({
|
|
132
|
+
text: isPinned ? 'Unpin from your Menu' : 'Pin to your Menu',
|
|
133
|
+
icon: Icon.pin({
|
|
134
|
+
prefix: isPinned ? 'fas' : 'far',
|
|
135
|
+
className: isPinned ? 'xh-yellow' : null
|
|
136
|
+
}),
|
|
137
|
+
width: 200,
|
|
138
|
+
outlined: true,
|
|
139
|
+
onClick: () => parent.togglePinned([view])
|
|
140
|
+
}),
|
|
141
|
+
button({
|
|
142
|
+
text: `Promote to ${capitalize(parent.globalDisplayName)} ${parent.typeDisplayName}`,
|
|
143
|
+
icon: Icon.globe(),
|
|
144
|
+
width: 200,
|
|
145
|
+
outlined: true,
|
|
146
|
+
omit: readonly || view.isGlobal || !parent.manageGlobal,
|
|
147
|
+
onClick: () => parent.makeGlobalAsync(view)
|
|
148
|
+
}),
|
|
149
|
+
button({
|
|
150
|
+
text: 'Delete',
|
|
151
|
+
icon: Icon.delete(),
|
|
152
|
+
width: 200,
|
|
153
|
+
outlined: true,
|
|
154
|
+
intent: 'danger',
|
|
155
|
+
omit: readonly,
|
|
156
|
+
onClick: () => parent.deleteAsync([view])
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
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 {FormModel} from '@xh/hoist/cmp/form';
|
|
9
|
+
import {fragment, p, strong} from '@xh/hoist/cmp/layout';
|
|
10
|
+
import {HoistModel, managed, TaskObserver, XH} from '@xh/hoist/core';
|
|
11
|
+
import {capitalize} from 'lodash';
|
|
12
|
+
import {ManageDialogModel} from './ManageDialogModel';
|
|
13
|
+
import {makeObservable} from '@xh/hoist/mobx';
|
|
14
|
+
import {ViewInfo} from '@xh/hoist/cmp/viewmanager';
|
|
15
|
+
import {ReactNode} from 'react';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Backing model for EditForm
|
|
19
|
+
*/
|
|
20
|
+
export class ViewPanelModel extends HoistModel {
|
|
21
|
+
parent: ManageDialogModel;
|
|
22
|
+
|
|
23
|
+
@managed formModel: FormModel;
|
|
24
|
+
|
|
25
|
+
get view(): ViewInfo {
|
|
26
|
+
return this.parent.selectedView;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get loadTask(): TaskObserver {
|
|
30
|
+
return this.parent.loadModel;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
constructor(parent: ManageDialogModel) {
|
|
34
|
+
super();
|
|
35
|
+
makeObservable(this);
|
|
36
|
+
|
|
37
|
+
this.parent = parent;
|
|
38
|
+
this.formModel = this.createFormModel();
|
|
39
|
+
|
|
40
|
+
this.addReaction({
|
|
41
|
+
track: () => this.view,
|
|
42
|
+
run: view => {
|
|
43
|
+
if (view) {
|
|
44
|
+
const {formModel} = this;
|
|
45
|
+
formModel.init({
|
|
46
|
+
...view,
|
|
47
|
+
owner: view.owner ?? capitalize(parent.globalDisplayName)
|
|
48
|
+
});
|
|
49
|
+
formModel.readonly = !view.isEditable;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
fireImmediately: true
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async saveAsync() {
|
|
57
|
+
const {parent, view, formModel} = this,
|
|
58
|
+
{name, group, description, isDefaultPinned, isShared} = formModel.getData(),
|
|
59
|
+
isValid = await formModel.validateAsync(),
|
|
60
|
+
isDirty = formModel.isDirty;
|
|
61
|
+
|
|
62
|
+
if (!isValid || !isDirty) return;
|
|
63
|
+
|
|
64
|
+
if (view.isOwned && view.isShared != isShared) {
|
|
65
|
+
const msg: ReactNode = !isShared
|
|
66
|
+
? `Your ${view.typedName} will no longer be visible to all other ${XH.appName} users.`
|
|
67
|
+
: `Your ${view.typedName} will become visible to all other ${XH.appName} users.`;
|
|
68
|
+
const msgs = [msg, strong('Are you sure you want to proceed?')];
|
|
69
|
+
|
|
70
|
+
const confirmed = await XH.confirm({
|
|
71
|
+
message: fragment(msgs.map(m => p(m))),
|
|
72
|
+
confirmProps: {
|
|
73
|
+
text: 'Yes, update sharing',
|
|
74
|
+
outlined: true,
|
|
75
|
+
autoFocus: false,
|
|
76
|
+
intent: 'primary'
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (!confirmed) return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await parent.updateAsync(view, {
|
|
83
|
+
name,
|
|
84
|
+
group,
|
|
85
|
+
description,
|
|
86
|
+
isShared,
|
|
87
|
+
isDefaultPinned
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//------------------------
|
|
92
|
+
// Implementation
|
|
93
|
+
//------------------------
|
|
94
|
+
private createFormModel(): FormModel {
|
|
95
|
+
return new FormModel({
|
|
96
|
+
fields: [
|
|
97
|
+
{
|
|
98
|
+
name: 'name',
|
|
99
|
+
rules: [
|
|
100
|
+
async ({value}) => {
|
|
101
|
+
return this.parent.viewManagerModel.validateViewNameAsync(
|
|
102
|
+
value,
|
|
103
|
+
this.view
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{name: 'owner'},
|
|
109
|
+
{name: 'group'},
|
|
110
|
+
{name: 'description'},
|
|
111
|
+
{name: 'isShared'},
|
|
112
|
+
{name: 'isDefaultPinned'}
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
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.1734118787755",
|
|
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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {HoistService, LoadSpec, PlainObject, XH} from '@xh/hoist/core';
|
|
8
|
-
import {
|
|
8
|
+
import {isUndefined, omitBy} from 'lodash';
|
|
9
9
|
|
|
10
10
|
export interface JsonBlob {
|
|
11
11
|
/** Either null for private blobs or special token "*" for globally shared blobs. */
|
|
@@ -91,9 +91,9 @@ export class JsonBlobService extends HoistService {
|
|
|
91
91
|
/** Modify mutable properties of an existing JSONBlob, as identified by its unique token. */
|
|
92
92
|
async updateAsync(
|
|
93
93
|
token: string,
|
|
94
|
-
{acl, description, meta, name, value}: Partial<JsonBlob>
|
|
94
|
+
{acl, description, meta, name, owner, value}: Partial<JsonBlob>
|
|
95
95
|
): Promise<JsonBlob> {
|
|
96
|
-
const update =
|
|
96
|
+
const update = omitBy({acl, description, meta, name, owner, value}, isUndefined);
|
|
97
97
|
return XH.fetchJson({
|
|
98
98
|
url: 'xh/updateJsonBlob',
|
|
99
99
|
params: {
|