@xh/hoist 74.1.1 → 75.0.0-SNAPSHOT.1749666833237

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 (33) hide show
  1. package/CHANGELOG.md +5 -35
  2. package/build/types/cmp/grouping/GroupingChooserModel.d.ts +4 -8
  3. package/build/types/cmp/viewmanager/ViewInfo.d.ts +3 -7
  4. package/build/types/core/types/Interfaces.d.ts +1 -1
  5. package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +6 -17
  6. package/build/types/desktop/cmp/panel/Panel.d.ts +1 -1
  7. package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +10 -26
  8. package/build/types/mobile/cmp/grouping/GroupingChooser.d.ts +6 -2
  9. package/build/types/utils/impl/index.d.ts +0 -1
  10. package/cmp/grouping/GroupingChooserModel.ts +12 -25
  11. package/cmp/viewmanager/ViewInfo.ts +3 -7
  12. package/core/HoistAppModel.ts +0 -1
  13. package/core/exception/ExceptionHandler.ts +1 -1
  14. package/core/types/Interfaces.ts +2 -2
  15. package/desktop/cmp/button/AppMenuButton.ts +46 -3
  16. package/desktop/cmp/grouping/GroupingChooser.scss +35 -39
  17. package/desktop/cmp/grouping/GroupingChooser.ts +89 -157
  18. package/desktop/cmp/panel/Panel.ts +1 -1
  19. package/desktop/cmp/viewmanager/ViewManager.ts +16 -58
  20. package/desktop/cmp/viewmanager/ViewMenu.ts +2 -9
  21. package/mobile/appcontainer/FeedbackDialog.ts +0 -4
  22. package/mobile/appcontainer/OptionsDialog.ts +1 -3
  23. package/mobile/cmp/grid/impl/ColChooser.ts +2 -3
  24. package/mobile/cmp/grouping/GroupingChooser.scss +20 -41
  25. package/mobile/cmp/grouping/GroupingChooser.ts +89 -60
  26. package/mobile/cmp/panel/DialogPanel.scss +0 -5
  27. package/package.json +1 -1
  28. package/svc/TrackService.ts +3 -4
  29. package/tsconfig.tsbuildinfo +1 -1
  30. package/utils/impl/index.ts +0 -1
  31. package/utils/js/LangUtils.ts +1 -1
  32. package/build/types/utils/impl/MenuItems.d.ts +0 -13
  33. package/utils/impl/MenuItems.ts +0 -57
@@ -5,66 +5,43 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
8
- import {
9
- box,
10
- div,
11
- filler,
12
- fragment,
13
- hbox,
14
- hframe,
15
- placeholder,
16
- vbox,
17
- vframe
18
- } from '@xh/hoist/cmp/layout';
19
- import {hoistCmp, Side, uses} from '@xh/hoist/core';
8
+ import {box, div, filler, fragment, hbox, vbox} from '@xh/hoist/cmp/layout';
9
+ import {hoistCmp, uses} from '@xh/hoist/core';
20
10
  import {button, ButtonProps} from '@xh/hoist/desktop/cmp/button';
21
11
  import {select} from '@xh/hoist/desktop/cmp/input';
22
12
  import {panel} from '@xh/hoist/desktop/cmp/panel';
23
13
  import '@xh/hoist/desktop/register';
24
- import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
25
14
  import {Icon} from '@xh/hoist/icon';
26
- import {menu, menuItem, popover} from '@xh/hoist/kit/blueprint';
15
+ import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
27
16
  import {dragDropContext, draggable, droppable} from '@xh/hoist/kit/react-beautiful-dnd';
28
- import {apiDeprecated, elemWithin, getTestId} from '@xh/hoist/utils/js';
17
+ import {elemWithin, getTestId, TEST_ID} from '@xh/hoist/utils/js';
29
18
  import {splitLayoutProps} from '@xh/hoist/utils/react';
19
+ import {ReactElement} from 'react';
30
20
  import classNames from 'classnames';
31
- import {compact, isEmpty, isNil, isUndefined, sortBy} from 'lodash';
21
+ import {compact, isEmpty, sortBy} from 'lodash';
32
22
  import './GroupingChooser.scss';
