@xh/hoist 74.0.0 → 74.1.1
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 +36 -4
- package/build/types/cmp/grouping/GroupingChooserModel.d.ts +8 -4
- package/build/types/cmp/viewmanager/ViewInfo.d.ts +7 -3
- package/build/types/core/types/Interfaces.d.ts +1 -1
- package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +17 -6
- package/build/types/desktop/cmp/panel/Panel.d.ts +1 -1
- package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +26 -10
- package/build/types/mobile/cmp/grouping/GroupingChooser.d.ts +2 -6
- package/build/types/utils/impl/MenuItems.d.ts +13 -0
- package/build/types/utils/impl/index.d.ts +1 -0
- package/cmp/grouping/GroupingChooserModel.ts +25 -12
- package/cmp/viewmanager/ViewInfo.ts +7 -3
- package/core/HoistAppModel.ts +1 -0
- package/core/exception/ExceptionHandler.ts +1 -1
- package/core/types/Interfaces.ts +2 -2
- package/desktop/cmp/button/AppMenuButton.ts +3 -46
- package/desktop/cmp/grouping/GroupingChooser.scss +39 -35
- package/desktop/cmp/grouping/GroupingChooser.ts +157 -89
- package/desktop/cmp/panel/Panel.ts +1 -1
- package/desktop/cmp/viewmanager/ViewManager.ts +58 -16
- package/desktop/cmp/viewmanager/ViewMenu.ts +9 -2
- package/mobile/appcontainer/FeedbackDialog.ts +4 -0
- package/mobile/appcontainer/OptionsDialog.ts +3 -1
- package/mobile/cmp/grid/impl/ColChooser.ts +3 -2
- package/mobile/cmp/grouping/GroupingChooser.scss +41 -20
- package/mobile/cmp/grouping/GroupingChooser.ts +60 -89
- package/mobile/cmp/panel/DialogPanel.scss +5 -0
- package/package.json +1 -1
- package/svc/TrackService.ts +4 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/impl/MenuItems.ts +57 -0
- package/utils/impl/index.ts +1 -0
- package/utils/js/LangUtils.ts +1 -1
|
@@ -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:
|
|
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
|
-
|
|
57
|
+
// Align with already-added level controls.
|
|
58
|
+
padding: 5px 40px 5px 30px;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
&
|
|
56
|
-
height:
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
73
|
+
.xh-panel__content {
|
|
74
|
+
padding: var(--xh-pad-px);
|
|
75
|
+
}
|
|
69
76
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
}
|
|
@@ -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
|
|
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
|
-
|
|
57
|
+
dialogCmp({dialogTitle, emptyText})
|
|
71
58
|
]
|
|
72
59
|
});
|
|
73
60
|
}
|
|
74
61
|
});
|
|
75
62
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
|
219
|
+
const favoritesChooser = hoistCmp.factory<GroupingChooserModel>({
|
|
251
220
|
render({model}) {
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
|
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-
|
|
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.
|
|
257
|
+
model.closeEditor();
|
|
287
258
|
}
|
|
288
259
|
}),
|
|
289
260
|
button({
|
package/package.json
CHANGED
package/svc/TrackService.ts
CHANGED
|
@@ -132,10 +132,11 @@ 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
|
-
|
|
135
|
+
const {maxDataLength} = this.conf,
|
|
136
|
+
dataLength = JSON.stringify(ret.data)?.length ?? 0;
|
|
137
|
+
if (dataLength > maxDataLength) {
|
|
137
138
|
this.logWarn(
|
|
138
|
-
`Track log includes ${
|
|
139
|
+
`Track log includes ${dataLength} chars of JSON data`,
|
|
139
140
|
`exceeds limit of ${maxDataLength}`,
|
|
140
141
|
'data will not be persisted',
|
|
141
142
|
options.data
|