@xh/hoist 70.0.0-SNAPSHOT.1731083521069 → 70.0.0-SNAPSHOT.1731374612473
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 +54 -0
- package/build/types/cmp/filter/FilterChooserModel.d.ts +17 -12
- package/build/types/cmp/grid/GridModel.d.ts +5 -9
- package/build/types/cmp/grid/Types.d.ts +7 -19
- package/build/types/cmp/grid/columns/Column.d.ts +0 -1
- package/build/types/cmp/grid/impl/InitPersist.d.ts +7 -0
- package/build/types/cmp/grouping/GroupingChooserModel.d.ts +6 -8
- package/build/types/cmp/tab/TabContainerModel.d.ts +10 -4
- package/build/types/cmp/zoneGrid/Types.d.ts +6 -6
- package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +0 -2
- package/build/types/cmp/zoneGrid/impl/InitPersist.d.ts +7 -0
- package/build/types/core/HoistBase.d.ts +1 -1
- package/build/types/core/persist/CustomProvider.d.ts +5 -6
- package/build/types/core/persist/DashViewProvider.d.ts +6 -6
- package/build/types/core/persist/LocalStorageProvider.d.ts +4 -5
- package/build/types/core/persist/PersistOptions.d.ts +5 -4
- package/build/types/core/persist/Persistable.d.ts +14 -0
- package/build/types/core/persist/PersistenceProvider.d.ts +47 -34
- package/build/types/core/persist/PrefProvider.d.ts +5 -5
- package/build/types/core/persist/index.d.ts +2 -0
- package/build/types/core/persist/viewmanager/Types.d.ts +46 -0
- package/build/types/core/persist/viewmanager/ViewManagerModel.d.ts +149 -0
- package/build/types/core/persist/viewmanager/ViewManagerProvider.d.ts +10 -0
- package/build/types/core/persist/viewmanager/impl/ManageDialogModel.d.ts +30 -0
- package/build/types/core/persist/viewmanager/impl/SaveDialogModel.d.ts +23 -0
- package/build/types/core/persist/viewmanager/index.d.ts +2 -0
- package/build/types/desktop/cmp/button/ColAutosizeButton.d.ts +1 -1
- package/build/types/desktop/cmp/dash/DashConfig.d.ts +3 -1
- package/build/types/desktop/cmp/dash/DashModel.d.ts +1 -2
- package/build/types/desktop/cmp/dash/DashViewSpec.d.ts +1 -1
- package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +10 -2
- package/build/types/desktop/cmp/dash/container/DashContainerModel.d.ts +26 -10
- package/build/types/desktop/cmp/dash/container/impl/DashContainerUtils.d.ts +4 -2
- package/build/types/desktop/cmp/panel/PanelModel.d.ts +8 -4
- package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +22 -0
- package/build/types/desktop/cmp/viewmanager/cmp/ManageDialog.d.ts +6 -0
- package/build/types/desktop/cmp/viewmanager/cmp/SaveDialog.d.ts +2 -0
- package/build/types/desktop/cmp/viewmanager/index.d.ts +3 -0
- package/build/types/kit/blueprint/Wrappers.d.ts +1 -1
- package/build/types/mobile/cmp/button/ColAutosizeButton.d.ts +1 -1
- package/build/types/svc/GridAutosizeService.d.ts +2 -5
- package/build/types/svc/JsonBlobService.d.ts +45 -24
- package/cmp/filter/FilterChooserModel.ts +142 -125
- package/cmp/grid/Grid.ts +2 -10
- package/cmp/grid/GridModel.ts +18 -31
- package/cmp/grid/Types.ts +7 -21
- package/cmp/grid/columns/Column.ts +0 -1
- package/cmp/grid/impl/InitPersist.ts +71 -0
- package/cmp/grouping/GroupingChooserModel.ts +48 -57
- package/cmp/tab/TabContainerModel.ts +22 -36
- package/cmp/zoneGrid/Types.ts +6 -6
- package/cmp/zoneGrid/ZoneGridModel.ts +2 -7
- package/cmp/zoneGrid/impl/InitPersist.ts +70 -0
- package/core/HoistBase.ts +14 -22
- package/core/HoistBaseDecorators.ts +26 -28
- package/core/persist/CustomProvider.ts +7 -10
- package/core/persist/DashViewProvider.ts +8 -10
- package/core/persist/LocalStorageProvider.ts +9 -12
- package/core/persist/PersistOptions.ts +6 -4
- package/core/persist/Persistable.ts +23 -0
- package/core/persist/PersistenceProvider.ts +159 -79
- package/core/persist/PrefProvider.ts +9 -12
- package/core/persist/index.ts +2 -0
- package/core/persist/viewmanager/Types.ts +51 -0
- package/core/persist/viewmanager/ViewManagerModel.ts +515 -0
- package/core/persist/viewmanager/ViewManagerProvider.ts +51 -0
- package/core/persist/viewmanager/impl/ManageDialogModel.ts +274 -0
- package/core/persist/viewmanager/impl/SaveDialogModel.ts +112 -0
- package/core/persist/viewmanager/index.ts +2 -0
- package/desktop/cmp/button/ColAutosizeButton.ts +1 -1
- package/desktop/cmp/dash/DashConfig.ts +3 -1
- package/desktop/cmp/dash/DashModel.ts +1 -2
- package/desktop/cmp/dash/DashViewSpec.ts +1 -1
- package/desktop/cmp/dash/canvas/DashCanvasModel.ts +31 -30
- package/desktop/cmp/dash/container/DashContainerModel.ts +68 -43
- package/desktop/cmp/dash/container/impl/DashContainerUtils.ts +13 -4
- package/desktop/cmp/leftrightchooser/LeftRightChooserFilter.ts +1 -1
- package/desktop/cmp/panel/PanelModel.ts +33 -53
- package/desktop/cmp/store/impl/StoreFilterField.ts +1 -1
- package/desktop/cmp/viewmanager/ViewManager.scss +58 -0
- package/desktop/cmp/viewmanager/ViewManager.ts +274 -0
- package/desktop/cmp/viewmanager/cmp/ManageDialog.ts +197 -0
- package/desktop/cmp/viewmanager/cmp/SaveDialog.ts +89 -0
- package/desktop/cmp/viewmanager/index.ts +3 -0
- package/mobile/cmp/button/ColAutosizeButton.ts +1 -1
- package/package.json +1 -1
- package/svc/GridAutosizeService.ts +73 -36
- package/svc/JsonBlobService.ts +64 -31
- package/tsconfig.tsbuildinfo +1 -1
- package/build/types/cmp/grid/impl/GridPersistenceModel.d.ts +0 -41
- package/build/types/cmp/zoneGrid/impl/ZoneGridPersistenceModel.d.ts +0 -39
- package/cmp/grid/impl/GridPersistenceModel.ts +0 -174
- package/cmp/zoneGrid/impl/ZoneGridPersistenceModel.ts +0 -149
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import {FormModel} from '@xh/hoist/cmp/form';
|
|
2
|
+
import {GridAutosizeMode, GridModel} from '@xh/hoist/cmp/grid';
|
|
3
|
+
import {fragment, p} from '@xh/hoist/cmp/layout';
|
|
4
|
+
import {HoistModel, lookup, managed, TaskObserver, XH} from '@xh/hoist/core';
|
|
5
|
+
import {lengthIs, required} from '@xh/hoist/data';
|
|
6
|
+
import {Icon} from '@xh/hoist/icon';
|
|
7
|
+
import {makeObservable} from '@xh/hoist/mobx';
|
|
8
|
+
import {pluralize, throwIf} from '@xh/hoist/utils/js';
|
|
9
|
+
import {ViewManagerModel} from '../ViewManagerModel';
|
|
10
|
+
|
|
11
|
+
export class ManageDialogModel extends HoistModel {
|
|
12
|
+
@lookup(() => ViewManagerModel)
|
|
13
|
+
private viewManagerModel: ViewManagerModel;
|
|
14
|
+
|
|
15
|
+
@managed gridModel: GridModel;
|
|
16
|
+
@managed formModel: FormModel;
|
|
17
|
+
|
|
18
|
+
readonly saveTask = TaskObserver.trackLast();
|
|
19
|
+
readonly deleteTask = TaskObserver.trackLast();
|
|
20
|
+
|
|
21
|
+
get selectedId(): string {
|
|
22
|
+
return this.gridModel.selectedId as string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get selectedIds(): string[] {
|
|
26
|
+
return this.gridModel.selectedIds as string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get hasMultiSelection(): boolean {
|
|
30
|
+
return this.selectedIds.length > 1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get selIsShared(): boolean {
|
|
34
|
+
return this.gridModel.selectedRecords.some(rec => rec.data.isShared);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get canDelete(): boolean {
|
|
38
|
+
const {viewManagerModel, selIsShared, enableSharing, selectedIds} = this,
|
|
39
|
+
{views, enableDefault} = viewManagerModel;
|
|
40
|
+
|
|
41
|
+
// Can't delete shared views without manager role.
|
|
42
|
+
if (selIsShared && !enableSharing) return false;
|
|
43
|
+
|
|
44
|
+
// Can't delete all the views, unless default mode is enabled.
|
|
45
|
+
return enableDefault || views.length - selectedIds.length > 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get canEdit(): boolean {
|
|
49
|
+
return this.enableSharing || !this.selIsShared;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get enableSharing(): boolean {
|
|
53
|
+
return this.viewManagerModel.enableSharing;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get showSaveButton(): boolean {
|
|
57
|
+
const {formModel, viewManagerModel} = this;
|
|
58
|
+
return formModel.isDirty && !formModel.readonly && !viewManagerModel.loadModel.isPending;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get displayName(): string {
|
|
62
|
+
return this.viewManagerModel.displayName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get enableFavorites(): boolean {
|
|
66
|
+
return this.viewManagerModel.enableFavorites;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
constructor() {
|
|
70
|
+
super();
|
|
71
|
+
makeObservable(this);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override onLinked() {
|
|
75
|
+
super.onLinked();
|
|
76
|
+
|
|
77
|
+
this.gridModel = this.createGridModel();
|
|
78
|
+
this.formModel = this.createFormModel();
|
|
79
|
+
|
|
80
|
+
this.addReaction(
|
|
81
|
+
{
|
|
82
|
+
track: () => this.viewManagerModel.views,
|
|
83
|
+
run: () => this.refreshAsync()
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
track: () => this.gridModel.selectedRecord,
|
|
87
|
+
run: record => {
|
|
88
|
+
if (record) {
|
|
89
|
+
this.formModel.readonly = !this.canEdit;
|
|
90
|
+
this.formModel.init(record.data);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
override async doLoadAsync() {
|
|
98
|
+
this.gridModel.loadData(this.viewManagerModel.views);
|
|
99
|
+
await this.ensureGridHasSelection();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async saveAsync() {
|
|
103
|
+
return this.doSaveAsync().linkTo(this.saveTask).catchDefault();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async deleteAsync() {
|
|
107
|
+
return this.doDeleteAsync().linkTo(this.deleteTask).catchDefault();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//------------------------
|
|
111
|
+
// Implementation
|
|
112
|
+
//------------------------
|
|
113
|
+
private async doSaveAsync() {
|
|
114
|
+
const {formModel, viewManagerModel, enableSharing, selectedId, gridModel, displayName} =
|
|
115
|
+
this,
|
|
116
|
+
{isDirty} = formModel,
|
|
117
|
+
{name, description, isShared} = formModel.getData(),
|
|
118
|
+
isValid = await formModel.validateAsync(),
|
|
119
|
+
{token, owner} = gridModel.selectedRecord.data,
|
|
120
|
+
isOwnView = owner === XH.getUsername();
|
|
121
|
+
|
|
122
|
+
if (!isValid || !selectedId || !isDirty) return;
|
|
123
|
+
|
|
124
|
+
// Additional sanity-check before POSTing an update - non-admins should never be modifying global views.
|
|
125
|
+
throwIf(
|
|
126
|
+
isShared && !enableSharing,
|
|
127
|
+
`Cannot save changes to shared ${displayName} - missing required permission.`
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (formModel.getField('isShared').isDirty) {
|
|
131
|
+
const confirmMsgs = [];
|
|
132
|
+
if (isShared) {
|
|
133
|
+
confirmMsgs.push(
|
|
134
|
+
`This ${displayName} will become visible to all other ${XH.appName} users.`
|
|
135
|
+
);
|
|
136
|
+
} else if (isOwnView) {
|
|
137
|
+
confirmMsgs.push(
|
|
138
|
+
`The selected ${displayName} will revert to being private to you. It will no longer be available to other users.`
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
confirmMsgs.push(
|
|
142
|
+
`The selected ${displayName} will revert to being private to its owner (${owner}).`,
|
|
143
|
+
`Note that you will no longer have access to this view, meaning you will not be able to undo this change.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
confirmMsgs.push('Are you sure you want to proceed?');
|
|
148
|
+
|
|
149
|
+
const confirmed = await XH.confirm({
|
|
150
|
+
message: fragment(confirmMsgs.map(msg => p(msg))),
|
|
151
|
+
confirmProps: {
|
|
152
|
+
text: 'Yes, update visibility',
|
|
153
|
+
outlined: true,
|
|
154
|
+
autoFocus: false,
|
|
155
|
+
intent: 'primary'
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (!confirmed) return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await XH.jsonBlobService.updateAsync(token, {
|
|
163
|
+
name,
|
|
164
|
+
description,
|
|
165
|
+
acl: isShared ? '*' : null
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
await viewManagerModel.refreshAsync();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async doDeleteAsync() {
|
|
172
|
+
const {viewManagerModel, gridModel, displayName, selectedIds, hasMultiSelection} = this,
|
|
173
|
+
count = selectedIds.length;
|
|
174
|
+
if (!count) return;
|
|
175
|
+
|
|
176
|
+
const confirmStr = hasMultiSelection
|
|
177
|
+
? pluralize(displayName, count, true)
|
|
178
|
+
: `"${gridModel.selectedRecord.data.name}"`,
|
|
179
|
+
confirmed = await XH.confirm({
|
|
180
|
+
message: `Are you sure you want to delete ${confirmStr}?`,
|
|
181
|
+
confirmProps: {
|
|
182
|
+
text: `Yes, delete ${pluralize(displayName, count)}`,
|
|
183
|
+
outlined: true,
|
|
184
|
+
autoFocus: false,
|
|
185
|
+
intent: 'danger'
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
if (!confirmed) return;
|
|
189
|
+
|
|
190
|
+
for (const token of selectedIds) {
|
|
191
|
+
viewManagerModel.removeFavorite(token);
|
|
192
|
+
await XH.jsonBlobService.archiveAsync(token);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await viewManagerModel.refreshAsync();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async ensureGridHasSelection() {
|
|
199
|
+
const {gridModel, viewManagerModel} = this,
|
|
200
|
+
{selectedToken} = viewManagerModel;
|
|
201
|
+
|
|
202
|
+
if (!gridModel.hasSelection) {
|
|
203
|
+
selectedToken
|
|
204
|
+
? await gridModel.selectAsync(selectedToken)
|
|
205
|
+
: await gridModel.preSelectFirstAsync();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private createGridModel(): GridModel {
|
|
210
|
+
return new GridModel({
|
|
211
|
+
emptyText: `No saved ${pluralize(this.displayName)} found...`,
|
|
212
|
+
sortBy: 'name',
|
|
213
|
+
groupBy: 'group',
|
|
214
|
+
hideHeaders: true,
|
|
215
|
+
showGroupRowCounts: false,
|
|
216
|
+
selModel: 'multiple',
|
|
217
|
+
store: {
|
|
218
|
+
idSpec: 'token',
|
|
219
|
+
fields: [
|
|
220
|
+
{name: 'token', type: 'string'},
|
|
221
|
+
{name: 'name', type: 'string'},
|
|
222
|
+
{name: 'description', type: 'string'},
|
|
223
|
+
{name: 'isShared', type: 'bool'},
|
|
224
|
+
{name: 'isFavorite', type: 'bool'},
|
|
225
|
+
{name: 'acl', type: 'json'},
|
|
226
|
+
{name: 'meta', type: 'json'},
|
|
227
|
+
{name: 'dateCreated', type: 'date'},
|
|
228
|
+
{name: 'createdBy', type: 'string'},
|
|
229
|
+
{name: 'owner', type: 'string'},
|
|
230
|
+
{name: 'lastUpdatedBy', type: 'string'},
|
|
231
|
+
{name: 'lastUpdated', type: 'date'}
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
autosizeOptions: {mode: GridAutosizeMode.DISABLED},
|
|
235
|
+
columns: [
|
|
236
|
+
{field: 'name', flex: true},
|
|
237
|
+
{
|
|
238
|
+
field: 'isFavorite',
|
|
239
|
+
omit: !this.enableFavorites,
|
|
240
|
+
width: 40,
|
|
241
|
+
align: 'center',
|
|
242
|
+
headerName: Icon.favorite(),
|
|
243
|
+
highlightOnChange: true,
|
|
244
|
+
renderer: v => {
|
|
245
|
+
return Icon.favorite({
|
|
246
|
+
prefix: v ? 'fas' : 'fal',
|
|
247
|
+
className: v ? 'xh-yellow' : 'xh-text-color-muted'
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
{field: 'group', hidden: true}
|
|
252
|
+
],
|
|
253
|
+
onCellClicked: ({colDef, data: record}) => {
|
|
254
|
+
if (colDef.colId === 'isFavorite') {
|
|
255
|
+
this.viewManagerModel.toggleFavorite(record.id);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private createFormModel(): FormModel {
|
|
262
|
+
return new FormModel({
|
|
263
|
+
fields: [
|
|
264
|
+
{name: 'name', rules: [required, lengthIs({max: 255})]},
|
|
265
|
+
{name: 'description'},
|
|
266
|
+
{name: 'isShared', displayName: 'Shared'},
|
|
267
|
+
{name: 'owner', readonly: true},
|
|
268
|
+
{name: 'dateCreated', displayName: 'Created', readonly: true},
|
|
269
|
+
{name: 'lastUpdated', displayName: 'Updated', readonly: true},
|
|
270
|
+
{name: 'lastUpdatedBy', displayName: 'Updated By', readonly: true}
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {FormModel} from '@xh/hoist/cmp/form';
|
|
2
|
+
import {HoistModel, managed, TaskObserver, XH} from '@xh/hoist/core';
|
|
3
|
+
import {ViewManagerModel} from '@xh/hoist/core/persist/viewmanager';
|
|
4
|
+
import {lengthIs, required} from '@xh/hoist/data';
|
|
5
|
+
import {makeObservable} from '@xh/hoist/mobx';
|
|
6
|
+
import {JsonBlob} from '@xh/hoist/svc';
|
|
7
|
+
import {action, observable} from 'mobx';
|
|
8
|
+
import {View} from '../Types';
|
|
9
|
+
|
|
10
|
+
export class SaveDialogModel extends HoistModel {
|
|
11
|
+
private readonly viewManagerModel: ViewManagerModel;
|
|
12
|
+
|
|
13
|
+
@managed readonly formModel: FormModel;
|
|
14
|
+
readonly saveTask = TaskObserver.trackLast();
|
|
15
|
+
|
|
16
|
+
@observable viewStub: Partial<View>;
|
|
17
|
+
@observable isOpen: boolean = false;
|
|
18
|
+
|
|
19
|
+
private resolveOpen: (value: JsonBlob) => void;
|
|
20
|
+
private invalidNames: string[] = [];
|
|
21
|
+
|
|
22
|
+
get type(): string {
|
|
23
|
+
return this.viewManagerModel.viewType;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get DisplayName(): string {
|
|
27
|
+
return this.viewManagerModel.DisplayName;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
constructor(viewManagerModel: ViewManagerModel) {
|
|
31
|
+
super();
|
|
32
|
+
makeObservable(this);
|
|
33
|
+
this.viewManagerModel = viewManagerModel;
|
|
34
|
+
this.formModel = this.createFormModel();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@action
|
|
38
|
+
openAsync(viewStub: Partial<View>, invalidNames: string[]): Promise<JsonBlob> {
|
|
39
|
+
this.viewStub = viewStub;
|
|
40
|
+
this.invalidNames = invalidNames;
|
|
41
|
+
|
|
42
|
+
this.formModel.init({
|
|
43
|
+
name: viewStub.name ? `${viewStub.name} (COPY)` : '',
|
|
44
|
+
description: viewStub.description
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
this.isOpen = true;
|
|
48
|
+
|
|
49
|
+
return new Promise(resolve => {
|
|
50
|
+
this.resolveOpen = resolve;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
cancel() {
|
|
55
|
+
this.close();
|
|
56
|
+
this.resolveOpen(null);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async saveAsAsync() {
|
|
60
|
+
return this.doSaveAsAsync().linkTo(this.saveTask);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//------------------------
|
|
64
|
+
// Implementation
|
|
65
|
+
//------------------------
|
|
66
|
+
private createFormModel(): FormModel {
|
|
67
|
+
return new FormModel({
|
|
68
|
+
fields: [
|
|
69
|
+
{
|
|
70
|
+
name: 'name',
|
|
71
|
+
rules: [
|
|
72
|
+
required,
|
|
73
|
+
lengthIs({max: 255}),
|
|
74
|
+
({value}) => {
|
|
75
|
+
if (this.invalidNames.includes(value)) {
|
|
76
|
+
return `An entry with name "${value}" already exists`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{name: 'description'}
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async doSaveAsAsync() {
|
|
87
|
+
const {formModel, viewStub, type} = this,
|
|
88
|
+
{name, description} = formModel.getData(),
|
|
89
|
+
isValid = await formModel.validateAsync();
|
|
90
|
+
|
|
91
|
+
if (!isValid) return;
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const newObj = await XH.jsonBlobService.createAsync({
|
|
95
|
+
type,
|
|
96
|
+
name,
|
|
97
|
+
description,
|
|
98
|
+
value: viewStub.value
|
|
99
|
+
});
|
|
100
|
+
this.close();
|
|
101
|
+
this.resolveOpen(newObj);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
XH.handleException(e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@action
|
|
108
|
+
private close() {
|
|
109
|
+
this.isOpen = false;
|
|
110
|
+
this.formModel.init();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {MenuItemLike, PersistOptions} from '@xh/hoist/core';
|
|
9
|
+
import {DashViewState} from '@xh/hoist/desktop/cmp/dash/DashViewModel';
|
|
10
|
+
import {DashViewSpec} from '@xh/hoist/desktop/cmp/dash/DashViewSpec';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Base interface for {@link DashCanvasConfig} and {@link DashContainerConfig}.
|
|
12
14
|
*/
|
|
13
|
-
export interface DashConfig<VSPEC, VSTATE> {
|
|
15
|
+
export interface DashConfig<VSPEC extends DashViewSpec, VSTATE extends DashViewState> {
|
|
14
16
|
/**
|
|
15
17
|
* A collection of viewSpecs, each describing a type of view that can be displayed in this
|
|
16
18
|
* container.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
8
|
-
import {HoistModel, managed,
|
|
8
|
+
import {HoistModel, managed, RefreshContextModel} from '@xh/hoist/core';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Base Model for {@link DashCanvasModel} and {@link DashContainerModel}.
|
|
@@ -40,7 +40,6 @@ export abstract class DashModel<VSPEC, VSTATE, VMODEL> extends HoistModel {
|
|
|
40
40
|
// Implementation properties
|
|
41
41
|
//------------------------
|
|
42
42
|
protected restoreState: any;
|
|
43
|
-
protected provider: PersistenceProvider;
|
|
44
43
|
|
|
45
44
|
constructor() {
|
|
46
45
|
super();
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {PersistenceProvider, XH} from '@xh/hoist/core';
|
|
7
|
+
import {Persistable, PersistableState, PersistenceProvider, XH} from '@xh/hoist/core';
|
|
8
8
|
import {required} from '@xh/hoist/data';
|
|
9
9
|
import {DashCanvasViewModel, DashCanvasViewSpec, DashConfig, DashViewState, DashModel} from '../';
|
|
10
10
|
import '@xh/hoist/desktop/register';
|
|
@@ -64,11 +64,10 @@ export interface DashCanvasItemLayout {
|
|
|
64
64
|
* Model for {@link DashCanvas}, managing all configurable options for the component and publishing
|
|
65
65
|
* the observable state of its current widgets and their layout.
|
|
66
66
|
*/
|
|
67
|
-
export class DashCanvasModel
|
|
68
|
-
DashCanvasViewSpec,
|
|
69
|
-
DashCanvasItemState
|
|
70
|
-
|
|
71
|
-
> {
|
|
67
|
+
export class DashCanvasModel
|
|
68
|
+
extends DashModel<DashCanvasViewSpec, DashCanvasItemState, DashCanvasViewModel>
|
|
69
|
+
implements Persistable<{state: DashCanvasItemState[]}>
|
|
70
|
+
{
|
|
72
71
|
//-----------------------------
|
|
73
72
|
// Settable State
|
|
74
73
|
//------------------------------
|
|
@@ -182,26 +181,23 @@ export class DashCanvasModel extends DashModel<
|
|
|
182
181
|
this.addViewButtonText = addViewButtonText;
|
|
183
182
|
this.extraMenuItems = extraMenuItems;
|
|
184
183
|
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
this.loadState(initialState);
|
|
185
|
+
this.state = this.buildState();
|
|
186
|
+
|
|
187
187
|
if (persistWith) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
188
|
+
PersistenceProvider.create({
|
|
189
|
+
persistOptions: {
|
|
190
|
+
path: 'dashCanvas',
|
|
191
|
+
...persistWith
|
|
192
|
+
},
|
|
193
|
+
target: this
|
|
194
|
+
});
|
|
196
195
|
}
|
|
197
196
|
|
|
198
|
-
this.loadState(persistState?.state ?? initialState);
|
|
199
|
-
this.state = this.buildState();
|
|
200
|
-
|
|
201
197
|
this.addReaction(
|
|
202
198
|
{
|
|
203
199
|
track: () => this.viewState,
|
|
204
|
-
run: () => this.
|
|
200
|
+
run: () => (this.state = this.buildState())
|
|
205
201
|
},
|
|
206
202
|
{
|
|
207
203
|
when: () => !!this.ref.current,
|
|
@@ -238,7 +234,6 @@ export class DashCanvasModel extends DashModel<
|
|
|
238
234
|
this.columns = restoreState.columns;
|
|
239
235
|
this.rowHeight = restoreState.rowHeight;
|
|
240
236
|
this.loadState(restoreState.initialState);
|
|
241
|
-
this.provider?.clear();
|
|
242
237
|
}
|
|
243
238
|
|
|
244
239
|
/**
|
|
@@ -318,6 +313,18 @@ export class DashCanvasModel extends DashModel<
|
|
|
318
313
|
this.getView(id)?.ensureVisible();
|
|
319
314
|
}
|
|
320
315
|
|
|
316
|
+
//------------------------
|
|
317
|
+
// Persistable Interface
|
|
318
|
+
//------------------------
|
|
319
|
+
getPersistableState(): PersistableState<{state: DashCanvasItemState[]}> {
|
|
320
|
+
return new PersistableState({state: this.state});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
setPersistableState(persistableState: PersistableState<{state: DashCanvasItemState[]}>) {
|
|
324
|
+
const {state} = persistableState.value;
|
|
325
|
+
if (state) this.loadState(state);
|
|
326
|
+
}
|
|
327
|
+
|
|
321
328
|
//------------------------
|
|
322
329
|
// Implementation
|
|
323
330
|
//------------------------
|
|
@@ -396,7 +403,7 @@ export class DashCanvasModel extends DashModel<
|
|
|
396
403
|
if (!layoutChanged) return;
|
|
397
404
|
|
|
398
405
|
this.layout = layout;
|
|
399
|
-
if (!this.isLoadingState) this.
|
|
406
|
+
if (!this.isLoadingState) this.state = this.buildState();
|
|
400
407
|
|
|
401
408
|
// Check if scrollbar visibility has changed, and force resize event if so
|
|
402
409
|
const node = this.ref.current;
|
|
@@ -409,7 +416,7 @@ export class DashCanvasModel extends DashModel<
|
|
|
409
416
|
}
|
|
410
417
|
|
|
411
418
|
@action
|
|
412
|
-
private loadState(state) {
|
|
419
|
+
private loadState(state: DashCanvasItemState[]) {
|
|
413
420
|
this.isLoadingState = true;
|
|
414
421
|
try {
|
|
415
422
|
this.clear();
|
|
@@ -427,13 +434,7 @@ export class DashCanvasModel extends DashModel<
|
|
|
427
434
|
}
|
|
428
435
|
}
|
|
429
436
|
|
|
430
|
-
|
|
431
|
-
private publishState() {
|
|
432
|
-
this.state = this.buildState();
|
|
433
|
-
this.provider?.write({state: this.state});
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
private buildState() {
|
|
437
|
+
private buildState(): DashCanvasItemState[] {
|
|
437
438
|
const {viewState} = this;
|
|
438
439
|
|
|
439
440
|
return this.layout.map(it => {
|