33
- import {ReactNode} from 'react';
34
23
 
35
24
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
36
- /** Title for value-editing portion of popover, or null to suppress. */
37
- editorTitle?: ReactNode;
38
-
39
25
  /** Text to represent empty state (i.e. value = null or []) */
40
26
  emptyText?: string;
41
27
 
42
- /**
43
- * Side of the popover, relative to the value-editing controls, on which the Favorites list
44
- * should be rendered, if enabled.
45
- */
46
- favoritesSide?: Side;
47
-
48
- /** Title for favorites-list portion of popover, or null to suppress. */
49
- favoritesTitle?: ReactNode;
50
-
51
28
  /** Min height in pixels of the popover menu itself. */
52
29
  popoverMinHeight?: number;
53
30
 
54
31
  /** Position of popover relative to target button. */
55
32
  popoverPosition?: 'bottom' | 'top';
56
33
 
57
- /** @deprecated - use `editorTitle` instead */
58
- popoverTitle?: ReactNode;
34
+ /** Title for popover (default "GROUP BY") or null to suppress. */
35
+ popoverTitle?: string;
59
36
 
60
- /**
61
- * Width in pixels of the popover menu itself.
62
- * If unspecified, will default based on favorites enabled status + side.
63
- */
37
+ /** Width in pixels of the popover menu itself. */
64
38
  popoverWidth?: number;
65
39
 
66
40
  /** True (default) to style target button as an input field - blends better in toolbars. */
67
41
  styleButtonAsInput?: boolean;
42
+
43
+ /** Icon clicked to launch favorites menu. Defaults to Icon.favorite() */
44
+ favoritesIcon?: ReactElement;
68
45
  }
69
46
 
