@xh/hoist 74.1.2 → 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 -41
  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 +40 -45
  17. package/desktop/cmp/grouping/GroupingChooser.ts +89 -159
  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,67 +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
- frame,
14
- hbox,
15
- hframe,
16
- placeholder,
17
- vbox,
18
- vframe
19
- } from '@xh/hoist/cmp/layout';
20
- 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';
21
10
  import {button, ButtonProps} from '@xh/hoist/desktop/cmp/button';
22
11
  import {select} from '@xh/hoist/desktop/cmp/input';
23
12
  import {panel} from '@xh/hoist/desktop/cmp/panel';
24
13
  import '@xh/hoist/desktop/register';
25
- import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
26
14
  import {Icon} from '@xh/hoist/icon';
27
- import {menu, menuItem, popover} from '@xh/hoist/kit/blueprint';
15
+ import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
28
16
  import {dragDropContext, draggable, droppable} from '@xh/hoist/kit/react-beautiful-dnd';
29
- import {apiDeprecated, elemWithin, getTestId} from '@xh/hoist/utils/js';
17
+ import {elemWithin, getTestId, TEST_ID} from '@xh/hoist/utils/js';
30
18
  import {splitLayoutProps} from '@xh/hoist/utils/react';
19
+ import {ReactElement} from 'react';
31
20
  import classNames from 'classnames';
32
- import {compact, isEmpty, isNil, isUndefined, sortBy} from 'lodash';
21
+ import {compact, isEmpty, sortBy} from 'lodash';
33
22
  import './GroupingChooser.scss';
34
- import {ReactNode} from 'react';
35
23
 
36
24
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
37
- /** Title for value-editing portion of popover, or null to suppress. */
38
- editorTitle?: ReactNode;
39
-
40
25
  /** Text to represent empty state (i.e. value = null or []) */
41
26
  emptyText?: string;
42
27
 
43
- /**
44
- * Side of the popover, relative to the value-editing controls, on which the Favorites list
45
- * should be rendered, if enabled.
46
- */
47
- favoritesSide?: Side;
48
-
49
- /** Title for favorites-list portion of popover, or null to suppress. */
50
- favoritesTitle?: ReactNode;
51
-
52
28
  /** Min height in pixels of the popover menu itself. */
53
29
  popoverMinHeight?: number;
54
30
 
55
31
  /** Position of popover relative to target button. */
56
32
  popoverPosition?: 'bottom' | 'top';
57
33
 
58
- /** @deprecated - use `editorTitle` instead */
59
- popoverTitle?: ReactNode;
34
+ /** Title for popover (default "GROUP BY") or null to suppress. */
35
+ popoverTitle?: string;
60
36
 
61
- /**
62
- * Width in pixels of the popover menu itself.
63
- * If unspecified, will default based on favorites enabled status + side.
64
- */
37
+ /** Width in pixels of the popover menu itself. */
65
38
  popoverWidth?: number;
66
39
 
67
40
  /** True (default) to style target button as an input field - blends better in toolbars. */
68
41
  styleButtonAsInput?: boolean;
42
+
43
+ /** Icon clicked to launch favorites menu. Defaults to Icon.favorite() */
44
+ favoritesIcon?: ReactElement;
69
45
  }
70
46
 
