@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,191 @@
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 {div, filler, fragment, hbox, span} from '@xh/hoist/cmp/layout';
9
+ import {hoistCmp} from '@xh/hoist/core';
10
+ import {ViewManagerModel, ViewInfo} from '@xh/hoist/cmp/viewmanager';
11
+ import {switchInput} from '@xh/hoist/desktop/cmp/input';
12
+ import {Icon} from '@xh/hoist/icon';
13
+ import {menu, menuDivider, menuItem} from '@xh/hoist/kit/blueprint';
14
+ import {consumeEvent, pluralize} from '@xh/hoist/utils/js';
15
+ import {isEmpty, startCase} from 'lodash';
16
+ import {ReactNode} from 'react';
17
+ import {ViewManagerProps} from './ViewManager';
18
+
19
+ /**
20
+ * Default Menu used by ViewManager.
21
+ */
22
+ export const viewMenu = hoistCmp.factory<ViewManagerProps>({
23
+ render({model, showPrivateViewsInSubMenu, showGlobalViewsInSubMenu}) {
24
+ const {
25
+ enableAutoSave,
26
+ autoSaveUnavailableReason,
27
+ autoSave,
28
+ enableDefault,
29
+ isViewSavable,
30
+ view,
31
+ typeDisplayName,
32
+ globalDisplayName,
33
+ favoriteViews,
34
+ views,
35
+ isValueDirty,
36
+ privateViews,
37
+ globalViews
38
+ } = model;
39
+
40
+ const pluralName = pluralize(startCase(typeDisplayName)),
41
+ myPluralName = `My ${pluralName}`,
42
+ globalPluralName = `${startCase(globalDisplayName)} ${pluralName}`,
43
+ items = [];
44
+ if (!isEmpty(favoriteViews)) {
45
+ items.push(
46
+ menuDivider({title: 'Favorites'}),
47
+ ...favoriteViews.map(info => {
48
+ return menuItem({
49
+ key: `${info.token}-favorite`,
50
+ icon: view.info?.token === info.token ? Icon.check() : Icon.placeholder(),
51
+ text: textAndFaveToggle({info}),
52
+ onClick: () => model.selectViewAsync(info),
53
+ title: info.description
54
+ });
55
+ })
56
+ );
57
+ }
58
+
59
+ if (!isEmpty(privateViews)) {
60
+ const privateItems = privateViews.map(it => buildMenuItem(it, model));
61
+ if (showPrivateViewsInSubMenu) {
62
+ items.push(
63
+ menuDivider({omit: isEmpty(items)}),
64
+ menuItem({
65
+ text: myPluralName,
66
+ shouldDismissPopover: false,
67
+ items: privateItems
68
+ })
69
+ );
70
+ } else {
71
+ items.push(menuDivider({title: myPluralName}), ...privateItems);
72
+ }
73
+ }
74
+
75
+ if (!isEmpty(globalViews)) {
76
+ const globalItems = globalViews.map(it => buildMenuItem(it, model));
77
+ if (showGlobalViewsInSubMenu) {
78
+ items.push(
79
+ menuDivider({omit: isEmpty(items)}),
80
+ menuItem({
81
+ text: globalPluralName,
82
+ shouldDismissPopover: false,
83
+ items: globalItems
84
+ })
85
+ );
86
+ } else {
87
+ items.push(menuDivider({title: globalPluralName}), ...globalItems);
88
+ }
89
+ }
90
+
91
+ return menu({
92
+ className: 'xh-view-manager__menu',
93
+ items: [
94
+ ...items,
95
+ menuDivider({omit: !enableDefault || isEmpty(items)}),
96
+ menuItem({
97
+ icon: view.isDefault ? Icon.check() : Icon.placeholder(),
98
+ text: `Default ${startCase(typeDisplayName)}`,
99
+ omit: !enableDefault,
100
+ onClick: () => model.selectViewAsync(null)
101
+ }),
102
+ menuDivider(),
103
+ menuItem({
104
+ icon: Icon.save(),
105
+ text: 'Save',
106
+ disabled: !isViewSavable || !isValueDirty,
107
+ onClick: () => model.saveAsync()
108
+ }),
109
+ menuItem({
110
+ icon: Icon.placeholder(),
111
+ text: 'Save As...',
112
+ onClick: () => model.saveAsAsync()
113
+ }),
114
+ menuItem({
115
+ icon: Icon.reset(),
116
+ text: `Revert`,
117
+ disabled: !isValueDirty,
118
+ onClick: () => model.resetAsync()
119
+ }),
120
+ menuDivider({omit: !enableAutoSave}),
121
+ menuItem({
122
+ omit: !enableAutoSave,
123
+ text: switchInput({
124
+ label: 'Auto Save',
125
+ value: !autoSaveUnavailableReason && autoSave,
126
+ disabled: !!autoSaveUnavailableReason,
127
+ onChange: v => (model.autoSave = v),
128
+ inline: true
129
+ }),
130
+ title: autoSaveUnavailableReason,
131
+ shouldDismissPopover: false
132
+ }),
133
+ menuDivider(),
134
+ menuItem({
135
+ icon: Icon.gear(),
136
+ disabled: isEmpty(views),
137
+ text: `Manage ${pluralName}...`,
138
+ onClick: () => model.openManageDialog()
139
+ }),
140
+ menuItem({
141
+ icon: Icon.refresh(),
142
+ text: `Refresh ${pluralName}`,
143
+ onClick: e => {
144
+ model.refreshAsync();
145
+ consumeEvent(e);
146
+ }
147
+ })
148
+ ]
149
+ });
150
+ }
151
+ });
152
+
153
+ function buildMenuItem(data: ViewInfo, model: ViewManagerModel): ReactNode {
154
+ const selected = data.token === model.view.token,
155
+ icon = selected ? Icon.check() : Icon.placeholder();
156
+
157
+ return menuItem({
158
+ className: 'xh-view-manager__menu-item',
159
+ key: data.token,
160
+ icon,
161
+ text: textAndFaveToggle({info: data}),
162
+ title: data.description,
163
+ onClick: () => model.selectViewAsync(data)
164
+ });
165
+ }
166
+
167
+ const textAndFaveToggle = hoistCmp.factory<ViewManagerModel>({
168
+ render({model, info}) {
169
+ const {isFavorite, name} = info;
170
+ return hbox({
171
+ alignItems: 'center',
172
+ items: [
173
+ span({style: {paddingRight: 5}, item: name}),
174
+ fragment({
175
+ omit: !model.enableFavorites,
176
+ items: [
177
+ filler(),
178
+ div({
179
+ className: `xh-view-manager__menu-item__fave-toggle ${isFavorite ? 'xh-view-manager__menu-item__fave-toggle--active' : ''}`,
180
+ item: Icon.favorite({prefix: isFavorite ? 'fas' : 'far'}),
181
+ onClick: e => {
182
+ consumeEvent(e);
183
+ model.toggleFavorite(info.token);
184
+ }
185
+ })
186
+ ]
187
+ })
188
+ ]
189
+ });
190
+ }
191
+ });
@@ -0,0 +1,126 @@
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, vframe} from '@xh/hoist/cmp/layout';
10
+
11
+ import {hoistCmp, uses, XH} from '@xh/hoist/core';
12
+ import {EditFormModel} from '@xh/hoist/desktop/cmp/viewmanager/dialog/EditFormModel';
13
+ import {button} from '@xh/hoist/desktop/cmp/button';
14
+ import {formField} from '@xh/hoist/desktop/cmp/form';
15
+ import {select, textArea, textInput} from '@xh/hoist/desktop/cmp/input';
16
+ import {panel} from '@xh/hoist/desktop/cmp/panel';
17
+ import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
18
+ import {fmtCompactDate} from '@xh/hoist/format';
19
+ import {Icon} from '@xh/hoist/icon';
20
+ import {startCase} from 'lodash';
21
+
22
+ /**
23
+ * Default Edit Form for ViewManager
24
+ */
25
+ export const editForm = hoistCmp.factory({
26
+ model: uses(EditFormModel),
27
+ render({model}) {
28
+ const {formModel, view, parent} = model;
29
+ if (!view) return null;
30
+
31
+ const {manageGlobal, globalDisplayName} = parent,
32
+ {lastUpdated, lastUpdatedBy, owner} = view,
33
+ isOwnView = owner === XH.getUsername();
34
+
35
+ return panel({
36
+ item: form({
37
+ fieldDefaults: {
38
+ commitOnChange: true
39
+ },
40
+ item: vframe({
41
+ className: 'xh-view-manager__manage-dialog__form',
42
+ items: [
43
+ formField({
44
+ field: 'name',
45
+ item: textInput()
46
+ }),
47
+ formField({
48
+ field: 'description',
49
+ item: textArea({
50
+ selectOnFocus: true,
51
+ height: 70
52
+ }),
53
+ readonlyRenderer: v =>
54
+ v
55
+ ? v
56
+ : span({
57
+ item: 'None provided',
58
+ className: 'xh-text-color-muted'
59
+ })
60
+ }),
61
+ formField({
62
+ field: 'isGlobal',
63
+ label: 'Visibility',
64
+ item: select({
65
+ options: [
66
+ {value: true, label: startCase(globalDisplayName)},
67
+ {
68
+ value: false,
69
+ label: `Private to ${isOwnView ? 'me' : owner}`
70
+ }
71
+ ],
72
+ enableFilter: false
73
+ }),
74
+ omit: !manageGlobal
75
+ }),
76
+ hbox({
77
+ omit: !model.showSaveButton,
78
+ style: {margin: '10px 20px'},
79
+ items: [
80
+ button({
81
+ text: 'Save Changes',
82
+ icon: Icon.check(),
83
+ intent: 'success',
84
+ minimal: false,
85
+ disabled: !formModel.isValid,
86
+ flex: 1,
87
+ onClick: () => model.saveAsync()
88
+ }),
89
+ hspacer(),
90
+ button({
91
+ icon: Icon.reset(),
92
+ tooltip: 'Revert changes',
93
+ minimal: false,
94
+ onClick: () => formModel.reset()
95
+ })
96
+ ]
97
+ }),
98
+ filler(),
99
+ div({
100
+ className: 'xh-view-manager__manage-dialog__metadata',
101
+ item: `Last Updated: ${fmtCompactDate(lastUpdated)} (${lastUpdatedBy === XH.getUsername() ? 'you' : lastUpdatedBy})`
102
+ })
103
+ ]
104
+ })
105
+ }),
106
+ bbar: bbar()
107
+ });
108
+ }
109
+ });
110
+
111
+ const bbar = hoistCmp.factory<EditFormModel>({
112
+ render({model}) {
113
+ const {parent} = model;
114
+ return toolbar(
115
+ button({
116
+ text: 'Delete',
117
+ icon: Icon.delete(),
118
+ intent: 'danger',
119
+ disabled: !parent.canDelete,
120
+ onClick: () => parent.deleteAsync([model.view])
121
+ }),
122
+ filler(),
123
+ button({text: 'Close', onClick: () => parent.close()})
124
+ );
125
+ }
126
+ });
@@ -0,0 +1,125 @@
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, span, strong} from '@xh/hoist/cmp/layout';
10
+ import {HoistModel, managed, TaskObserver, XH} from '@xh/hoist/core';
11
+ import {ManageDialogModel} from './ManageDialogModel';
12
+ import {makeObservable} from '@xh/hoist/mobx';
13
+ import {throwIf} from '@xh/hoist/utils/js';
14
+ import {ViewInfo} from '@xh/hoist/cmp/viewmanager';
15
+ import {action, observable} from 'mobx';
16
+
17
+ /**
18
+ * Backing model for EditForm
19
+ */
20
+ export class EditFormModel extends HoistModel {
21
+ parent: ManageDialogModel;
22
+
23
+ @managed formModel: FormModel;
24
+ @observable.ref view: ViewInfo;
25
+
26
+ @action
27
+ setView(view: ViewInfo) {
28
+ const {formModel, parent} = this;
29
+ this.view = view;
30
+ if (!view) return null;
31
+ formModel.init(view);
32
+ formModel.readonly = view.isGlobal && !parent.manageGlobal;
33
+ }
34
+
35
+ get loadTask(): TaskObserver {
36
+ return this.parent.loadModel;
37
+ }
38
+
39
+ get showSaveButton(): boolean {
40
+ const {formModel, parent} = this;
41
+ return formModel.isDirty && !formModel.readonly && !parent.loadModel.isPending;
42
+ }
43
+
44
+ constructor(parent: ManageDialogModel) {
45
+ super();
46
+ makeObservable(this);
47
+ this.formModel = this.createFormModel();
48
+ this.parent = parent;
49
+ }
50
+
51
+ async saveAsync() {
52
+ const {parent, view, formModel} = this,
53
+ {manageGlobal, typeDisplayName, globalDisplayName} = parent,
54
+ {name, description, isGlobal} = formModel.getData(),
55
+ isValid = await formModel.validateAsync(),
56
+ isDirty = formModel.isDirty;
57
+
58
+ if (!isValid || !isDirty) return;
59
+
60
+ throwIf(
61
+ (view.isGlobal || isGlobal) && !manageGlobal,
62
+ `Cannot save changes to ${globalDisplayName} ${typeDisplayName} - missing required permission.`
63
+ );
64
+
65
+ if (isGlobal != view.isGlobal) {
66
+ const msgs = [];
67
+ if (isGlobal) {
68
+ msgs.push(
69
+ `This ${typeDisplayName} will become visible to all other ${XH.appName} users.`
70
+ );
71
+ } else {
72
+ msgs.push(
73
+ span(
74
+ `The selected ${typeDisplayName} will revert to being private `,
75
+ strong('It will no longer be available to ALL users.')
76
+ )
77
+ );
78
+ if (view.owner != XH.getUsername()) {
79
+ msgs.push(
80
+ `The selected ${typeDisplayName} will revert to being private to its owner (${view.owner}).`,
81
+ `Note that you will no longer have access to this ${typeDisplayName} and will not be able to undo this change.`
82
+ );
83
+ }
84
+ }
85
+
86
+ msgs.push('Are you sure you want to proceed?');
87
+
88
+ const confirmed = await XH.confirm({
89
+ message: fragment(msgs.map(m => p(m))),
90
+ confirmProps: {
91
+ text: 'Yes, update visibility',
92
+ outlined: true,
93
+ autoFocus: false,
94
+ intent: 'primary'
95
+ }
96
+ });
97
+ if (!confirmed) return;
98
+ }
99
+
100
+ await parent.updateAsync(view, name, description, isGlobal);
101
+ }
102
+
103
+ //------------------------
104
+ // Implementation
105
+ //------------------------
106
+ private createFormModel(): FormModel {
107
+ return new FormModel({
108
+ fields: [
109
+ {
110
+ name: 'name',
111
+ rules: [
112
+ async ({value}) => {
113
+ return this.parent.viewManagerModel.validateViewNameAsync(
114
+ value,
115
+ this.view
116
+ );
117
+ }
118
+ ]
119
+ },
120
+ {name: 'description'},
121
+ {name: 'isGlobal', displayName: 'Global'}
122
+ ]
123
+ });
124
+ }
125
+ }
@@ -0,0 +1,98 @@
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 {tabContainer} from '@xh/hoist/cmp/tab';
9
+ import {filler, hframe, placeholder} from '@xh/hoist/cmp/layout';
10
+ import {storeFilterField} from '@xh/hoist/cmp/store';
11
+ import {creates, hoistCmp} from '@xh/hoist/core';
12
+ import {editForm} from './EditForm';
13
+ import {ManageDialogModel} from './ManageDialogModel';
14
+ import {button} from '@xh/hoist/desktop/cmp/button';
15
+ import {panel} from '@xh/hoist/desktop/cmp/panel';
16
+ import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
17
+ import {Icon} from '@xh/hoist/icon';
18
+ import {dialog} from '@xh/hoist/kit/blueprint';
19
+ import {pluralize} from '@xh/hoist/utils/js';
20
+ import {capitalize} from 'lodash';
21
+
22
+ /**
23
+ * Default management dialog for ViewManager
24
+ */
25
+ export const manageDialog = hoistCmp.factory({
26
+ displayName: 'ManageDialog',
27
+ className: 'xh-view-manager__manage-dialog',
28
+ model: creates(ManageDialogModel),
29
+
30
+ render({model, className}) {
31
+ const {typeDisplayName, updateTask, loadTask, selectedViews} = model;
32
+ const count = selectedViews.length;
33
+ return dialog({
34
+ title: `Manage ${capitalize(pluralize(typeDisplayName))}`,
35
+ icon: Icon.gear(),
36
+ className,
37
+ isOpen: true,
38
+ style: {width: '800px', maxWidth: '90vm', minHeight: '430px'},
39
+ canOutsideClickClose: false,
40
+ onClose: () => model.close(),
41
+ item: panel({
42
+ item: hframe(
43
+ viewPanel(),
44
+ count == 0 ? placeholderPanel() : count > 1 ? multiSelectionPanel() : editForm()
45
+ ),
46
+ mask: [updateTask, loadTask]
47
+ })
48
+ });
49
+ }
50
+ });
51
+
52
+ const viewPanel = hoistCmp.factory<ManageDialogModel>({
53
+ render({model}) {
54
+ return panel({
55
+ modelConfig: {defaultSize: 350, side: 'left', collapsible: false},
56
+ item: tabContainer(),
57
+ bbar: [
58
+ storeFilterField({
59
+ autoApply: false,
60
+ includeFields: ['name'],
61
+ onFilterChange: f => (model.filter = f)
62
+ })
63
+ ]
64
+ });
65
+ }
66
+ });
67
+
68
+ const placeholderPanel = hoistCmp.factory<ManageDialogModel>({
69
+ render({model}) {
70
+ return panel({
71
+ item: placeholder(Icon.gears(), `Select a ${model.typeDisplayName}`),
72
+ bbar: toolbar(filler(), button({text: 'Close', onClick: () => model.close()}))
73
+ });
74
+ }
75
+ });
76
+
77
+ const multiSelectionPanel = hoistCmp.factory<ManageDialogModel>({
78
+ render({model}) {
79
+ const {selectedViews} = model;
80
+ return panel({
81
+ item: placeholder(
82
+ Icon.gears(),
83
+ `${selectedViews.length} selected ${pluralize(model.typeDisplayName)}`
84
+ ),
85
+ bbar: toolbar(
86
+ button({
87
+ text: 'Delete',
88
+ icon: Icon.delete(),
89
+ intent: 'danger',
90
+ disabled: !model.canDelete,
91
+ onClick: () => model.deleteAsync(selectedViews)
92
+ }),
93
+ filler(),
94
+ button({text: 'Close', onClick: () => model.close()})
95
+ )
96
+ });
97
+ }
98
+ });