70
47
  /**
@@ -82,35 +59,25 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
82
59
  {
83
60
  model,
84
61
  className,
85
- editorTitle = 'Group By',
86
62
  emptyText = 'Ungrouped',
87
- favoritesSide = 'right',
88
- favoritesTitle = 'Favorites',
89
- popoverWidth,
63
+ popoverWidth = 250,
90
64
  popoverMinHeight,
91
- popoverTitle,
65
+ popoverTitle = 'Group By',
92
66
  popoverPosition = 'bottom',
93
67
  styleButtonAsInput = true,
94
68
  testId,
69
+ favoritesIcon,
95
70
  ...rest
96
71
  },
97
72
  ref
98
73
  ) {
99
- const {editorIsOpen, value, allowEmpty, persistFavorites} = model,
100
- isOpen = editorIsOpen,
74
+ const {editorIsOpen, favoritesIsOpen, persistFavorites, value, allowEmpty} = model,
75
+ isOpen = editorIsOpen || favoritesIsOpen,
101
76
  label = isEmpty(value) && allowEmpty ? emptyText : model.getValueLabel(value),
102
77
  [layoutProps, buttonProps] = splitLayoutProps(rest),
103
- favesClassNameMod = `faves-${persistFavorites ? favoritesSide : 'disabled'}`,
104
- favesTB = isTB(favoritesSide);
105
-
106
- if (!isUndefined(popoverTitle)) {
107
- apiDeprecated('GroupingChooser.popoverTitle', {
108
- msg: `Update to use 'editorTitle' instead`
109
- });
110
- editorTitle = popoverTitle;
111
- }
112
-
113
- popoverWidth = popoverWidth || (persistFavorites && !favesTB ? 500 : 250);
78
+ favoritesMenuTestId = getTestId(testId, 'favorites-menu'),
79
+ favoritesIconTestId = getTestId(testId, 'favorites-icon'),
80
+ editorTestId = getTestId(testId, 'editor');
114
81
 
115
82
  return box({
116
83
  ref,
@@ -119,10 +86,11 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
119
86
  item: popover({
120
87
  isOpen,
121
88
  popoverRef: model.popoverRef,
122
- popoverClassName: `xh-grouping-chooser-popover xh-grouping-chooser-popover--${favesClassNameMod} xh-popup--framed`,
123
- // Left align editor to keep in place when button changing size when commitOnChange: true
124
- position: `${popoverPosition}-left`,
125
- minimal: false,
89
+ popoverClassName: 'xh-grouping-chooser-popover xh-popup--framed',
90
+ // right align favorites popover to match star icon
91
+ // left align editor to keep in place when button changing size when commitOnChange: true
92
+ position: favoritesIsOpen ? `${popoverPosition}-right` : `${popoverPosition}-left`,
93
+ minimal: styleButtonAsInput,
126
94
  item: fragment(
127
95
  button({
128
96
  text: label,
@@ -130,29 +98,34 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
130
98
  tabIndex: -1,
131
99
  className: classNames(
132
100
  'xh-grouping-chooser-button',
133
- styleButtonAsInput ? 'xh-grouping-chooser-button--as-input' : null
101
+ styleButtonAsInput ? 'xh-grouping-chooser-button--as-input' : null,
102
+ persistFavorites ? 'xh-grouping-chooser-button--with-favorites' : null
134
103
  ),
135
104
  minimal: styleButtonAsInput,
136
105
  ...buttonProps,
137
106
  onClick: () => model.toggleEditor(),
138
107
  testId
139
- })
108
+ }),
109
+ favoritesIconCmp({testId: favoritesIconTestId, favoritesIcon})
140
110
  ),
141
- content: popoverCmp({
142
- editorTitle,
143
- emptyText,
144
- favoritesSide,
145
- favoritesTitle,
146
- popoverWidth,
147
- popoverMinHeight,
148
- testId
149
- }),
111
+ content: favoritesIsOpen
112
+ ? favoritesMenu({testId: favoritesMenuTestId})
113
+ : editor({
114
+ popoverWidth,
115
+ popoverMinHeight,
116
+ popoverTitle,
117
+ emptyText,
118
+ testId: editorTestId
119
+ }),
150
120
  onInteraction: (nextOpenState, e) => {
151
121
  if (
152
122
  isOpen &&
153
123
  nextOpenState === false &&
154
124
  e?.target &&
155
- !elemWithin(e.target as HTMLElement, 'xh-grouping-chooser-button')
125
+ !elemWithin(
126
+ e.target as HTMLElement,
127
+ 'xh-grouping-chooser-button--with-favorites'
128
+ )
156
129
  ) {
157
130
  model.commitPendingValueAndClose();
158
131
  }
@@ -165,65 +138,18 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
165
138
  //------------------
166
139
  // Editor
167
140
  //------------------
168
- const popoverCmp = hoistCmp.factory<Partial<GroupingChooserProps>>({
169
- render({
170
- model,
171
- editorTitle,
172
- emptyText,
173
- favoritesSide,
174
- favoritesTitle,
175
- popoverWidth,
176
- popoverMinHeight,
177
- testId
178
- }) {
179
- const favesTB = isTB(favoritesSide),
180
- isFavesFirst = favoritesSide === 'left' || favoritesSide === 'top',
181
- items = [
182
- editor({
183
- editorTitle,
184
- emptyText,
185
- testId: getTestId(testId, 'editor')
186
- }),
187
- favoritesChooser({
188
- // Omit if favorites generally disabled, or if none saved yet AND in top/bottom
189
- // orientation - the empty state looks clumsy in that case. Show when empty in
190
- // left/right orientation to avoid large jump in popover width.
191
- omit: !model.persistFavorites || (!model.hasFavorites && favesTB),
192
- favoritesSide,
193
- favoritesTitle,
194
- testId: getTestId(testId, 'favorites')
195
- })
196
- ],
197
- itemsContainer = favesTB ? vframe : hframe;
198
-
199
- if (isFavesFirst) {
200
- items.reverse();
201
- }
202
-
141
+ const editor = hoistCmp.factory<GroupingChooserModel>({
142
+ render({popoverWidth, popoverMinHeight, popoverTitle, emptyText, testId}) {
203
143
  return panel({
204
- className: 'xh-grouping-chooser-popover__inner',
205
144
  width: popoverWidth,
206
145
  minHeight: popoverMinHeight,
207
- items: itemsContainer({items}),
208
- bbar: toolbar({
209
- compact: true,
210
- omit: !model.persistFavorites,
211
- items: [filler(), favoritesAddBtn({testId})]
212
- })
213
- });
214
- }
215
- });
216
-
217
- const editor = hoistCmp.factory<GroupingChooserModel>({
218
- render({editorTitle, emptyText, testId}) {
219
- return vbox({
220
- className: 'xh-grouping-chooser__editor',
221
- testId,
222
146
  items: [
223
- div({className: 'xh-popup__title', item: editorTitle, omit: isNil(editorTitle)}),
147
+ div({className: 'xh-popup__title', item: popoverTitle, omit: !popoverTitle}),
224
148
  dimensionList({emptyText}),
225
- addDimensionControl()
226
- ]
149
+ addDimensionControl(),
150
+ filler()
151
+ ],
152
+ testId
227
153
  });
228
154
  }
229
155
  });
@@ -357,7 +283,7 @@ const addDimensionControl = hoistCmp.factory<GroupingChooserModel>({
357
283
  // ensure the Select loses its internal input state.
358
284
  key: JSON.stringify(options),
359
285
  options,
360
- placeholder: 'Add level...',
286
+ placeholder: 'Add...',
361
287
  flex: 1,
362
288
  width: null,
363
289
  hideDropdownIndicator: true,
@@ -394,25 +320,47 @@ function getDimOptions(dims, model) {
394
320
  //------------------
395
321
  // Favorites
396
322
  //------------------
397
- const favoritesChooser = hoistCmp.factory<GroupingChooserModel>({
398
- render({model, favoritesSide, favoritesTitle, testId}) {
399
- const {favoritesOptions: options, hasFavorites} = model;
323
+ const favoritesIconCmp = hoistCmp.factory<GroupingChooserModel>({
324
+ render({model, testId, favoritesIcon}) {
325
+ if (!model.persistFavorites) return null;
326
+ return div({
327
+ item: favoritesIcon ?? Icon.favorite(),
328
+ className: 'xh-grouping-chooser__favorite-icon',
329
+ [TEST_ID]: testId,
330
+ onClick: e => {
331
+ model.toggleFavoritesMenu();
332
+ e.stopPropagation();
333
+ }
334
+ });
335
+ }
336
+ });
337
+
338
+ const favoritesMenu = hoistCmp.factory<GroupingChooserModel>({
339
+ render({model, testId}) {
340
+ const options = model.favoritesOptions,
341
+ isFavorite = model.isFavorite(model.value),
342
+ omitAdd = isEmpty(model.value) || isFavorite,
343
+ items = [];
344
+
345
+ if (isEmpty(options)) {
346
+ items.push(menuItem({text: 'No favorites saved...', disabled: true}));
347
+ } else {
348
+ items.push(...options.map(it => favoriteMenuItem(it)));
349
+ }
350
+
351
+ items.push(
352
+ menuDivider({omit: omitAdd}),
353
+ menuItem({
354
+ icon: Icon.add({intent: 'success'}),
355
+ text: 'Add current',
356
+ omit: omitAdd,
357
+ onClick: () => model.addFavorite(model.value)
358
+ })
359
+ );
400
360
 
401
361
  return vbox({
402
- className: `xh-grouping-chooser__favorites xh-grouping-chooser__favorites--${favoritesSide}`,
403
362
  testId,
404
- items: [
405
- div({
406
- className: 'xh-popup__title',
407
- item: favoritesTitle,
408
- omit: isNil(favoritesTitle)
409
- }),
410
- hasFavorites
411
- ? menu({
412
- items: options.map(it => favoriteMenuItem(it))
413
- })
414
- : placeholder('No favorites saved.')
415
- ]
363
+ items: [div({className: 'xh-popup__title', item: 'Favorites'}), menu({items})]
416
364
  });
417
365
  }
418
366
  });
@@ -421,7 +369,7 @@ const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
421
369
  render({model, value, label}) {
422
370
  return menuItem({
423
371
  text: label,
424
- className: 'xh-grouping-chooser__favorites__favorite',
372
+ className: 'xh-grouping-chooser__favorite',
425
373
  onClick: () => model.setValue(value),
426
374
  labelElement: button({
427
375
  icon: Icon.delete(),
@@ -434,19 +382,3 @@ const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
434
382
  });
435
383
  }
436
384
  });
437
-
438
- const favoritesAddBtn = hoistCmp.factory<GroupingChooserModel>({
439
- render({model, testId}) {
440
- return button({
441
- text: 'Save as Favorite',
442
- icon: Icon.favorite(),
443
- className: 'xh-grouping-chooser__favorites__add-btn',
444
- testId: getTestId(testId, 'favorites-add-btn'),
445
- omit: !model.persistFavorites,
446
- disabled: !model.isAddFavoriteEnabled,
447
- onClick: () => model.addPendingAsFavorite()
448
- });
449
- }
450
- });
451
-
452
- const isTB = (favoritesSide: Side) => favoritesSide === 'top' || favoritesSide === 'bottom';
@@ -84,7 +84,7 @@ export interface PanelProps extends HoistProps<PanelModel>, Omit<BoxProps, 'titl
84
84
  tbar?: Some<ReactNode>;
85
85
 
86
86
  /**
87
- * A toolbar to be docked at the bottom of the panel.
87
+ * A toolbar to be docked at the top of the panel.
88
88
  * If specified as an array, items will be passed as children to a Toolbar component.
89
89
  */
