@xh/hoist 73.0.0-SNAPSHOT.1746829743606 → 73.0.0-SNAPSHOT.1746837312368
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 +15 -15
- package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +2 -2
- package/admin/tabs/userData/roles/RolePanel.ts +2 -2
- package/build/types/cmp/filter/FilterChooserModel.d.ts +1 -0
- package/build/types/desktop/cmp/filter/FilterChooser.d.ts +2 -0
- package/build/types/desktop/cmp/filter/PopoverFilterChooser.d.ts +10 -0
- package/build/types/desktop/cmp/filter/index.d.ts +1 -0
- package/build/types/mobile/cmp/navigator/NavigatorModel.d.ts +7 -1
- package/cmp/filter/FilterChooserModel.ts +4 -0
- package/cmp/tab/TabContainerModel.ts +4 -4
- package/desktop/cmp/filter/FilterChooser.scss +5 -0
- package/desktop/cmp/filter/FilterChooser.ts +60 -37
- package/desktop/cmp/filter/PopoverFilterChooser.scss +42 -0
- package/desktop/cmp/filter/PopoverFilterChooser.ts +104 -0
- package/desktop/cmp/filter/index.ts +1 -0
- package/mobile/cmp/navigator/NavigatorModel.ts +21 -8
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
applicable. This did not previously have any effect, but is required now for the superclass to
|
|
12
12
|
initialize a new `ViewManagerModel`.
|
|
13
13
|
* For clarity, [here is where Toolbox makes that call](https://github.com/xh/toolbox/blob/f15a8018ce36c2ae998b45724b48a16320b88e49/client-app/src/admin/AppModel.ts#L12).
|
|
14
|
-
|
|
14
|
+
* Requires call to `makeObservable(this)` in model constructors with `@bindable` (see below).
|
|
15
15
|
|
|
16
16
|
### 🎁 New Features
|
|
17
17
|
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
the "skip weekends" option.
|
|
28
28
|
* Client Error reports and user feedback have also been consolidated into the new tracking
|
|
29
29
|
system for more integrated and powerful reporting.
|
|
30
|
-
|
|
30
|
+
* Added new `PopoverFilterChooser` component - wraps `FilterChooser` in a `Popover` to allow it to
|
|
31
|
+
expand vertically when used in a `Toolbar` or other space-constrained, single-line layout.
|
|
31
32
|
* Updated `FormModel` to support `persistWith` for storing and recalling its values, including
|
|
32
33
|
developer options to persist all or a provided subset of fields.
|
|
33
34
|
|
|
@@ -35,17 +36,13 @@
|
|
|
35
36
|
|
|
36
37
|
* Fixed drag-and-drop usability issues with the mobile `ColChooser`.
|
|
37
38
|
* Made `GridModel.defaultGroupSortFn` null-safe and improved type signature.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
39
|
+
* Disabled `dashCanvasAddViewButton` if there are no `menuItems` to show.
|
|
40
|
+
* Hardened `@bindable` and `@persist` to handle lifecycle-related bugs. Note that previously
|
|
40
41
|
`@bindable` would work even if `makeObservable()` was not called, but this is no longer the case.
|
|
41
|
-
Please ensure
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* Corrected `GridGroupSortFn` param types.
|
|
46
|
-
* Corrected `StoreCountLabelProps` interface.
|
|
47
|
-
* Corrected `textAlign` type in `DateInputProps`, `NumberInputProps` `SearchInputProps` and
|
|
48
|
-
`TextInputProps`.
|
|
42
|
+
Please ensure you call `makeObservable(this)` in your model's constructor when using `@bindable`!
|
|
43
|
+
* Improved client `WebSocketService` heartbeat to check that it has been receiving inbound messages
|
|
44
|
+
from the server, not just successfully sending outbound heartbeats. Will auto-reconnect if needed
|
|
45
|
+
in a newly throttled/managed manner.
|
|
49
46
|
|
|
50
47
|
### ⚙️ Technical
|
|
51
48
|
|
|
@@ -59,9 +56,12 @@
|
|
|
59
56
|
as the server - would result in a false positive for an upgrade. The two should always match.
|
|
60
57
|
* Calls to `Promise.track()` that are rejected with an exception will be tracked with new
|
|
61
58
|
severity level of `TrackSeverity.ERROR`.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
|
|
60
|
+
### ⚙️ Typescript API Adjustments
|
|
61
|
+
|
|
62
|
+
* Corrected `GridGroupSortFn` param types.
|
|
63
|
+
* Corrected `StoreCountLabelProps` interface.
|
|
64
|
+
* Corrected `textAlign` type across several `HoistInput` prop interfaces.
|
|
65
65
|
|
|
66
66
|
## v72.5.1 - 2025-04-15
|
|
67
67
|
|
|
@@ -11,7 +11,7 @@ import {grid} from '@xh/hoist/cmp/grid';
|
|
|
11
11
|
import {div, filler, hframe} from '@xh/hoist/cmp/layout';
|
|
12
12
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
13
13
|
import {button, buttonGroup, colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button';
|
|
14
|
-
import {
|
|
14
|
+
import {popoverFilterChooser} from '@xh/hoist/desktop/cmp/filter';
|
|
15
15
|
import {formField} from '@xh/hoist/desktop/cmp/form';
|
|
16
16
|
import {groupingChooser} from '@xh/hoist/desktop/cmp/grouping';
|
|
17
17
|
import {dateInput, DateInputProps, select} from '@xh/hoist/desktop/cmp/input';
|
|
@@ -137,7 +137,7 @@ const filterChooserToggleButton = hoistCmp.factory<ActivityTrackingModel>(({mode
|
|
|
137
137
|
const filterBar = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
|
|
138
138
|
return model.showFilterChooser
|
|
139
139
|
? toolbar(
|
|
140
|
-
|
|
140
|
+
popoverFilterChooser({
|
|
141
141
|
flex: 1,
|
|
142
142
|
enableClear: true
|
|
143
143
|
})
|
|
@@ -10,7 +10,7 @@ import {fragment, hframe, vframe} from '@xh/hoist/cmp/layout';
|
|
|
10
10
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
11
11
|
import {button, colChooserButton} from '@xh/hoist/desktop/cmp/button';
|
|
12
12
|
import {errorMessage} from '@xh/hoist/cmp/error';
|
|
13
|
-
import {
|
|
13
|
+
import {popoverFilterChooser} from '@xh/hoist/desktop/cmp/filter';
|
|
14
14
|
import {switchInput} from '@xh/hoist/desktop/cmp/input';
|
|
15
15
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
16
16
|
import {recordActionBar} from '@xh/hoist/desktop/cmp/record';
|
|
@@ -44,7 +44,7 @@ export const rolePanel = hoistCmp.factory({
|
|
|
44
44
|
selModel: gridModel.selModel
|
|
45
45
|
}),
|
|
46
46
|
'-',
|
|
47
|
-
|
|
47
|
+
popoverFilterChooser({flex: 1}),
|
|
48
48
|
'-',
|
|
49
49
|
switchInput({
|
|
50
50
|
bind: 'showInGroups',
|
|
@@ -79,6 +79,7 @@ export declare class FilterChooserModel extends HoistModel {
|
|
|
79
79
|
favoritesIsOpen: boolean;
|
|
80
80
|
unsupportedFilter: boolean;
|
|
81
81
|
inputRef: import("react").RefObject<HTMLElement> & import("react").RefCallback<HTMLElement>;
|
|
82
|
+
get tagCount(): number;
|
|
82
83
|
constructor({ fieldSpecs, fieldSpecDefaults, bind, valueSource, initialValue, initialFavorites, suggestFieldsWhenEmpty, sortFieldSuggestions, maxTags, maxResults, persistWith, introHelpText }?: FilterChooserConfig);
|
|
83
84
|
/**
|
|
84
85
|
* Set the value displayed by this control.
|
|
@@ -10,6 +10,8 @@ export interface FilterChooserProps extends HoistProps<FilterChooserModel>, Layo
|
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
/** True to show a "clear" button at the right of the control. Defaults to true. */
|
|
12
12
|
enableClear?: boolean;
|
|
13
|
+
/** True to show count of filter tags next to the left icon. */
|
|
14
|
+
displayCount?: boolean;
|
|
13
15
|
/** Icon to display inline on the left side of the input. */
|
|
14
16
|
leftIcon?: ReactElement;
|
|
15
17
|
/** Max-height of dropdown. Either a number in pixels or a valid CSS string, such as '80vh'. */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import '@xh/hoist/desktop/register';
|
|
3
|
+
import './PopoverFilterChooser.scss';
|
|
4
|
+
import { FilterChooserProps } from './FilterChooser';
|
|
5
|
+
/**
|
|
6
|
+
* A wrapper around a FilterChooser that renders in a popover when opened, allowing it to expand
|
|
7
|
+
* vertically beyond the height of a toolbar.
|
|
8
|
+
* @see FilterChooser
|
|
9
|
+
*/
|
|
10
|
+
export declare const PopoverFilterChooser: import("react").FC<FilterChooserProps>, popoverFilterChooser: import("@xh/hoist/core").ElementFactory<FilterChooserProps>;
|
|
@@ -27,6 +27,10 @@ export interface NavigatorConfig {
|
|
|
27
27
|
* See enum for description of supported modes.
|
|
28
28
|
*/
|
|
29
29
|
refreshMode?: RefreshMode;
|
|
30
|
+
/**
|
|
31
|
+
* Base route name for this navigator, with the route for each page being "[route]/[page.id]".
|
|
32
|
+
*/
|
|
33
|
+
route?: string;
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
36
|
* Model for handling stack-based navigation between pages.
|
|
@@ -34,6 +38,8 @@ export interface NavigatorConfig {
|
|
|
34
38
|
*/
|
|
35
39
|
export declare class NavigatorModel extends HoistModel {
|
|
36
40
|
disableAppRefreshButton: boolean;
|
|
41
|
+
readonly route: string;
|
|
42
|
+
readonly routePrefix: string;
|
|
37
43
|
stack: PageModel[];
|
|
38
44
|
pages: PageConfig[];
|
|
39
45
|
track: boolean;
|
|
@@ -49,7 +55,7 @@ export declare class NavigatorModel extends HoistModel {
|
|
|
49
55
|
get activePageIdx(): number;
|
|
50
56
|
get allowSlideNext(): boolean;
|
|
51
57
|
get allowSlidePrev(): boolean;
|
|
52
|
-
constructor({ pages, track, pullDownToRefresh, transitionMs, renderMode, refreshMode }: NavigatorConfig);
|
|
58
|
+
constructor({ pages, route, track, pullDownToRefresh, transitionMs, renderMode, refreshMode }: NavigatorConfig);
|
|
53
59
|
/**
|
|
54
60
|
* @param callback - function to execute (once) after the next page transition.
|
|
55
61
|
*/
|
|
@@ -145,6 +145,10 @@ export class FilterChooserModel extends HoistModel {
|
|
|
145
145
|
@observable unsupportedFilter = false;
|
|
146
146
|
inputRef = createObservableRef<HTMLElement>();
|
|
147
147
|
|
|
148
|
+
get tagCount(): number {
|
|
149
|
+
return this.selectValue?.length ?? 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
148
152
|
constructor({
|
|
149
153
|
fieldSpecs,
|
|
150
154
|
fieldSpecDefaults,
|
|
@@ -144,10 +144,10 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
144
144
|
this.refreshContextModel.xhImpl = xhImpl;
|
|
145
145
|
|
|
146
146
|
if (route) {
|
|
147
|
-
if (XH.isMobileApp) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
147
|
+
// if (XH.isMobileApp) {
|
|
148
|
+
// this.logWarn('TabContainer routing is not supported for mobile applications.');
|
|
149
|
+
// return;
|
|
150
|
+
// }
|
|
151
151
|
|
|
152
152
|
this.addReaction({
|
|
153
153
|
track: () => XH.routerState,
|
|
@@ -16,6 +16,7 @@ import {withDefault} from '@xh/hoist/utils/js';
|
|
|
16
16
|
import {splitLayoutProps} from '@xh/hoist/utils/react';
|
|
17
17
|
import classNames from 'classnames';
|
|
18
18
|
import {isEmpty, sortBy} from 'lodash';
|
|
19
|
+
import {badge} from '@xh/hoist/cmp/badge';
|
|
19
20
|
import {ReactElement} from 'react';
|
|
20
21
|
import './FilterChooser.scss';
|
|
21
22
|
|
|
@@ -26,6 +27,8 @@ export interface FilterChooserProps extends HoistProps<FilterChooserModel>, Layo
|
|
|
26
27
|
disabled?: boolean;
|
|
27
28
|
/** True to show a "clear" button at the right of the control. Defaults to true. */
|
|
28
29
|
enableClear?: boolean;
|
|
30
|
+
/** True to show count of filter tags next to the left icon. */
|
|
31
|
+
displayCount?: boolean;
|
|
29
32
|
/** Icon to display inline on the left side of the input. */
|
|
30
33
|
leftIcon?: ReactElement;
|
|
31
34
|
/** Max-height of dropdown. Either a number in pixels or a valid CSS string, such as '80vh'. */
|
|
@@ -47,10 +50,23 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory<FilterChooser
|
|
|
47
50
|
className: 'xh-filter-chooser',
|
|
48
51
|
render({model, className, ...props}, ref) {
|
|
49
52
|
const [layoutProps, chooserProps] = splitLayoutProps(props),
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
{
|
|
54
|
+
inputRef,
|
|
55
|
+
suggestFieldsWhenEmpty,
|
|
56
|
+
selectOptions,
|
|
57
|
+
unsupportedFilter,
|
|
58
|
+
favoritesIsOpen,
|
|
59
|
+
tagCount
|
|
60
|
+
} = model,
|
|
61
|
+
{
|
|
62
|
+
autoFocus,
|
|
63
|
+
enableClear,
|
|
64
|
+
displayCount,
|
|
65
|
+
leftIcon,
|
|
66
|
+
maxMenuHeight,
|
|
67
|
+
menuPlacement,
|
|
68
|
+
menuWidth
|
|
69
|
+
} = chooserProps,
|
|
54
70
|
disabled = unsupportedFilter || chooserProps.disabled,
|
|
55
71
|
placeholder = unsupportedFilter
|
|
56
72
|
? 'Unsupported filter (click to clear)'
|
|
@@ -61,42 +77,49 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory<FilterChooser
|
|
|
61
77
|
className,
|
|
62
78
|
...layoutProps,
|
|
63
79
|
item: popover({
|
|
64
|
-
item:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
item: hframe(
|
|
81
|
+
badge({
|
|
82
|
+
omit: !displayCount || tagCount < 1,
|
|
83
|
+
className: 'xh-filter-chooser__count',
|
|
84
|
+
item: tagCount
|
|
85
|
+
}),
|
|
86
|
+
select({
|
|
87
|
+
flex: 1,
|
|
88
|
+
height: layoutProps?.height,
|
|
89
|
+
bind: 'selectValue',
|
|
90
|
+
ref: inputRef,
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
92
|
+
autoFocus,
|
|
93
|
+
disabled,
|
|
94
|
+
menuPlacement,
|
|
95
|
+
menuWidth,
|
|
96
|
+
placeholder,
|
|
97
|
+
leftIcon: withDefault(leftIcon, Icon.filter()),
|
|
98
|
+
enableClear: withDefault(enableClear, true),
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
enableMulti: true,
|
|
101
|
+
queryFn: q => model.queryAsync(q),
|
|
102
|
+
options: selectOptions,
|
|
103
|
+
optionRenderer,
|
|
104
|
+
rsOptions: {
|
|
105
|
+
defaultOptions: suggestFieldsWhenEmpty,
|
|
106
|
+
openMenuOnClick: suggestFieldsWhenEmpty,
|
|
107
|
+
openMenuOnFocus: false,
|
|
108
|
+
isOptionDisabled: opt => opt.type === 'msg',
|
|
109
|
+
noOptionsMessage: () => null,
|
|
110
|
+
loadingMessage: () => null,
|
|
111
|
+
styles: {
|
|
112
|
+
menuList: base => ({
|
|
113
|
+
...base,
|
|
114
|
+
maxHeight: withDefault(maxMenuHeight, '50vh')
|
|
115
|
+
})
|
|
116
|
+
},
|
|
117
|
+
components: {
|
|
118
|
+
DropdownIndicator: () => favoritesIcon(model)
|
|
119
|
+
}
|
|
97
120
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
121
|
+
})
|
|
122
|
+
),
|
|
100
123
|
content: favoritesMenu(),
|
|
101
124
|
isOpen: favoritesIsOpen,
|
|
102
125
|
position: 'bottom-right',
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file belongs to Hoist, an application development toolkit
|
|
3
|
+
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
+
*
|
|
5
|
+
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
+
*/
|
|
7
|
+
.xh-popover-filter-chooser {
|
|
8
|
+
& > .bp5-popover-target {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex: 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Extra class names required to override the default styles of the popover
|
|
14
|
+
&__popover.bp5-popover.bp5-minimal {
|
|
15
|
+
margin-top: -15px !important;
|
|
16
|
+
box-shadow: none;
|
|
17
|
+
|
|
18
|
+
.bp5-popover-content {
|
|
19
|
+
background: transparent;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.xh-select__value-container--is-multi {
|
|
23
|
+
height: unset;
|
|
24
|
+
line-height: unset;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&__filter-chooser {
|
|
29
|
+
.xh-select__value-container--is-multi {
|
|
30
|
+
overflow-y: hidden !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.xh-select {
|
|
34
|
+
.xh-select__control--is-disabled {
|
|
35
|
+
background: var(--xh-input-bg);
|
|
36
|
+
}
|
|
37
|
+
.xh-select__multi-value--is-disabled .xh-select__multi-value__label {
|
|
38
|
+
color: unset;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file belongs to Hoist, an application development toolkit
|
|
3
|
+
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
+
*
|
|
5
|
+
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {hoistCmp, HoistModel, lookup, useLocalModel, uses} from '@xh/hoist/core';
|
|
9
|
+
import {bindable, makeObservable} from '@xh/hoist/mobx';
|
|
10
|
+
import {box, hframe} from '@xh/hoist/cmp/layout';
|
|
11
|
+
import '@xh/hoist/desktop/register';
|
|
12
|
+
import {popover} from '@xh/hoist/kit/blueprint';
|
|
13
|
+
import {getLayoutProps} from '@xh/hoist/utils/react';
|
|
14
|
+
import './PopoverFilterChooser.scss';
|
|
15
|
+
import {filterChooser, FilterChooserProps} from './FilterChooser';
|
|
16
|
+
import {FilterChooserModel} from '@xh/hoist/cmp/filter';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A wrapper around a FilterChooser that renders in a popover when opened, allowing it to expand
|
|
20
|
+
* vertically beyond the height of a toolbar.
|
|
21
|
+
* @see FilterChooser
|
|
22
|
+
*/
|
|
23
|
+
export const [PopoverFilterChooser, popoverFilterChooser] =
|
|
24
|
+
hoistCmp.withFactory<FilterChooserProps>({
|
|
25
|
+
model: uses(FilterChooserModel),
|
|
26
|
+
className: 'xh-popover-filter-chooser',
|
|
27
|
+
render({model, className, ...props}, ref) {
|
|
28
|
+
const layoutProps = getLayoutProps(props),
|
|
29
|
+
impl = useLocalModel(PopoverFilterChooserLocalModel);
|
|
30
|
+
|
|
31
|
+
return box({
|
|
32
|
+
ref,
|
|
33
|
+
className,
|
|
34
|
+
...layoutProps,
|
|
35
|
+
item: popover({
|
|
36
|
+
isOpen: impl.popoverIsOpen,
|
|
37
|
+
popoverClassName: 'xh-popover-filter-chooser__popover',
|
|
38
|
+
item: hframe(
|
|
39
|
+
filterChooser({
|
|
40
|
+
model,
|
|
41
|
+
// Omit when popover is open to force update the inputRef
|
|
42
|
+
omit: impl.popoverIsOpen,
|
|
43
|
+
className: 'xh-popover-filter-chooser__filter-chooser',
|
|
44
|
+
displayCount: true,
|
|
45
|
+
...props,
|
|
46
|
+
disabled: true
|
|
47
|
+
})
|
|
48
|
+
),
|
|
49
|
+
content: filterChooser({
|
|
50
|
+
model,
|
|
51
|
+
displayCount: true,
|
|
52
|
+
...props
|
|
53
|
+
}),
|
|
54
|
+
matchTargetWidth: true,
|
|
55
|
+
minimal: true,
|
|
56
|
+
position: 'bottom',
|
|
57
|
+
onInteraction: open => {
|
|
58
|
+
if (open) {
|
|
59
|
+
impl.open();
|
|
60
|
+
} else {
|
|
61
|
+
impl.close();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
class PopoverFilterChooserLocalModel extends HoistModel {
|
|
70
|
+
override xhImpl = true;
|
|
71
|
+
|
|
72
|
+
@lookup(FilterChooserModel)
|
|
73
|
+
model: FilterChooserModel;
|
|
74
|
+
|
|
75
|
+
@bindable
|
|
76
|
+
popoverIsOpen: boolean = false;
|
|
77
|
+
|
|
78
|
+
get displaySelectValue() {
|
|
79
|
+
return this.model.selectValue[0];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
constructor() {
|
|
83
|
+
super();
|
|
84
|
+
makeObservable(this);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
open() {
|
|
88
|
+
this.popoverIsOpen = true;
|
|
89
|
+
|
|
90
|
+
// Focus and open the menu when rendered
|
|
91
|
+
this.addReaction({
|
|
92
|
+
when: () => !!this.model.inputRef.current,
|
|
93
|
+
run: () => {
|
|
94
|
+
const inputRef = this.model.inputRef.current;
|
|
95
|
+
inputRef.focus();
|
|
96
|
+
(inputRef as any).reactSelectRef.current.select.onMenuOpen();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
close() {
|
|
102
|
+
this.popoverIsOpen = false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -44,6 +44,11 @@ export interface NavigatorConfig {
|
|
|
44
44
|
* See enum for description of supported modes.
|
|
45
45
|
*/
|
|
46
46
|
refreshMode?: RefreshMode;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Base route name for this navigator, with the route for each page being "[route]/[page.id]".
|
|
50
|
+
*/
|
|
51
|
+
route?: string;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/**
|
|
@@ -53,6 +58,9 @@ export interface NavigatorConfig {
|
|
|
53
58
|
export class NavigatorModel extends HoistModel {
|
|
54
59
|
@bindable disableAppRefreshButton: boolean;
|
|
55
60
|
|
|
61
|
+
readonly route: string;
|
|
62
|
+
readonly routePrefix: string;
|
|
63
|
+
|
|
56
64
|
@bindable.ref
|
|
57
65
|
stack: PageModel[] = [];
|
|
58
66
|
|
|
@@ -89,6 +97,7 @@ export class NavigatorModel extends HoistModel {
|
|
|
89
97
|
|
|
90
98
|
constructor({
|
|
91
99
|
pages,
|
|
100
|
+
route,
|
|
92
101
|
track = false,
|
|
93
102
|
pullDownToRefresh = true,
|
|
94
103
|
transitionMs = 500,
|
|
@@ -101,6 +110,9 @@ export class NavigatorModel extends HoistModel {
|
|
|
101
110
|
ensureNotEmpty(pages, 'NavigatorModel needs at least one page.');
|
|
102
111
|
ensureUniqueBy(pages, 'id', 'Multiple NavigatorModel PageModels have the same id.');
|
|
103
112
|
|
|
113
|
+
this.route = route ?? '';
|
|
114
|
+
this.routePrefix = route ? route.substring(0, route.lastIndexOf('.') + 1) : '';
|
|
115
|
+
|
|
104
116
|
this.pages = pages;
|
|
105
117
|
this.track = track;
|
|
106
118
|
this.pullDownToRefresh = pullDownToRefresh;
|
|
@@ -209,7 +221,7 @@ export class NavigatorModel extends HoistModel {
|
|
|
209
221
|
this.stack = this.stack.slice(0, this._swiper.activeIndex + 1);
|
|
210
222
|
|
|
211
223
|
// 2) Sync route to match the current page stack
|
|
212
|
-
const newRouteName = this.stack.map(it => it.id).join('.'),
|
|
224
|
+
const newRouteName = this.routePrefix + this.stack.map(it => it.id).join('.'),
|
|
213
225
|
newRouteParams = mergeDeep({}, ...this.stack.map(it => it.props));
|
|
214
226
|
|
|
215
227
|
XH.navigate(newRouteName, newRouteParams);
|
|
@@ -226,16 +238,17 @@ export class NavigatorModel extends HoistModel {
|
|
|
226
238
|
};
|
|
227
239
|
|
|
228
240
|
private onRouteChange(init: boolean = false) {
|
|
229
|
-
|
|
241
|
+
const {route: myRoute, routePrefix: myRoutePrefix, _swiper} = this;
|
|
242
|
+
if (!XH.routerState || (myRoute && !XH.router.isActive(myRoute)) || !_swiper) return;
|
|
230
243
|
|
|
231
|
-
// Break the current route name into parts,
|
|
232
|
-
// Use meta.params to determine which params are associated
|
|
233
|
-
// Save these params to use as props for the page.
|
|
244
|
+
// Break the current route name into parts, only looking at our part of it (myRoute and below).
|
|
245
|
+
// Collect any params for each part. Use meta.params to determine which params are associated
|
|
246
|
+
// with each route part. Save these params to use as props for the page.
|
|
234
247
|
const {meta, name, params} = XH.routerState,
|
|
235
|
-
parts = name.split('.');
|
|
248
|
+
parts = name.replace(myRoutePrefix, '').split('.');
|
|
236
249
|
|
|
237
250
|
const routeParts = parts.map((id, idx) => {
|
|
238
|
-
const metaKey = parts.slice(0, idx + 1).join('.'),
|
|
251
|
+
const metaKey = myRoutePrefix + parts.slice(0, idx + 1).join('.'),
|
|
239
252
|
props = {};
|
|
240
253
|
|
|
241
254
|
// Extract props for this part
|
|
@@ -262,7 +275,7 @@ export class NavigatorModel extends HoistModel {
|
|
|
262
275
|
// we drop the rest of the route and redirect to the route so far
|
|
263
276
|
if (init && pageModelCfg.disableDirectLink) {
|
|
264
277
|
const completedRouteParts = routeParts.slice(0, i),
|
|
265
|
-
newRouteName = completedRouteParts.map(it => it.id).join('.'),
|
|
278
|
+
newRouteName = myRoutePrefix + completedRouteParts.map(it => it.id).join('.'),
|
|
266
279
|
newRouteParams = mergeDeep({}, ...completedRouteParts.map(it => it.props));
|
|
267
280
|
|
|
268
281
|
XH.navigate(newRouteName, newRouteParams, {replace: true});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "73.0.0-SNAPSHOT.
|
|
3
|
+
"version": "73.0.0-SNAPSHOT.1746837312368",
|
|
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",
|