@xh/hoist 75.0.0-SNAPSHOT.1751070190067 → 75.0.0-SNAPSHOT.1751290908617

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 CHANGED
@@ -2,8 +2,17 @@
2
2
 
3
3
  ## v75.0.0-SNAPSHOT - unreleased
4
4
 
5
+ ## v74.1.0 - 2025-06-30
6
+
5
7
  ### 🎁 New Features
6
8
 
9
+ * Updated the `GroupingChooser` UI to use a single popover for both updating the value and
10
+ selecting/managing favorite groupings (if enabled).
11
+ * Adjusted `GroupingChooserModel` API and some CSS class names and testIds of `GroupingChooser`
12
+ internals, although those changes are very unlikely to require app-level adjustments.
13
+ * Adjusted/removed (rarely used) desktop and mobile `GroupingChooser` props related to popover
14
+ sizing and titling.
15
+ * Updated the mobile UI to use a full-screen dialog, similar to `ColumnChooser`.
7
16
  * Added props to `ViewManager` to customize icons used for different types of views, and modified
8
17
  default icons for Global and Shared views.
9
18
  * Added `ViewManager.extraMenuItems` prop to allow insertion of custom, app-specific items into the
@@ -8,7 +8,10 @@ export interface GroupingChooserConfig {
8
8
  dimensions?: (DimensionSpec | string)[];
9
9
  /** Initial value as an array of dimension names, or a function to produce such an array. */
10
10
  initialValue?: string[] | (() => string[]);
11
- /** Initial favorites as an array of dim name arrays, or a function to produce such an array. */
11
+ /**
12
+ * Initial favorites as an array of dim name arrays, or a function to produce such an array.
13
+ * Ignored if `persistWith.persistFavorites: false`.
14
+ */
12
15
  initialFavorites?: string[][] | (() => string[][]);
13
16
  /** Options governing persistence. */
14
17
  persistWith?: GroupingChooserPersistOptions;
@@ -47,7 +50,6 @@ export declare class GroupingChooserModel extends HoistModel {
47
50
  persistFavorites: boolean;
48
51
  pendingValue: string[];
49
52
  editorIsOpen: boolean;
50
- favoritesIsOpen: boolean;
51
53
  popoverRef: import("react").RefObject<HTMLElement> & import("react").RefCallback<HTMLElement>;
52
54
  private dimensions;
53
55
  private dimensionNames;
@@ -55,12 +57,12 @@ export declare class GroupingChooserModel extends HoistModel {
55
57
  get dimensionSpecs(): DimensionSpec[];
56
58
  get isValid(): boolean;
57
59
  get isAddEnabled(): boolean;
60
+ get isAddFavoriteEnabled(): boolean;
58
61
  constructor({ dimensions, initialValue, initialFavorites, persistWith, allowEmpty, maxDepth, commitOnChange }: GroupingChooserConfig);
59
62
  setDimensions(dimensions: Array<DimensionSpec | string>): void;
60
63
  setValue(value: string[]): void;
61
64
  toggleEditor(): void;
62
- toggleFavoritesMenu(): void;
63
- closePopover(): void;
65
+ closeEditor(): void;
64
66
  addPendingDim(dimName: string): void;
65
67
  replacePendingDimAtIdx(dimName: string, idx: number): void;
66
68
  removePendingDimAtIdx(idx: number): void;
@@ -76,6 +78,7 @@ export declare class GroupingChooserModel extends HoistModel {
76
78
  }[];
77
79
  setFavorites(favorites: string[][]): void;
78
80
  addFavorite(value: string[]): void;
81
+ addPendingAsFavorite(): void;
79
82
  removeFavorite(value: string[]): void;
80
83
  isFavorite(value: string[]): boolean;
81
84
  private initPersist;
@@ -1,7 +1,6 @@
1
1
  import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
2
2
  import { ButtonProps } from '@xh/hoist/desktop/cmp/button';
3
3
  import '@xh/hoist/desktop/register';
4
- import { ReactElement } from 'react';
5
4
  import './GroupingChooser.scss';
6
5
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
7
6
  /** Text to represent empty state (i.e. value = null or []) */
@@ -12,12 +11,13 @@ export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel>
12
11
  popoverPosition?: 'bottom' | 'top';
13
12
  /** Title for popover (default "GROUP BY") or null to suppress. */
14
13
  popoverTitle?: string;
15
- /** Width in pixels of the popover menu itself. */
14
+ /**
15
+ * Width in pixels of the popover menu itself.
16
+ * If unspecified, will default based on whether favorites are enabled.
17
+ */
16
18
  popoverWidth?: number;
17
19
  /** True (default) to style target button as an input field - blends better in toolbars. */
18
20
  styleButtonAsInput?: boolean;
19
- /** Icon clicked to launch favorites menu. Defaults to Icon.favorite() */
20
- favoritesIcon?: ReactElement;
21
21
  }
22
22
  /**
23
23
  * Control for selecting a list of dimensions for grouping APIs, with built-in support for
@@ -3,14 +3,10 @@ import { ButtonProps } from '@xh/hoist/mobile/cmp/button';
3
3
  import '@xh/hoist/mobile/register';
4
4
  import './GroupingChooser.scss';
5
5
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
6
+ /** Custom title for editor dialog, or null to suppress. */
7
+ dialogTitle?: string;
6
8
  /** Text to represent empty state (i.e. value = null or [])*/
7
9
  emptyText?: string;
8
- /** Title for popover (default "GROUP BY") or null to suppress. */
9
- popoverTitle?: string;
10
- /** Min height in pixels of the popover inner content (excl. header & toolbar). */
11
- popoverMinHeight?: number;
12
- /** Width in pixels of the popover menu itself. */
13
- popoverWidth?: number;
14
10
  }
15
11
  /**
16
12
  * Control for selecting a list of dimensions for grouping APIs.
@@ -23,7 +23,10 @@ export interface GroupingChooserConfig {
23
23
  /** Initial value as an array of dimension names, or a function to produce such an array. */
24
24
  initialValue?: string[] | (() => string[]);
25
25
 
26
- /** Initial favorites as an array of dim name arrays, or a function to produce such an array. */
26
+ /**
27
+ * Initial favorites as an array of dim name arrays, or a function to produce such an array.
28
+ * Ignored if `persistWith.persistFavorites: false`.
29
+ */
27
30
  initialFavorites?: string[][] | (() => string[][]);
28
31
 
29
32
  /** Options governing persistence. */
@@ -74,7 +77,6 @@ export class GroupingChooserModel extends HoistModel {
74
77
  // Implementation fields for Control
75
78
  @observable.ref pendingValue: string[] = [];
76
79
  @observable editorIsOpen: boolean = false;
77
- @observable favoritesIsOpen: boolean = false;
78
80
  popoverRef = createObservableRef<HTMLElement>();
79
81
 
80
82
  // Internal state
@@ -105,6 +107,15 @@ export class GroupingChooserModel extends HoistModel {
105
107
  return !atMaxDepth && !isEmpty(availableDims);
106
108
  }
107
109
 
110
+ @computed
111
+ get isAddFavoriteEnabled(): boolean {
112
+ return (
113
+ this.persistFavorites &&
114
+ !isEmpty(this.pendingValue) &&
115
+ !this.isFavorite(this.pendingValue)
116
+ );
117
+ }
118
+
108
119
  constructor({
109
120
  dimensions,
110
121
  initialValue = [],
@@ -169,21 +180,13 @@ export class GroupingChooserModel extends HoistModel {
169
180
  toggleEditor() {
170
181
  this.pendingValue = this.value;
171
182
  this.editorIsOpen = !this.editorIsOpen;
172
- this.favoritesIsOpen = false;
173
183
  }
174
184
 
175
185
  @action
176
- toggleFavoritesMenu() {
177
- this.favoritesIsOpen = !this.favoritesIsOpen;
186
+ closeEditor() {
178
187
  this.editorIsOpen = false;
179
188
  }
180
189
 
181
- @action
182
- closePopover() {
183
- this.editorIsOpen = false;
184
- this.favoritesIsOpen = false;
185
- }
186
-
187
190
  //-------------------------
188
191
  // Value handling
189
192
  //-------------------------
@@ -224,7 +227,7 @@ export class GroupingChooserModel extends HoistModel {
224
227
  if (!isEqual(value, pendingValue) && this.validateValue(pendingValue)) {
225
228
  this.setValue(pendingValue);
226
229
  }
227
- this.closePopover();
230
+ this.closeEditor();
228
231
  }
229
232
 
230
233
  validateValue(value: string[]) {
@@ -274,6 +277,11 @@ export class GroupingChooserModel extends HoistModel {
274
277
  this.favorites = [...this.favorites, value];
275
278
  }
276
279
 
280
+ @action
281
+ addPendingAsFavorite() {
282
+ this.addFavorite(this.pendingValue);
283
+ }
284
+
277
285
  @action
278
286
  removeFavorite(value: string[]) {
279
287
  this.favorites = this.favorites.filter(v => !isEqual(v, value));
@@ -22,11 +22,6 @@
22
22
  color: var(--xh-text-color-muted);
23
23
  }
24
24
  }
25
-
26
- // We must account for the favorites icon when eliding text
27
- &--with-favorites {
28
- padding-right: 30px !important;
29
- }
30
25
  }
31
26
 
32
27
  // Outer box is sized via layoutSupport - all nested inner elements should stretch to fill.
@@ -39,6 +34,10 @@
39
34
  position: relative;
40
35
  }
41
36
 
37
+ &__editor {
38
+ flex: 1;
39
+ }
40
+
42
41
  &__list {
43
42
  margin-bottom: var(--xh-pad-half-px);
44
43
  background-color: var(--xh-bg-alt);
@@ -121,48 +120,38 @@
121
120
  padding: 2px var(--xh-tbar-item-pad-px);
122
121
  }
123
122
 
124
- &__favorite-icon {
125
- display: flex;
126
- align-items: center;
127
- justify-content: center;
128
- width: 30px;
129
- height: 20px;
130
- position: absolute;
131
- top: 0;
132
- right: 0;
133
- cursor: pointer;
134
- color: var(--xh-text-color-muted);
135
-
136
- &:hover {
137
- color: var(--xh-text-color);
138
- }
123
+ &__favorites {
124
+ flex: 1;
125
+ border-left: 1px solid var(--xh-popup-border-color);
139
126
 
140
- .xh-toolbar--compact & {
141
- height: 15px;
142
- }
127
+ --xh-menu-border: none;
143
128
 
144
- // Matched to FilterChooser equivalent.
145
- & > .svg-inline--fa {
146
- width: 12px;
129
+ .bp5-menu {
130
+ padding: 0;
147
131
  }
148
- }
149
-
150
- &__favorite {
151
- align-items: center;
152
- max-width: 50vw;
153
132
 
154
- .xh-button {
155
- padding: 0 !important;
156
- min-width: 20px !important;
157
- min-height: 20px !important;
158
- visibility: hidden;
133
+ &__add-btn {
134
+ flex: none;
135
+ margin: var(--xh-pad-px) auto;
159
136
  }
160
137
 
161
- &:hover {
162
- background-color: var(--xh-bg-highlight);
138
+ &__favorite {
139
+ align-items: center;
140
+ max-width: 50vw;
163
141
 
164
142
  .xh-button {
165
- visibility: unset;
143
+ padding: 0 !important;
144
+ min-width: 20px !important;
145
+ min-height: 20px !important;
146
+ visibility: hidden;
147
+ }
148
+
149
+ &:hover {
150
+ background-color: var(--xh-bg-highlight);
151
+
152
+ .xh-button {
153
+ visibility: unset;
154
+ }
166
155
  }
167
156
  }
168
157
  }
@@ -5,18 +5,17 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
8
- import {box, div, filler, fragment, hbox, vbox} from '@xh/hoist/cmp/layout';
8
+ import {box, div, filler, fragment, hbox, hframe, vbox} from '@xh/hoist/cmp/layout';
9
9
  import {hoistCmp, uses} from '@xh/hoist/core';
10
10
  import {button, ButtonProps} from '@xh/hoist/desktop/cmp/button';
11
11
  import {select} from '@xh/hoist/desktop/cmp/input';
12
12
  import {panel} from '@xh/hoist/desktop/cmp/panel';
13
13
  import '@xh/hoist/desktop/register';
14
14
  import {Icon} from '@xh/hoist/icon';
15
- import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
15
+ import {menu, menuItem, popover} from '@xh/hoist/kit/blueprint';
16
16
  import {dragDropContext, draggable, droppable} from '@xh/hoist/kit/react-beautiful-dnd';
17
- import {elemWithin, getTestId, TEST_ID} from '@xh/hoist/utils/js';
17
+ import {elemWithin, getTestId} from '@xh/hoist/utils/js';
18
18
  import {splitLayoutProps} from '@xh/hoist/utils/react';
19
- import {ReactElement} from 'react';
20
19
  import classNames from 'classnames';
21
20
  import {compact, isEmpty, sortBy} from 'lodash';
22
21
  import './GroupingChooser.scss';
@@ -34,14 +33,14 @@ export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel>
34
33
  /** Title for popover (default "GROUP BY") or null to suppress. */
35
34
  popoverTitle?: string;
36
35
 
37
- /** Width in pixels of the popover menu itself. */
36
+ /**
37
+ * Width in pixels of the popover menu itself.
38
+ * If unspecified, will default based on whether favorites are enabled.
39
+ */
38
40
  popoverWidth?: number;
39
41
 
40
42
  /** True (default) to style target button as an input field - blends better in toolbars. */
41
43
  styleButtonAsInput?: boolean;
42
-
43
- /** Icon clicked to launch favorites menu. Defaults to Icon.favorite() */
44
- favoritesIcon?: ReactElement;
45
44
  }
46
45
 
47
46
  /**
@@ -60,24 +59,22 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
60
59
  model,
61
60
  className,
62
61
  emptyText = 'Ungrouped',
63
- popoverWidth = 250,
62
+ popoverWidth,
64
63
  popoverMinHeight,
65
64
  popoverTitle = 'Group By',
66
65
  popoverPosition = 'bottom',
67
66
  styleButtonAsInput = true,
68
67
  testId,
69
- favoritesIcon,
70
68
  ...rest
71
69
  },
72
70
  ref
73
71
  ) {
74
- const {editorIsOpen, favoritesIsOpen, persistFavorites, value, allowEmpty} = model,
75
- isOpen = editorIsOpen || favoritesIsOpen,
72
+ const {editorIsOpen, value, allowEmpty, persistFavorites} = model,
73
+ isOpen = editorIsOpen,
76
74
  label = isEmpty(value) && allowEmpty ? emptyText : model.getValueLabel(value),
77
- [layoutProps, buttonProps] = splitLayoutProps(rest),
78
- favoritesMenuTestId = getTestId(testId, 'favorites-menu'),
79
- favoritesIconTestId = getTestId(testId, 'favorites-icon'),
80
- editorTestId = getTestId(testId, 'editor');
75
+ [layoutProps, buttonProps] = splitLayoutProps(rest);
76
+
77
+ popoverWidth = popoverWidth || (persistFavorites ? 500 : 250);
81
78
 
82
79
  return box({
83
80
  ref,
@@ -87,10 +84,9 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
87
84
  isOpen,
88
85
  popoverRef: model.popoverRef,
89
86
  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,
87
+ // Left align editor to keep in place when button changing size when commitOnChange: true
88
+ position: `${popoverPosition}-left`,
89
+ minimal: false,
94
90
  item: fragment(
95
91
  button({
96
92
  text: label,
@@ -98,34 +94,27 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
98
94
  tabIndex: -1,
99
95
  className: classNames(
100
96
  'xh-grouping-chooser-button',
101
- styleButtonAsInput ? 'xh-grouping-chooser-button--as-input' : null,
102
- persistFavorites ? 'xh-grouping-chooser-button--with-favorites' : null
97
+ styleButtonAsInput ? 'xh-grouping-chooser-button--as-input' : null
103
98
  ),
104
99
  minimal: styleButtonAsInput,
105
100
  ...buttonProps,
106
101
  onClick: () => model.toggleEditor(),
107
102
  testId
108
- }),
109
- favoritesIconCmp({testId: favoritesIconTestId, favoritesIcon})
103
+ })
110
104
  ),
111
- content: favoritesIsOpen
112
- ? favoritesMenu({testId: favoritesMenuTestId})
113
- : editor({
114
- popoverWidth,
115
- popoverMinHeight,
116
- popoverTitle,
117
- emptyText,
118
- testId: editorTestId
119
- }),
105
+ content: popoverCmp({
106
+ popoverWidth,
107
+ popoverMinHeight,
108
+ popoverTitle,
109
+ emptyText,
110
+ testId
111
+ }),
120
112
  onInteraction: (nextOpenState, e) => {
121
113
  if (
122
114
  isOpen &&
123
115
  nextOpenState === false &&
124
116
  e?.target &&
125
- !elemWithin(
126
- e.target as HTMLElement,
127
- 'xh-grouping-chooser-button--with-favorites'
128
- )
117
+ !elemWithin(e.target as HTMLElement, 'xh-grouping-chooser-button')
129
118
  ) {
130
119
  model.commitPendingValueAndClose();
131
120
  }
@@ -138,18 +127,38 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
138
127
  //------------------
139
128
  // Editor
140
129
  //------------------
141
- const editor = hoistCmp.factory<GroupingChooserModel>({
142
- render({popoverWidth, popoverMinHeight, popoverTitle, emptyText, testId}) {
130
+ const popoverCmp = hoistCmp.factory<GroupingChooserModel>({
131
+ render({model, popoverWidth, popoverMinHeight, popoverTitle, emptyText, testId}) {
143
132
  return panel({
144
133
  width: popoverWidth,
145
134
  minHeight: popoverMinHeight,
135
+ items: hframe({
136
+ items: [
137
+ editor({
138
+ popoverTitle,
139
+ emptyText,
140
+ testId: getTestId(testId, 'editor')
141
+ }),
142
+ favoritesChooser({
143
+ omit: !model.persistFavorites,
144
+ testId: getTestId(testId, 'favorites')
145
+ })
146
+ ]
147
+ })
148
+ });
149
+ }
150
+ });
151
+
152
+ const editor = hoistCmp.factory<GroupingChooserModel>({
153
+ render({popoverTitle, emptyText, testId}) {
154
+ return vbox({
155
+ className: 'xh-grouping-chooser__editor',
156
+ testId,
146
157
  items: [
147
158
  div({className: 'xh-popup__title', item: popoverTitle, omit: !popoverTitle}),
148
159
  dimensionList({emptyText}),
149
- addDimensionControl(),
150
- filler()
151
- ],
152
- testId
160
+ addDimensionControl()
161
+ ]
153
162
  });
154
163
  }
155
164
  });
@@ -283,7 +292,7 @@ const addDimensionControl = hoistCmp.factory<GroupingChooserModel>({
283
292
  // ensure the Select loses its internal input state.
284
293
  key: JSON.stringify(options),
285
294
  options,
286
- placeholder: 'Add...',
295
+ placeholder: 'Add level...',
287
296
  flex: 1,
288
297
  width: null,
289
298
  hideDropdownIndicator: true,
@@ -320,47 +329,28 @@ function getDimOptions(dims, model) {
320
329
  //------------------
321
330
  // Favorites
322
331
  //------------------
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>({
332
+ const favoritesChooser = hoistCmp.factory<GroupingChooserModel>({
339
333
  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
- );
334
+ const {favoritesOptions: options, isAddFavoriteEnabled} = model,
335
+ items = isEmpty(options)
336
+ ? [menuItem({text: 'No favorites saved.', disabled: true})]
337
+ : options.map(it => favoriteMenuItem(it));
360
338
 
361
339
  return vbox({
340
+ className: 'xh-grouping-chooser__favorites',
362
341
  testId,
363
- items: [div({className: 'xh-popup__title', item: 'Favorites'}), menu({items})]
342
+ items: [
343
+ div({className: 'xh-popup__title', item: 'Favorites'}),
344
+ menu({items}),
345
+ button({
346
+ text: 'Add current',
347
+ icon: Icon.add({intent: 'success'}),
348
+ className: 'xh-grouping-chooser__favorites__add-btn',
349
+ outlined: true,
350
+ omit: !isAddFavoriteEnabled,
351
+ onClick: () => model.addPendingAsFavorite()
352
+ })
353
+ ]
364
354
  });
365
355
  }
366
356
  });
@@ -369,7 +359,7 @@ const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
369
359
  render({model, value, label}) {
370
360
  return menuItem({
371
361
  text: label,
372
- className: 'xh-grouping-chooser__favorite',
362
+ className: 'xh-grouping-chooser__favorites__favorite',
373
363
  onClick: () => model.setValue(value),
374
364
  labelElement: button({
375
365
  icon: Icon.delete(),
@@ -7,6 +7,7 @@
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';
10
11
  import {button} from '@xh/hoist/mobile/cmp/button';
11
12
  import {dialog} from '@xh/hoist/mobile/cmp/dialog';
12
13
  import {textArea} from '@xh/hoist/mobile/cmp/input';
@@ -41,6 +42,9 @@ export const feedbackDialog = hoistCmp.factory({
41
42
  }),
42
43
  button({
43
44
  text: 'Send',
45
+ icon: Icon.mail(),
46
+ intent: 'primary',
47
+ outlined: true,
44
48
  onClick: () => model.submitAsync()
45
49
  })
46
50
  ]
@@ -57,8 +57,10 @@ export const optionsDialog = hoistCmp.factory({
57
57
  onClick: () => model.hide()
58
58
  }),
59
59
  button({
60
- text: 'Save',
60
+ text: 'Apply',
61
61
  icon: reloadRequired ? Icon.refresh() : Icon.check(),
62
+ intent: 'primary',
63
+ outlined: true,
62
64
  disabled: !formModel.isDirty,
63
65
  onClick: () => model.saveAsync()
64
66
  })
@@ -60,7 +60,6 @@ export const [ColChooser, colChooser] = hoistCmp.withFactory<ColChooserProps>({
60
60
  onDragEnd: impl.onDragEnd,
61
61
  items: [
62
62
  panel({
63
- title: 'Visible Columns',
64
63
  className: 'xh-col-chooser__section',
65
64
  scrollable: true,
66
65
  items: [
@@ -119,8 +118,10 @@ export const [ColChooser, colChooser] = hoistCmp.withFactory<ColChooserProps>({
119
118
  onClick: () => model.close()
120
119
  }),
121
120
  button({
122
- text: 'Save',
121
+ text: 'Apply',
123
122
  icon: Icon.check(),
123
+ intent: 'primary',
124
+ outlined: true,
124
125
  onClick: () => {
125
126
  model.commit();
126
127
  model.close();
@@ -16,18 +16,23 @@
16
16
  }
17
17
  }
18
18
 
19
+ &__editor {
20
+ .xh-panel__content {
21
+ padding: var(--xh-pad-px);
22
+ }
23
+ }
24
+
19
25
  &__row {
20
26
  display: flex;
21
27
  align-items: center;
22
28
  background-color: var(--xh-bg);
23
- height: 35px;
29
+ height: 45px;
24
30
  flex-shrink: 0;
25
31
 
26
32
  &__grabber {
27
33
  display: flex;
28
34
  align-items: center;
29
35
  justify-content: center;
30
- height: 30px;
31
36
  width: 30px;
32
37
  }
33
38
 
@@ -49,30 +54,46 @@
49
54
  }
50
55
 
51
56
  &__add-control {
52
- padding: var(--xh-pad-half-px);
57
+ // Align with already-added level controls.
58
+ padding: 5px 40px 5px 30px;
53
59
  }
54
60
 
55
- &__favorite {
56
- height: 35px;
57
- align-items: center;
61
+ &__favorites {
62
+ // Fix at 50% of dialog height, preventing panel's default flex:auto
63
+ flex: none !important;
64
+ height: 50%;
58
65
 
59
- .xh-button {
60
- justify-content: left;
66
+ // Section heading style for internal title (like colChooser)
67
+ .xh-panel-header {
68
+ background-color: var(--xh-appbar-bg);
69
+ color: var(--xh-appbar-title-color);
70
+ padding-left: var(--xh-pad-px);
61
71
  }
62
- }
63
- }
64
72
 
65
- .xh-grouping-chooser-popover {
66
- .dialog {
67
- min-width: unset;
68
- }
73
+ .xh-panel__content {
74
+ padding: var(--xh-pad-px);
75
+ }
69
76
 
70
- .xh-dialog__inner {
71
- padding: 0;
72
- }
77
+ // Empty state placeholder - don't flex as it pushes add button down.
78
+ .xh-placeholder {
79
+ flex: none;
80
+ margin: var(--xh-pad-px);
81
+ }
82
+
83
+ // Individual favorite items (hBoxes w/buttons)
84
+ &__favorite {
85
+ flex: none !important;
86
+ height: 35px;
87
+ align-items: center;
73
88
 
74
- &__content {
75
- padding: 3px var(--xh-pad-half-px);
76
- min-width: 240px; // Required to allow for toolbars
89
+ .xh-button {
90
+ justify-content: left;
91
+ }
92
+ }
93
+
94
+ &__add-btn {
95
+ width: 200px;
96
+ margin: var(--xh-pad-px) auto;
97
+ }
77
98
  }
78
99
  }