90
90
  bbar?: Some<ReactNode>;
@@ -7,14 +7,13 @@
7
7
 
8
8
  import {box, fragment, hbox} from '@xh/hoist/cmp/layout';
9
9
  import {spinner} from '@xh/hoist/cmp/spinner';
10
- import {hoistCmp, HoistProps, MenuItemLike, useLocalModel, uses} from '@xh/hoist/core';
10
+ import {hoistCmp, HoistProps, useLocalModel, uses} from '@xh/hoist/core';
11
11
  import {ViewManagerModel} from '@xh/hoist/cmp/viewmanager';
12
12
  import {button, ButtonProps} from '@xh/hoist/desktop/cmp/button';
13
13
  import {Icon} from '@xh/hoist/icon';
14
14
  import {popover} from '@xh/hoist/kit/blueprint';
15
15
  import {useOnVisibleChange} from '@xh/hoist/utils/react';
16
16
  import {startCase} from 'lodash';
17
- import {ReactElement} from 'react';
18
17
  import {viewMenu} from './ViewMenu';
19
18
  import {ViewManagerLocalModel} from './ViewManagerLocalModel';
20
19
  import {manageDialog} from './dialog/ManageDialog';
@@ -22,44 +21,28 @@ import {saveAsDialog} from './dialog/SaveAsDialog';
22
21
 
