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

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.
@@ -5,14 +5,14 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
8
- import {box, div, filler, hbox, placeholder, span, vbox, vframe} from '@xh/hoist/cmp/layout';
8
+ import {box, div, filler, hbox, placeholder, span} from '@xh/hoist/cmp/layout';
9
9
  import {hoistCmp, uses} from '@xh/hoist/core';
10
10
  import {Icon} from '@xh/hoist/icon';
11
11
  import {dragDropContext, draggable, droppable} from '@xh/hoist/kit/react-beautiful-dnd';
12
12
  import {button, ButtonProps} from '@xh/hoist/mobile/cmp/button';
13
- import {dialog} from '@xh/hoist/mobile/cmp/dialog';
14
13
  import {select} from '@xh/hoist/mobile/cmp/input';
15
14
  import '@xh/hoist/mobile/register';
15
+ import {dialogPanel, panel} from '@xh/hoist/mobile/cmp/panel';
16
16
  import {splitLayoutProps} from '@xh/hoist/utils/react';
17
17
  import classNames from 'classnames';
18
18
  import {compact, isEmpty, sortBy} from 'lodash';
@@ -20,14 +20,10 @@ import {compact, isEmpty, sortBy} from 'lodash';
20
20
  import './GroupingChooser.scss';
21
21
 
22
22
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
23
+ /** Custom title for editor dialog, or null to suppress. */
24
+ dialogTitle?: string;
23
25
  /** Text to represent empty state (i.e. value = null or [])*/
24
26
  emptyText?: string;
25
- /** Title for popover (default "GROUP BY") or null to suppress. */
26
- popoverTitle?: string;
27
- /** Min height in pixels of the popover inner content (excl. header & toolbar). */
28
- popoverMinHeight?: number;
29
- /** Width in pixels of the popover menu itself. */
30
- popoverWidth?: number;
31
27
  }
32
28
 