71
47
  /**
@@ -83,35 +59,25 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
83
59
  {
84
60
  model,
85
61
  className,
86
- editorTitle = 'Group By',
87
62
  emptyText = 'Ungrouped',
88
- favoritesSide = 'right',
89
- favoritesTitle = 'Favorites',
90
- popoverWidth,
63
+ popoverWidth = 250,
91
64
  popoverMinHeight,
92
- popoverTitle,
65
+ popoverTitle = 'Group By',
93
66
  popoverPosition = 'bottom',
94
67
  styleButtonAsInput = true,
95
68
  testId,
69
+ favoritesIcon,
96
70
  ...rest
97
71
  },
98
72
  ref
99
73
  ) {
100
- const {editorIsOpen, value, allowEmpty, persistFavorites} = model,
101
- isOpen = editorIsOpen,
74
+ const {editorIsOpen, favoritesIsOpen, persistFavorites, value, allowEmpty} = model,
75
+ isOpen = editorIsOpen || favoritesIsOpen,
102
76
  label = isEmpty(value) && allowEmpty ? emptyText : model.getValueLabel(value),
103
77
  [layoutProps, buttonProps] = splitLayoutProps(rest),
104
- favesClassNameMod = `faves-${persistFavorites ? favoritesSide : 'disabled'}`,
105
- favesTB = isTB(favoritesSide);
106
-
107
- if (!isUndefined(popoverTitle)) {
108
- apiDeprecated('GroupingChooser.popoverTitle', {
109
- msg: `Update to use 'editorTitle' instead`
110
- });
111
- editorTitle = popoverTitle;
112
- }
113
-
114
- popoverWidth = popoverWidth || (persistFavorites && !favesTB ? 500 : 250);
78
+ favoritesMenuTestId = getTestId(testId, 'favorites-menu'),
79
+ favoritesIconTestId = getTestId(testId, 'favorites-icon'),
80
+ editorTestId = getTestId(testId, 'editor');
115
81
 
116
82
  return box({
117
83
  ref,
@@ -120,10 +86,11 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
120
86
  item: popover({
121
87
  isOpen,
122
88
  popoverRef: model.popoverRef,
123
- popoverClassName: `xh-grouping-chooser-popover xh-grouping-chooser-popover--${favesClassNameMod} xh-popup--framed`,
124
- // Left align editor to keep in place when button changing size when commitOnChange: true
125
- position: `${popoverPosition}-left`,
126
- 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,
127
94
  item: fragment(
128
95
  button({
129
96
  text: label,
@@ -131,29 +98,34 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
131
98
  tabIndex: -1,
132
99
  className: classNames(
133
100
  'xh-grouping-chooser-button',
134
- 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
135
103
  ),
136
104
  minimal: styleButtonAsInput,
137
105
  ...buttonProps,
138
106
  onClick: () => model.toggleEditor(),
139
107
  testId
140
- })
108
+ }),
109
+ favoritesIconCmp({testId: favoritesIconTestId, favoritesIcon})
141
110
  ),
142
- content: popoverCmp({
143
- editorTitle,
144
- emptyText,
145
- favoritesSide,
146
- favoritesTitle,
147
- popoverWidth,
148
- popoverMinHeight,
149
- testId
150
- }),
111
+ content: favoritesIsOpen
112
+ ? favoritesMenu({testId: favoritesMenuTestId})
113
+ : editor({
114
+ popoverWidth,
115
+ popoverMinHeight,
116
+ popoverTitle,
117
+ emptyText,
118
+ testId: editorTestId
119
+ }),
151
120
  onInteraction: (nextOpenState, e) => {
152
121
  if (
153
122
  isOpen &&
154
123
  nextOpenState === false &&
155
124
  e?.target &&
156
- !elemWithin(e.target as HTMLElement, 'xh-grouping-chooser-button')
125
+ !elemWithin(
126
+ e.target as HTMLElement,
127
+ 'xh-grouping-chooser-button--with-favorites'
128
+ )
157
129
  ) {
158
130
  model.commitPendingValueAndClose();
159
131
  }
@@ -166,66 +138,18 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
166
138
  //------------------
167
139
  // Editor
168
140
  //------------------
169
- const popoverCmp = hoistCmp.factory<Partial<GroupingChooserProps>>({
170
- render({
171
- model,
172
- editorTitle,
173
- emptyText,
174
- favoritesSide,
175
- favoritesTitle,
176
- popoverWidth,
177
- popoverMinHeight,
178
- testId
179
- }) {
180
- const {persistFavorites} = model,
181
- favesTB = isTB(favoritesSide),
182
- isFavesFirst = favoritesSide === 'left' || favoritesSide === 'top',
183
- items = [
184
- editor({
185
- editorTitle,
186
- emptyText,
187
- testId: getTestId(testId, 'editor')
188
- }),
189
- favoritesChooser({
190
- // Omit if favorites generally disabled, or if none saved yet AND in top/bottom
191
- // orientation - the empty state looks clumsy in that case. Show when empty in
192
- // left/right orientation to avoid large jump in popover width.
193
- omit: !model.persistFavorites || (!model.hasFavorites && favesTB),
194
- favoritesSide,
195
- favoritesTitle,
196
- testId: getTestId(testId, 'favorites')
197
- })
198
- ],
199
- itemsContainer = !persistFavorites ? frame : favesTB ? vframe : hframe;
200
-
201
- if (isFavesFirst) {
202
- items.reverse();
203
- }
204
-
141
+ const editor = hoistCmp.factory<GroupingChooserModel>({
142
+ render({popoverWidth, popoverMinHeight, popoverTitle, emptyText, testId}) {
205
143
  return panel({
206
- className: 'xh-grouping-chooser-popover__inner',
207
144
  width: popoverWidth,
208
145
  minHeight: popoverMinHeight,
209
- items: itemsContainer({items}),
210
- bbar: toolbar({
211
- compact: true,
212
- omit: !model.persistFavorites,
213
- items: [filler(), favoritesAddBtn({testId})]
214
- })
215
- });
216
- }
217
- });
218
-
219
- const editor = hoistCmp.factory<GroupingChooserModel>({
220
- render({editorTitle, emptyText, testId}) {
221
- return vbox({
222
- className: 'xh-grouping-chooser__editor',
223
- testId,
224
146
  items: [
225
- div({className: 'xh-popup__title', item: editorTitle, omit: isNil(editorTitle)}),
147
+ div({className: 'xh-popup__title', item: popoverTitle, omit: !popoverTitle}),
226
148
  dimensionList({emptyText}),
227
- addDimensionControl()
228
- ]
149
+ addDimensionControl(),
150
+ filler()
151
+ ],
152
+ testId
229
153
  });
230
154
  }
231
155
  });
@@ -359,7 +283,7 @@ const addDimensionControl = hoistCmp.factory<GroupingChooserModel>({
359
283
  // ensure the Select loses its internal input state.
360
284
  key: JSON.stringify(options),
361
285
  options,
362
- placeholder: 'Add level...',
286
+ placeholder: 'Add...',
363
287
  flex: 1,
364
288
  width: null,
365
289
  hideDropdownIndicator: true,
@@ -396,25 +320,47 @@ function getDimOptions(dims, model) {
396
320
  //------------------
397
321
  // Favorites
398
322
  //------------------
399
- const favoritesChooser = hoistCmp.factory<GroupingChooserModel>({
400
- render({model, favoritesSide, favoritesTitle, testId}) {
401
- 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
+ );
402
360
 
403
361
  return vbox({
404
- className: `xh-grouping-chooser__favorites xh-grouping-chooser__favorites--${favoritesSide}`,
405
362
  testId,
406
- items: [
407
- div({
408
- className: 'xh-popup__title',
409
- item: favoritesTitle,
410
- omit: isNil(favoritesTitle)
411
- }),
412
- hasFavorites
413
- ? menu({
414
- items: options.map(it => favoriteMenuItem(it))
415
- })
416
- : placeholder('No favorites saved.')
417
- ]
363
+ items: [div({className: 'xh-popup__title', item: 'Favorites'}), menu({items})]
418
364
  });
419
365
  }
420
366
  });
@@ -423,7 +369,7 @@ const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
423
369
  render({model, value, label}) {
424
370
  return menuItem({
425
371
  text: label,
426
- className: 'xh-grouping-chooser__favorites__favorite',
372
+ className: 'xh-grouping-chooser__favorite',
427
373
  onClick: () => model.setValue(value),
428
374
  labelElement: button({
429
375
  icon: Icon.delete(),
@@ -436,19 +382,3 @@ const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
436
382
  });
437
383
  }
438
384
  });
439
-
440
- const favoritesAddBtn = hoistCmp.factory<GroupingChooserModel>({
441
- render({model, testId}) {
442
- return button({
443
- text: 'Save as Favorite',
444
- icon: Icon.favorite(),
445
- className: 'xh-grouping-chooser__favorites__add-btn',
446
- testId: getTestId(testId, 'favorites-add-btn'),
447
- omit: !model.persistFavorites,
448
- disabled: !model.isAddFavoriteEnabled,
449
- onClick: () => model.addPendingAsFavorite()
450
- });
451
- }
452
- });
453
-
454
- 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();