23
22
  import './ViewManager.scss';
24
23
 
24
+ /**
25
+ * Visibility options for save/revert button.
26
+ *
27
+ * 'never' to hide button.
28
+ * 'whenDirty' to only show when persistence state is dirty and button is therefore enabled.
29
+ * 'always' will always show button.
30
+ */
31
+ export type ViewManagerStateButtonMode = 'whenDirty' | 'always' | 'never';
32
+
25
33
  export interface ViewManagerProps extends HoistProps<ViewManagerModel> {
26
34
  menuButtonProps?: Partial<ButtonProps>;
27
35
  saveButtonProps?: Partial<ButtonProps>;
28
36
  revertButtonProps?: Partial<ButtonProps>;
29
37
 
30
- /** Button icon when on the default (in-code state) view. Default `Icon.bookmark`. */
31
- defaultViewIcon?: ReactElement;
32
- /** Button icon when the selected view is owned by the current user. Default `Icon.bookmark`. */
33
- ownedViewIcon?: ReactElement;
34
- /** Button icon when the selected view is shared by another user. Default `Icon.users`. */
35
- sharedViewIcon?: ReactElement;
36
- /** Button icon when the selected view is globally shared. Default `Icon.globe`. */
37
- globalViewIcon?: ReactElement;
38
-
39
38
  /** Default 'whenDirty' */
40
39
  showSaveButton?: ViewManagerStateButtonMode;
41
40
  /** Default 'never' */
42
41
  showRevertButton?: ViewManagerStateButtonMode;
43
- /** Side relative to the menu on which save/revert buttons should render. Default 'right'. */
42
+ /** Side the save and revert buttons should appear on (default 'right') */
44
43
  buttonSide?: 'left' | 'right';
45
- /**
46
- * Array of extra menu items. Can contain:
47
- * + `MenuItems` or configs to create them.
48
- * + `MenuDividers` or the special string token '-'.
49
- * + React Elements or strings, which will be interpreted as the `text` property for a MenuItem.
50
- */
51
- extraMenuItems?: MenuItemLike[];
52
44
  }