33
29
  /**
@@ -40,15 +36,7 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
40
36
  className: 'xh-grouping-chooser',
41
37
 
42
38
  render(
43
- {
44
- model,
45
- className,
46
- emptyText = 'Ungrouped',
47
- popoverWidth = 270,
48
- popoverMinHeight,
49
- popoverTitle = 'Group By',
50
- ...rest
51
- },
39
+ {model, className, dialogTitle = 'Choose Group By', emptyText = 'Ungrouped', ...rest},
52
40
  ref
53
41
  ) {
54
42
  const {value, allowEmpty} = model,
@@ -60,74 +48,55 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
60
48
  className,
61
49
  ...layoutProps,
62
50
  items: [
63
- popoverCmp({popoverTitle, popoverWidth, popoverMinHeight, emptyText}),
64
51
  button({
65
52
  className: 'xh-grouping-chooser-button',
66
53
  item: span(label),
67
54
  ...buttonProps,
68
55
  onClick: () => model.toggleEditor()
69
56
  }),
70
- favoritesButton()
57
+ dialogCmp({dialogTitle, emptyText})
71
58
  ]
72
59
  });
73
60
  }
74
61
  });
75
62
 
76
- //---------------------------
77
- // Popover
78
- //---------------------------
79
- const popoverCmp = hoistCmp.factory<GroupingChooserModel>(
80
- ({model, popoverTitle, popoverWidth, popoverMinHeight, emptyText}) => {
81
- const {editorIsOpen, favoritesIsOpen, isValid, value} = model,
82
- isOpen = editorIsOpen || favoritesIsOpen,
83
- addFavoriteDisabled = isEmpty(value) || !!model.isFavorite(value);
84
-
85
- return dialog({
86
- isOpen,
87
- title: favoritesIsOpen ? 'Favorites' : popoverTitle,
88
- icon: favoritesIsOpen ? Icon.favorite({prefix: 'fas'}) : Icon.treeList(),
89
- className: 'xh-grouping-chooser-popover',
90
- content: vframe({
91
- className: 'xh-grouping-chooser-popover__content',
92
- width: popoverWidth,
93
- minHeight: popoverMinHeight,
94
- item: favoritesIsOpen ? favoritesMenu() : editor({emptyText})
95
- }),
96
- onCancel: () => model.closePopover(),
97
- buttons: favoritesIsOpen
98
- ? [
99
- button({
100
- icon: Icon.add(),
101
- flex: 1,
102
- text: 'Add current',
103
- disabled: addFavoriteDisabled,
104
- onClick: () => model.addFavorite(model.value)
105
- })
106
- ]
107
- : [
108
- filler(),
109
- button({
110
- text: 'Cancel',
111
- minimal: true,
112
- onClick: () => model.closePopover()
113
- }),
114
- button({
115
- icon: Icon.check(),
116
- text: 'Apply',
117
- disabled: !isValid,
118
- onClick: () => model.commitPendingValueAndClose()
119
- })
120
- ]
63
+ const dialogCmp = hoistCmp.factory<GroupingChooserModel>({
64
+ render({model, dialogTitle, emptyText}) {
65
+ return dialogPanel({
66
+ isOpen: model.editorIsOpen,
67
+ title: dialogTitle,
68
+ icon: Icon.treeList(),
69
+ items: [editor({emptyText}), favoritesChooser({omit: !model.persistFavorites})],
70
+ bbar: [
71
+ filler(),
72
+ button({
73
+ text: 'Cancel',
74
+ minimal: true,
75
+ onClick: () => model.closeEditor()
76
+ }),
77
+ button({
78
+ text: 'Apply',
79
+ icon: Icon.check(),
80
+ intent: 'primary',
81
+ outlined: true,
82
+ disabled: !model.isValid,
83
+ onClick: () => model.commitPendingValueAndClose()
84
+ })
85
+ ]
121
86
  });
122
87
  }
123
- );
88
+ });
124
89
 
125
90
  //------------------
126
91
  // Editor
127
92
  //------------------
128
93
  const editor = hoistCmp.factory({
129
94
  render({emptyText}) {
130
- return vbox(dimensionList({emptyText}), addDimensionControl());
95
+ return panel({
96
+ className: 'xh-grouping-chooser__editor',
97
+ scrollable: true,
98
+ items: [dimensionList({emptyText}), addDimensionControl()]
99
+ });
131
100
  }
132
101
  });
133
102
 
@@ -222,7 +191,7 @@ const addDimensionControl = hoistCmp.factory<GroupingChooserModel>({
222
191
  // ensure the Select loses its internal input state.
223
192
  key: JSON.stringify(options),
224
193
  options,
225
- placeholder: 'Add...',
194
+ placeholder: 'Add level...',
226
195
  flex: 1,
227
196
  width: null,
228
197
  hideDropdownIndicator: true,
@@ -247,35 +216,37 @@ function getDimOptions(dims, model) {
247
216
  //------------------
248
217
  // Favorites
249
218
  //------------------
250
- const favoritesButton = hoistCmp.factory<GroupingChooserModel>({
219
+ const favoritesChooser = hoistCmp.factory<GroupingChooserModel>({
251
220
  render({model}) {
252
- if (!model.persistFavorites) return null;
253
- return button({
221
+ const {favoritesOptions: options, isAddFavoriteEnabled} = model,
222
+ items = isEmpty(options)
223
+ ? [placeholder('No favorites saved.')]
224
+ : options.map(it => favoriteItem(it));
225
+
226
+ return panel({
227
+ title: 'Favorites',
254
228
  icon: Icon.favorite(),
255
- minimal: true,
256
- className: 'xh-grouping-chooser__favorite-button',
257
- onClick: () => model.toggleFavoritesMenu()
229
+ className: 'xh-grouping-chooser__favorites',
230
+ scrollable: true,
231
+ items: [
232
+ ...items,
233
+ button({
234
+ text: 'Add current',
235
+ icon: Icon.add({intent: 'success'}),
236
+ className: 'xh-grouping-chooser__favorites__add-btn',
237
+ outlined: true,
238
+ omit: !isAddFavoriteEnabled,
239
+ onClick: () => model.addPendingAsFavorite()
240
+ })
241
+ ]
258
242
  });
259
243
  }
260
244
  });
261
245
 
262
- const favoritesMenu = hoistCmp.factory<GroupingChooserModel>({
263
- render({model}) {
264
- const options = model.favoritesOptions;
265
-
266
- if (isEmpty(options)) {
267
- return placeholder('No favorites saved...');
268
- }
269
-
270
- const items = options.map(it => favoriteMenuItem(it));
271
- return div({items});
272
- }
273
- });
274
-
275
- const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
246
+ const favoriteItem = hoistCmp.factory<GroupingChooserModel>({
276
247
  render({model, value, label}) {
277
248
  return hbox({
278
- className: 'xh-grouping-chooser__favorite',
249
+ className: 'xh-grouping-chooser__favorites__favorite',
279
250
  items: [
280
251
  button({
281
252
  text: label,
@@ -283,7 +254,7 @@ const favoriteMenuItem = hoistCmp.factory<GroupingChooserModel>({
283
254
  flex: 1,
284
255
  onClick: () => {
285
256
  model.setValue(value);
286
- model.closePopover();
257
+ model.closeEditor();
287
258
  }
288
259
  }),
289
260
  button({
@@ -23,6 +23,11 @@
23
23
  & > .xh-panel {
24
24
  width: 100%;
25
25
  height: 100%;
26
+
27
+ // Additional padding on dialog panel's own header.
28
+ > .xh-panel-header {
29
+ padding: 10px;
30
+ }
26
31
  }
27
32
  }
28
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "75.0.0-SNAPSHOT.1751070190067",
3
+ "version": "75.0.0-SNAPSHOT.1751273865753",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",