@xh/hoist 74.1.2 → 75.0.0-SNAPSHOT.1750963411217

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 (31) hide show
  1. package/CHANGELOG.md +5 -37
  2. package/build/types/cmp/grouping/GroupingChooserModel.d.ts +4 -8
  3. package/build/types/core/types/Interfaces.d.ts +1 -1
  4. package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +6 -17
  5. package/build/types/desktop/cmp/panel/Panel.d.ts +1 -1
  6. package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +1 -8
  7. package/build/types/mobile/cmp/grouping/GroupingChooser.d.ts +6 -2
  8. package/build/types/utils/impl/index.d.ts +0 -1
  9. package/cmp/grouping/GroupingChooserModel.ts +12 -25
  10. package/core/HoistAppModel.ts +0 -1
  11. package/core/exception/ExceptionHandler.ts +1 -1
  12. package/core/types/Interfaces.ts +2 -2
  13. package/desktop/cmp/button/AppMenuButton.ts +46 -3
  14. package/desktop/cmp/grouping/GroupingChooser.scss +40 -45
  15. package/desktop/cmp/grouping/GroupingChooser.ts +89 -159
  16. package/desktop/cmp/panel/Panel.ts +1 -1
  17. package/desktop/cmp/viewmanager/ViewManager.ts +3 -11
  18. package/desktop/cmp/viewmanager/ViewMenu.ts +2 -9
  19. package/mobile/appcontainer/FeedbackDialog.ts +0 -4
  20. package/mobile/appcontainer/OptionsDialog.ts +1 -3
  21. package/mobile/cmp/grid/impl/ColChooser.ts +2 -3
  22. package/mobile/cmp/grouping/GroupingChooser.scss +20 -41
  23. package/mobile/cmp/grouping/GroupingChooser.ts +89 -60
  24. package/mobile/cmp/panel/DialogPanel.scss +0 -5
  25. package/package.json +1 -1
  26. package/svc/TrackService.ts +3 -4
  27. package/tsconfig.tsbuildinfo +1 -1
  28. package/utils/impl/index.ts +0 -1
  29. package/utils/js/LangUtils.ts +1 -1
  30. package/build/types/utils/impl/MenuItems.d.ts +0 -13
  31. package/utils/impl/MenuItems.ts +0 -57
@@ -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} from '@xh/hoist/cmp/layout';
8
+ import {box, div, filler, hbox, placeholder, span, vbox, vframe} 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';
13
14
  import {select} from '@xh/hoist/mobile/cmp/input';
14
15
  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,10 +20,14 @@ 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;
25
23
  /** Text to represent empty state (i.e. value = null or [])*/
26
24
  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;
27
31
  }
28
32
 
29
33
  /**
@@ -36,7 +40,15 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
36
40
  className: 'xh-grouping-chooser',
37
41
 
38
42
  render(
39
- {model, className, dialogTitle = 'Choose Group By', emptyText = 'Ungrouped', ...rest},
43
+ {
44
+ model,
45
+ className,
46
+ emptyText = 'Ungrouped',
47
+ popoverWidth = 270,
48
+ popoverMinHeight,
49
+ popoverTitle = 'Group By',
50
+ ...rest
51
+ },
40
52
  ref
41
53
  ) {
42
54
  const {value, allowEmpty} = model,
@@ -48,55 +60,74 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
48
60
  className,
49
61
  ...layoutProps,
50
62
  items: [
63
+ popoverCmp({popoverTitle, popoverWidth, popoverMinHeight, emptyText}),
51
64
  button({
52
65
  className: 'xh-grouping-chooser-button',
53
66
  item: span(label),
54
67
  ...buttonProps,
55
68
  onClick: () => model.toggleEditor()
56
69
  }),
57
- dialogCmp({dialogTitle, emptyText})
70
+ favoritesButton()
58
71
  ]
59
72
  });
60
73
  }
61
74
  });
62
75
 
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
- ]
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
+ ]
86
121
  });
87
122
  }
88
- });
123
+ );
89
124
 
90
125
  //------------------
91
126
  // Editor
92
127
  //------------------
93
128
  const editor = hoistCmp.factory({
94
129
  render({emptyText}) {
95
- return panel({
96
- className: 'xh-grouping-chooser__editor',
97
- scrollable: true,
98
- items: [dimensionList({emptyText}), addDimensionControl()]
99
- });
130
+ return vbox(dimensionList({emptyText}), addDimensionControl());
100
131
  }
101
132
  });
102
133
 
@@ -191,7 +222,7 @@ const addDimensionControl = hoistCmp.factory<GroupingChooserModel>({
191
222
  // ensure the Select loses its internal input state.
192
223
  key: JSON.stringify(options),
193
224
  options,
194
- placeholder: 'Add level...',
225
+ placeholder: 'Add...',
195
226
  flex: 1,
196
227
  width: null,
197
228
  hideDropdownIndicator: true,
@@ -216,37 +247,35 @@ function getDimOptions(dims, model) {
216
247
  //------------------
217
248
  // Favorites
218
249
  //------------------
219
- const favoritesChooser = hoistCmp.factory<GroupingChooserModel>({
250
+ const favoritesButton = hoistCmp.factory<GroupingChooserModel>({
220
251
  render({model}) {
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',
252
+ if (!model.persistFavorites) return null;
253
+ return button({
228
254
  icon: Icon.favorite(),
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
- ]
255
+ minimal: true,
256
+ className: 'xh-grouping-chooser__favorite-button',
257
+ onClick: () => model.toggleFavoritesMenu()
242
258
  });
243
259
  }
244
260
  });
245
261
 
246
- const favoriteItem = hoistCmp.factory<GroupingChooserModel>({
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>({
247
276
  render({model, value, label}) {
248
277
  return hbox({
249
- className: 'xh-grouping-chooser__favorites__favorite',
278
+ className: 'xh-grouping-chooser__favorite',
250
279
  items: [
251
280
  button({
252
281
  text: label,
@@ -254,7 +283,7 @@ const favoriteItem = hoistCmp.factory<GroupingChooserModel>({
254
283
  flex: 1,
255
284
  onClick: () => {
256
285
  model.setValue(value);
257
- model.closeEditor();
286
+ model.closePopover();
258
287
  }
259
288
  }),
260
289
  button({
@@ -23,11 +23,6 @@
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
- }
31
26
  }
32
27
  }
33
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "74.1.2",
3
+ "version": "75.0.0-SNAPSHOT.1750963411217",
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",
@@ -132,11 +132,10 @@ export class TrackService extends HoistService {
132
132
  if (options.logData !== undefined) ret.logData = options.logData;
133
133
  if (options.elapsed !== undefined) ret.elapsed = options.elapsed;
134
134
 
135
- const {maxDataLength} = this.conf,
136
- dataLength = JSON.stringify(ret.data)?.length ?? 0;
137
- if (dataLength > maxDataLength) {
135
+ const {maxDataLength} = this.conf;
136
+ if (ret.data?.length > maxDataLength) {
138
137
  this.logWarn(
139
- `Track log includes ${dataLength} chars of JSON data`,
138
+ `Track log includes ${ret.data.length} chars of JSON data`,
140
139
  `exceeds limit of ${maxDataLength}`,
141
140
  'data will not be persisted',
142
141
  options.data