53
45
 
54
- /**
55
- * Visibility options for save/revert buttons inlined next to the ViewManager menu:
56
- * 'never' to always hide - user must save/revert via menu.
57
- * 'whenDirty' (default) to show only when view state is dirty and the button is enabled.
58
- * 'always' to always show, including when view not dirty and the button is disabled.
59
- * Useful to avoid jumpiness in toolbar layouts.
60
- */
61
- export type ViewManagerStateButtonMode = 'whenDirty' | 'always' | 'never';
62
-
63
46
  /**
64
47
  * Desktop ViewManager component - a button-based menu for saving and swapping between named
65
48
  * bundles of persisted component state (e.g. grid views, dashboards, and similar).
@@ -77,14 +60,9 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
77
60
  menuButtonProps,
78
61
  saveButtonProps,
79
62
  revertButtonProps,
80
- defaultViewIcon = Icon.bookmark(),
81
- ownedViewIcon = Icon.bookmark(),
82
- sharedViewIcon = Icon.users(),
83
- globalViewIcon = Icon.globe(),
84
63
  showSaveButton = 'whenDirty',
85
64
  showRevertButton = 'never',
86
- buttonSide = 'right',
87
- extraMenuItems = []
65
+ buttonSide = 'right'
88
66
  }: ViewManagerProps) {
89
67
  const {loadModel} = model,
90
68
  locModel = useLocalModel(() => new ViewManagerLocalModel(model)),
@@ -92,17 +70,7 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
92
70
  revert = revertButton({model: locModel, mode: showRevertButton, ...revertButtonProps}),
93
71
  menu = popover({
94
72
  disabled: !locModel.isVisible, // Prevent orphaned popover menu
95
- item: menuButton({
96
- model: locModel,
97
- icon: buttonIcon({
98
- model: locModel,
99
- defaultViewIcon,
100
- ownedViewIcon,
101
- sharedViewIcon,
102
- globalViewIcon
103
- }),
104
- ...menuButtonProps
105
- }),
73
+ item: menuButton({model: locModel, ...menuButtonProps}),
106
74
  content: loadModel.isPending
107
75
  ? box({
108
76
  item: spinner({compact: true}),
@@ -111,7 +79,7 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
111
79
  height: 30,
112
80
  width: 30
113
81
  })
114
- : viewMenu({model: locModel, extraMenuItems}),
82
+ : viewMenu({model: locModel}),
115
83
  onOpening: () => model.refreshAsync(),
116
84
  placement: 'bottom',
117
85
  popoverClassName: 'xh-view-manager__popover'
@@ -129,13 +97,13 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
129
97
  });
130
98
 
131
99
  const menuButton = hoistCmp.factory<ViewManagerLocalModel>({
132
- render({model, icon, ...rest}) {
100
+ render({model, ...rest}) {
133
101
  const {view, typeDisplayName, isLoading} = model.parent;
134
102
  return button({
135
103
  className: 'xh-view-manager__menu-button',
136
104
  text: view.isDefault ? `Default ${startCase(typeDisplayName)}` : view.name,
137
105
  icon: !isLoading
138
- ? icon
106
+ ? Icon.bookmark()
139
107
  : box({
140
108
  item: spinner({width: 13, height: 13, style: {margin: 'auto'}}),
141
109
  width: 16.25
@@ -147,16 +115,6 @@ const menuButton = hoistCmp.factory<ViewManagerLocalModel>({
147
115
  }
148
116
  });
149
117
 
150
- const buttonIcon = hoistCmp.factory<ViewManagerLocalModel>({
151
- render({model, ownedViewIcon, sharedViewIcon, globalViewIcon, defaultViewIcon}) {
152
- const {view} = model.parent;
153
- if (view.isOwned) return ownedViewIcon;
154
- if (view.isShared) return sharedViewIcon;
155
- if (view.isGlobal) return globalViewIcon;
156
- return defaultViewIcon;
157
- }
158
- });
159
-
160
118
  const saveButton = hoistCmp.factory<ViewManagerLocalModel>({
161
119
  render({model, mode, ...rest}) {
162
120
  if (hideStateButton(model, mode)) return null;
@@ -11,7 +11,6 @@ import {switchInput} from '@xh/hoist/desktop/cmp/input';
11
11
  import {Icon} from '@xh/hoist/icon';
12
12
  import {menu, menuDivider, menuItem} from '@xh/hoist/kit/blueprint';
13
13
  import {pluralize} from '@xh/hoist/utils/js';
14
- import {filterConsecutiveMenuSeparators, parseMenuItems} from '@xh/hoist/utils/impl';
15
14
  import {Dictionary} from 'express-serve-static-core';
16
15
  import {each, filter, groupBy, isEmpty, isFunction, orderBy, some, startCase} from 'lodash';
17
16
  import {ReactNode} from 'react';
@@ -21,16 +20,10 @@ import {ViewManagerLocalModel} from './ViewManagerLocalModel';
21
20
  * Default Menu used by ViewManager.
22
21
  */
23
22
  export const viewMenu = hoistCmp.factory<ViewManagerLocalModel>({
24
- render({model, extraMenuItems}) {
23
+ render({model}) {
25
24
  return menu({
26
25
  className: 'xh-view-manager__menu',
27
- items: [
28
- ...getNavMenuItems(model.parent),
29
- menuDivider(),
30
- ...parseMenuItems(extraMenuItems),
31
- menuDivider(),
32
- ...getOtherMenuItems(model)
33
- ].filter(filterConsecutiveMenuSeparators())
26
+ items: [...getNavMenuItems(model.parent), menuDivider(), ...getOtherMenuItems(model)]
34
27
  });
35
28
  }
36
29
  });
@@ -7,7 +7,6 @@
7
7
  import {FeedbackDialogModel} from '@xh/hoist/appcontainer/FeedbackDialogModel';
8
8
  import {filler} from '@xh/hoist/cmp/layout';
9
9
  import {hoistCmp, uses} from '@xh/hoist/core';
10
- import {Icon} from '@xh/hoist/icon';
11
10
  import {button} from '@xh/hoist/mobile/cmp/button';
12
11
  import {dialog} from '@xh/hoist/mobile/cmp/dialog';
13
12
  import {textArea} from '@xh/hoist/mobile/cmp/input';
@@ -42,9 +41,6 @@ export const feedbackDialog = hoistCmp.factory({
42
41
  }),
43
42
  button({
44
43
  text: 'Send',
45
- icon: Icon.mail(),
46
- intent: 'primary',
47
- outlined: true,
48
44
  onClick: () => model.submitAsync()
49
45
  })
50
46
  ]
@@ -57,10 +57,8 @@ export const optionsDialog = hoistCmp.factory({
57
57
  onClick: () => model.hide()
58
58
  }),
59
59
  button({
60
- text: 'Apply',
60
+ text: 'Save',
61
61
  icon: reloadRequired ? Icon.refresh() : Icon.check(),
62
- intent: 'primary',
63
- outlined: true,
64
62
  disabled: !formModel.isDirty,
65
63
  onClick: () => model.saveAsync()
66
64
  })
@@ -60,6 +60,7 @@ export const [ColChooser, colChooser] = hoistCmp.withFactory<ColChooserProps>({
60
60
  onDragEnd: impl.onDragEnd,
61
61
  items: [
62
62
  panel({
63
+ title: 'Visible Columns',
63
64
  className: 'xh-col-chooser__section',
64
65
  scrollable: true,
65
66
  items: [
@@ -118,10 +119,8 @@ export const [ColChooser, colChooser] = hoistCmp.withFactory<ColChooserProps>({
118
119
  onClick: () => model.close()
119
120
  }),
120
121
  button({
121
- text: 'Apply',
122
+ text: 'Save',
122
123
  icon: Icon.check(),
123
- intent: 'primary',
124
- outlined: true,
125
124
  onClick: () => {
126
125
  model.commit();
127
126
  model.close();