@xh/hoist 79.0.0-SNAPSHOT.1766020485210 → 79.0.0-SNAPSHOT.1766094533168
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 +8 -0
- package/admin/AppComponent.ts +9 -1
- package/admin/AppModel.ts +0 -4
- package/admin/tabs/cluster/instances/InstancesTab.ts +1 -1
- package/admin/tabs/cluster/instances/InstancesTabModel.ts +0 -1
- package/admin/tabs/userData/roles/details/RoleDetailsModel.ts +0 -1
- package/build/types/cmp/tab/TabContainer.d.ts +19 -4
- package/build/types/cmp/tab/TabContainerModel.d.ts +18 -19
- package/build/types/cmp/tab/Types.d.ts +61 -0
- package/build/types/cmp/tab/index.d.ts +1 -1
- package/build/types/data/RecordAction.d.ts +4 -1
- package/build/types/desktop/cmp/tab/TabSwitcher.d.ts +1 -1
- package/build/types/desktop/cmp/tab/dynamic/DynamicTabSwitcher.d.ts +7 -0
- package/build/types/desktop/cmp/tab/dynamic/DynamicTabSwitcherModel.d.ts +30 -0
- package/build/types/desktop/cmp/tab/dynamic/scroller/Scroller.d.ts +19 -0
- package/build/types/desktop/cmp/tab/dynamic/scroller/ScrollerModel.d.ts +23 -0
- package/build/types/desktop/cmp/tab/impl/Tab.d.ts +7 -2
- package/build/types/desktop/cmp/tab/impl/TabContainer.d.ts +1 -1
- package/build/types/desktop/cmp/tab/impl/TabContextMenuItems.d.ts +4 -0
- package/build/types/desktop/cmp/tab/index.d.ts +1 -0
- package/build/types/dynamics/desktop.d.ts +1 -0
- package/build/types/mobile/cmp/tab/impl/TabContainer.d.ts +1 -1
- package/cmp/tab/TabContainer.ts +19 -4
- package/cmp/tab/TabContainerModel.ts +113 -54
- package/cmp/tab/TabModel.ts +1 -2
- package/cmp/tab/Types.ts +80 -0
- package/cmp/tab/index.ts +1 -1
- package/data/RecordAction.ts +4 -1
- package/desktop/appcontainer/AppContainer.ts +3 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/HeaderFilter.ts +1 -1
- package/desktop/cmp/grid/impl/filter/headerfilter/HeaderFilterModel.ts +0 -1
- package/desktop/cmp/tab/TabSwitcher.ts +18 -3
- package/desktop/cmp/tab/Tabs.scss +1 -0
- package/desktop/cmp/tab/dynamic/DynamicTabSwitcher.scss +53 -0
- package/desktop/cmp/tab/dynamic/DynamicTabSwitcher.ts +237 -0
- package/desktop/cmp/tab/dynamic/DynamicTabSwitcherModel.ts +167 -0
- package/desktop/cmp/tab/dynamic/scroller/Scroller.ts +69 -0
- package/desktop/cmp/tab/dynamic/scroller/ScrollerModel.ts +92 -0
- package/desktop/cmp/tab/impl/Tab.ts +30 -6
- package/desktop/cmp/tab/impl/TabContainer.ts +34 -9
- package/desktop/cmp/tab/impl/TabContextMenuItems.ts +21 -0
- package/desktop/cmp/tab/index.ts +1 -0
- package/dynamics/desktop.ts +2 -0
- package/mobile/cmp/tab/impl/TabContainer.ts +16 -9
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/build/types/cmp/tab/TabSwitcherProps.d.ts +0 -16
- package/cmp/tab/TabSwitcherProps.ts +0 -28
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {
|
|
8
|
+
TabSwitcherConfig,
|
|
9
|
+
IDynamicTabSwitcherModel,
|
|
10
|
+
TabContainerModelPersistOptions
|
|
11
|
+
} from '@xh/hoist/cmp/tab/Types';
|
|
7
12
|
import {
|
|
8
13
|
HoistModel,
|
|
9
14
|
managed,
|
|
10
|
-
Persistable,
|
|
11
15
|
PersistableState,
|
|
12
16
|
PersistenceProvider,
|
|
13
17
|
PersistOptions,
|
|
@@ -16,14 +20,14 @@ import {
|
|
|
16
20
|
RenderMode,
|
|
17
21
|
XH
|
|
18
22
|
} from '@xh/hoist/core';
|
|
23
|
+
import {DynamicTabSwitcherModel} from '@xh/hoist/desktop/cmp/tab/dynamic/DynamicTabSwitcherModel';
|
|
19
24
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
20
25
|
import {wait} from '@xh/hoist/promise';
|
|
21
26
|
import {isOmitted} from '@xh/hoist/utils/impl';
|
|
22
27
|
import {ensureUniqueBy, throwIf} from '@xh/hoist/utils/js';
|
|
23
|
-
import {difference, find, findLast, isString, without} from 'lodash';
|
|
28
|
+
import {difference, find, findLast, isObject, isString, without} from 'lodash';
|
|
24
29
|
import {ReactNode} from 'react';
|
|
25
30
|
import {TabConfig, TabModel} from './TabModel';
|
|
26
|
-
import {TabSwitcherProps} from './TabSwitcherProps';
|
|
27
31
|
|
|
28
32
|
export interface TabContainerConfig {
|
|
29
33
|
/** Tabs to be displayed. */
|
|
@@ -42,11 +46,10 @@ export interface TabContainerConfig {
|
|
|
42
46
|
route?: string;
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* include a switcher. Defaults to true.
|
|
49
|
+
* Specification for type of switcher. Specify `dynamic` or config for user-configurable tabs.
|
|
50
|
+
* Default `{mode: 'static'}` for simple, static switcher.
|
|
48
51
|
*/
|
|
49
|
-
switcher?:
|
|
52
|
+
switcher?: TabSwitcherConfig;
|
|
50
53
|
|
|
51
54
|
/**
|
|
52
55
|
* True to enable activity tracking of tab views (default false). Viewing of each tab will
|
|
@@ -87,7 +90,7 @@ export interface TabContainerConfig {
|
|
|
87
90
|
*
|
|
88
91
|
* Note: Routing is currently enabled for desktop applications only.
|
|
89
92
|
*/
|
|
90
|
-
export class TabContainerModel extends HoistModel
|
|
93
|
+
export class TabContainerModel extends HoistModel {
|
|
91
94
|
declare config: TabContainerConfig;
|
|
92
95
|
|
|
93
96
|
@managed
|
|
@@ -97,42 +100,47 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
97
100
|
@observable
|
|
98
101
|
activeTabId: string;
|
|
99
102
|
|
|
103
|
+
depth: number; // Depth in hierarchy of nested TabContainerModels
|
|
100
104
|
route: string;
|
|
101
105
|
defaultTabId: string;
|
|
102
|
-
switcher: TabSwitcherProps;
|
|
103
106
|
track: boolean;
|
|
104
107
|
renderMode: RenderMode;
|
|
105
108
|
refreshMode: RefreshMode;
|
|
106
109
|
emptyText: ReactNode;
|
|
110
|
+
switcherConfig: TabSwitcherConfig;
|
|
107
111
|
|
|
108
112
|
@managed
|
|
109
113
|
refreshContextModel: RefreshContextModel;
|
|
110
114
|
|
|
115
|
+
@managed
|
|
116
|
+
dynamicTabSwitcherModel: IDynamicTabSwitcherModel;
|
|
117
|
+
|
|
111
118
|
protected lastActiveTabId: string;
|
|
112
119
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
/**
|
|
121
|
+
* @param config - TabContainer configuration.
|
|
122
|
+
* @param [depth] - Depth in hierarchy of nested TabContainerModels. Not for application use.
|
|
123
|
+
*/
|
|
124
|
+
constructor(
|
|
125
|
+
{
|
|
126
|
+
tabs = [],
|
|
127
|
+
defaultTabId = null,
|
|
128
|
+
route = null,
|
|
129
|
+
track = false,
|
|
130
|
+
renderMode = 'lazy',
|
|
131
|
+
refreshMode = 'onShowLazy',
|
|
132
|
+
persistWith,
|
|
133
|
+
emptyText = 'No tabs to display.',
|
|
134
|
+
xhImpl = false,
|
|
135
|
+
switcher = {mode: 'static'}
|
|
136
|
+
}: TabContainerConfig,
|
|
137
|
+
depth = 0
|
|
138
|
+
) {
|
|
125
139
|
super();
|
|
126
140
|
makeObservable(this);
|
|
127
141
|
this.xhImpl = xhImpl;
|
|
128
142
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// Create default switcher props
|
|
132
|
-
if (switcher === true) switcher = {orientation: XH.isMobileApp ? 'bottom' : 'top'};
|
|
133
|
-
if (switcher === false) switcher = null;
|
|
134
|
-
|
|
135
|
-
this.switcher = switcher as TabSwitcherProps;
|
|
143
|
+
this.depth = depth;
|
|
136
144
|
this.renderMode = renderMode;
|
|
137
145
|
this.refreshMode = refreshMode;
|
|
138
146
|
this.defaultTabId = defaultTabId;
|
|
@@ -142,6 +150,8 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
142
150
|
this.setTabs(tabs);
|
|
143
151
|
this.refreshContextModel = new RefreshContextModel();
|
|
144
152
|
this.refreshContextModel.xhImpl = xhImpl;
|
|
153
|
+
this.switcherConfig = switcher;
|
|
154
|
+
this.dynamicTabSwitcherModel = this.parseSwitcher(switcher);
|
|
145
155
|
|
|
146
156
|
if (route) {
|
|
147
157
|
if (XH.isMobileApp) {
|
|
@@ -156,17 +166,10 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
156
166
|
wait().then(() => this.syncWithRouter());
|
|
157
167
|
|
|
158
168
|
this.forwardRouterToTab(this.activeTabId);
|
|
159
|
-
} else if (persistWith) {
|
|
160
|
-
((this.persistWith = {
|
|
161
|
-
path: 'tabContainer',
|
|
162
|
-
...persistWith
|
|
163
|
-
}),
|
|
164
|
-
PersistenceProvider.create({
|
|
165
|
-
persistOptions: this.persistWith,
|
|
166
|
-
target: this
|
|
167
|
-
}));
|
|
168
169
|
}
|
|
169
170
|
|
|
171
|
+
if (persistWith) this.initPersist(persistWith);
|
|
172
|
+
|
|
170
173
|
if (track) {
|
|
171
174
|
this.addReaction({
|
|
172
175
|
track: () => this.activeTab,
|
|
@@ -269,16 +272,18 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
269
272
|
return this.findTab(this.activeTabId);
|
|
270
273
|
}
|
|
271
274
|
|
|
272
|
-
/** The tab immediately before the active tab in the model's tab list. */
|
|
275
|
+
/** The visitable tab immediately before the active tab in the model's tab list. */
|
|
273
276
|
get prevTab(): TabModel {
|
|
274
|
-
const
|
|
275
|
-
|
|
277
|
+
const tabs = this.tabs.filter(t => !t.disabled || t === this.activeTab),
|
|
278
|
+
activeTabIdx = tabs.indexOf(this.activeTab);
|
|
279
|
+
return activeTabIdx > 0 ? tabs[activeTabIdx - 1] : null;
|
|
276
280
|
}
|
|
277
281
|
|
|
278
|
-
/** The tab immediately after the active tab in the model's tab list. */
|
|
282
|
+
/** The visitable tab immediately after the active tab in the model's tab list. */
|
|
279
283
|
get nextTab(): TabModel {
|
|
280
|
-
const
|
|
281
|
-
|
|
284
|
+
const tabs = this.tabs.filter(t => !t.disabled || t === this.activeTab),
|
|
285
|
+
activeTabIdx = tabs.indexOf(this.activeTab);
|
|
286
|
+
return activeTabIdx < tabs.length - 1 ? tabs[activeTabIdx + 1] : null;
|
|
282
287
|
}
|
|
283
288
|
|
|
284
289
|
/**
|
|
@@ -330,17 +335,6 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
330
335
|
if (target) this.activateTab(target);
|
|
331
336
|
}
|
|
332
337
|
|
|
333
|
-
//-------------------------
|
|
334
|
-
// Persistable Interface
|
|
335
|
-
//-------------------------
|
|
336
|
-
getPersistableState(): PersistableState<{activeTabId: string}> {
|
|
337
|
-
return new PersistableState({activeTabId: this.activeTabId});
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
setPersistableState(state: PersistableState<{activeTabId: string}>): void {
|
|
341
|
-
this.activateTab(state.value.activeTabId);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
338
|
//-------------------------
|
|
345
339
|
// Implementation
|
|
346
340
|
//-------------------------
|
|
@@ -395,6 +389,71 @@ export class TabContainerModel extends HoistModel implements Persistable<{active
|
|
|
395
389
|
|
|
396
390
|
return null;
|
|
397
391
|
}
|
|
392
|
+
|
|
393
|
+
private parseSwitcher(switcher: TabContainerConfig['switcher']): IDynamicTabSwitcherModel {
|
|
394
|
+
if (!switcher || switcher.mode === 'static') return null;
|
|
395
|
+
throwIf(XH.isMobileApp, 'DynamicTabSwitcherModel not supported for mobile TabContainer.');
|
|
396
|
+
|
|
397
|
+
return this.markManaged(new DynamicTabSwitcherModel(switcher, this));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private initPersist({
|
|
401
|
+
persistActiveTabId = !this.route,
|
|
402
|
+
persistFavoriteTabIds = !!this.dynamicTabSwitcherModel,
|
|
403
|
+
path = 'tabContainer',
|
|
404
|
+
...rootPersistWith
|
|
405
|
+
}: TabContainerModelPersistOptions) {
|
|
406
|
+
if (persistActiveTabId) {
|
|
407
|
+
if (this.route) {
|
|
408
|
+
this.logWarn('persistActiveTabId and route cannot both be specified.');
|
|
409
|
+
} else {
|
|
410
|
+
const persistWith = isObject(persistActiveTabId)
|
|
411
|
+
? PersistenceProvider.mergePersistOptions(rootPersistWith, persistActiveTabId)
|
|
412
|
+
: rootPersistWith;
|
|
413
|
+
PersistenceProvider.create({
|
|
414
|
+
persistOptions: {
|
|
415
|
+
path: `${path}.activeTabId`,
|
|
416
|
+
...persistWith
|
|
417
|
+
},
|
|
418
|
+
target: {
|
|
419
|
+
getPersistableState: () => new PersistableState(this.activeTabId),
|
|
420
|
+
setPersistableState: ({value}) => this.activateTab(value)
|
|
421
|
+
},
|
|
422
|
+
owner: this
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (persistFavoriteTabIds) {
|
|
428
|
+
const {dynamicTabSwitcherModel} = this;
|
|
429
|
+
if (!dynamicTabSwitcherModel) {
|
|
430
|
+
this.logWarn(
|
|
431
|
+
'persistFavoriteTabIds is set but no DynamicTabSwitcherModel is present.'
|
|
432
|
+
);
|
|
433
|
+
} else {
|
|
434
|
+
const persistWith = isObject(persistFavoriteTabIds)
|
|
435
|
+
? PersistenceProvider.mergePersistOptions(
|
|
436
|
+
rootPersistWith,
|
|
437
|
+
persistFavoriteTabIds
|
|
438
|
+
)
|
|
439
|
+
: rootPersistWith;
|
|
440
|
+
PersistenceProvider.create({
|
|
441
|
+
persistOptions: {
|
|
442
|
+
path: `${path}.favoriteTabIds`,
|
|
443
|
+
...persistWith
|
|
444
|
+
},
|
|
445
|
+
target: {
|
|
446
|
+
getPersistableState: () =>
|
|
447
|
+
new PersistableState(dynamicTabSwitcherModel.favoriteTabIds),
|
|
448
|
+
setPersistableState: ({value}) => {
|
|
449
|
+
dynamicTabSwitcherModel.setFavoriteTabIds(value);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
owner: this
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
398
457
|
}
|
|
399
458
|
|
|
400
459
|
export interface AddTabOptions {
|
package/cmp/tab/TabModel.ts
CHANGED
|
@@ -196,7 +196,6 @@ export class TabModel extends HoistModel {
|
|
|
196
196
|
renderMode: parent.renderMode,
|
|
197
197
|
refreshMode: parent.refreshMode,
|
|
198
198
|
emptyText: parent.emptyText,
|
|
199
|
-
switcher: parent.switcher,
|
|
200
199
|
track: parent.track,
|
|
201
200
|
...childConfig
|
|
202
201
|
};
|
|
@@ -211,7 +210,7 @@ export class TabModel extends HoistModel {
|
|
|
211
210
|
};
|
|
212
211
|
}
|
|
213
212
|
|
|
214
|
-
this.childContainerModel = new TabContainerModel(childConfig);
|
|
213
|
+
this.childContainerModel = new TabContainerModel(childConfig, parent.depth + 1);
|
|
215
214
|
return tabContainer({model: this.childContainerModel});
|
|
216
215
|
}
|
|
217
216
|
}
|
package/cmp/tab/Types.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {TabContainerModel} from '@xh/hoist/cmp/tab/TabContainerModel';
|
|
2
|
+
import {TabModel} from '@xh/hoist/cmp/tab/TabModel';
|
|
3
|
+
import {
|
|
4
|
+
BoxProps,
|
|
5
|
+
HoistModel,
|
|
6
|
+
HoistProps,
|
|
7
|
+
MenuContext,
|
|
8
|
+
MenuItemLike,
|
|
9
|
+
MenuToken,
|
|
10
|
+
PersistOptions,
|
|
11
|
+
Side
|
|
12
|
+
} from '@xh/hoist/core';
|
|
13
|
+
|
|
14
|
+
export interface TabSwitcherProps extends HoistProps<TabContainerModel>, BoxProps {
|
|
15
|
+
/** Relative position within the parent TabContainer. Defaults to 'top'. */
|
|
16
|
+
orientation?: Side;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* True to animate the indicator when switching tabs. False (default) to change instantly.
|
|
20
|
+
* Not supported by DynamicTabSwitcher.
|
|
21
|
+
*/
|
|
22
|
+
animate?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Enable scrolling and place tabs that overflow into a menu. Default to false.
|
|
26
|
+
* Not supported by DynamicTabSwitcher.
|
|
27
|
+
*/
|
|
28
|
+
enableOverflow?: boolean;
|
|
29
|
+
|
|
30
|
+
/** Width (in px) to render tabs. Only applies to horizontal orientations */
|
|
31
|
+
tabWidth?: number;
|
|
32
|
+
|
|
33
|
+
/** Minimum width (in px) to render tabs. Only applies to horizontal orientations */
|
|
34
|
+
tabMinWidth?: number;
|
|
35
|
+
|
|
36
|
+
/** Maximum width (in px) to render tabs. Only applies to horizontal orientations */
|
|
37
|
+
tabMaxWidth?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface TabSwitcherConfig {
|
|
41
|
+
/** Specification for type of switcher. Specify `dynamic`for user-configurable tabs */
|
|
42
|
+
mode: 'static' | 'dynamic';
|
|
43
|
+
/** Additional menu items to include in tab context menus. Only supported on desktop */
|
|
44
|
+
extraMenuItems?: Array<MenuItemLike<MenuToken, TabSwitcherMenuContext>>;
|
|
45
|
+
/** IDs of favorite tabs to display by default (in order). Only for `dynamic` switchers */
|
|
46
|
+
initialFavorites?: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TabContainerModelPersistOptions extends PersistOptions {
|
|
50
|
+
/** True (default) to persist the active tab ID or provide custom PersistOptions. */
|
|
51
|
+
persistActiveTabId?: boolean;
|
|
52
|
+
/** True (default) to persist favorite tab IDs or provide custom PersistOptions. */
|
|
53
|
+
persistFavoriteTabIds?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface TabSwitcherMenuContext extends MenuContext {
|
|
57
|
+
tab: TabModel;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Cross-platform interface for desktop and mobile (TBA) DynamicTabSwitcherModels. */
|
|
61
|
+
export interface IDynamicTabSwitcherModel extends HoistModel {
|
|
62
|
+
/** IDs of favorite tabs, in order. */
|
|
63
|
+
get favoriteTabIds(): string[];
|
|
64
|
+
/** Tabs displayed in switcher, in order. */
|
|
65
|
+
get visibleTabs(): TabModel[];
|
|
66
|
+
/** Enabled tabs displayed in switcher, in order. */
|
|
67
|
+
get enabledVisibleTabs(): TabModel[];
|
|
68
|
+
/** Whether the specified tab is currently active in the TabContainer. */
|
|
69
|
+
isTabActive(tabId: string): boolean;
|
|
70
|
+
/** Whether the specified tab is currently marked as a favorite. */
|
|
71
|
+
isTabFavorite(tabId: string): boolean;
|
|
72
|
+
/** Toggle the favorite status of the specified tab. */
|
|
73
|
+
toggleTabFavorite(tabId: string): void;
|
|
74
|
+
/** Activate the specified tab in the TabContainer. */
|
|
75
|
+
activate(tabId: string): void;
|
|
76
|
+
/** Remove the specified tab from the switcher. */
|
|
77
|
+
hide(tabId: string): void;
|
|
78
|
+
/** Set the IDs of all favorite tabs, in order. */
|
|
79
|
+
setFavoriteTabIds(tabIds: string[]): void;
|
|
80
|
+
}
|
package/cmp/tab/index.ts
CHANGED
package/data/RecordAction.ts
CHANGED
|
@@ -33,7 +33,10 @@ export interface RecordActionSpec extends TestSupportProps {
|
|
|
33
33
|
/** Function called on action execution. */
|
|
34
34
|
actionFn?: (data: ActionFnData) => void;
|
|
35
35
|
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Function called to append / override display properties prior to each render. This function
|
|
38
|
+
* allows dynamic control over display properties.
|
|
39
|
+
* */
|
|
37
40
|
displayFn?: (data: ActionFnData) => RecordActionSpec;
|
|
38
41
|
|
|
39
42
|
/** Sub-actions for this action. */
|
|
@@ -25,7 +25,7 @@ import {storeFilterFieldImpl} from '@xh/hoist/desktop/cmp/store/impl/StoreFilter
|
|
|
25
25
|
import {tabContainerImpl} from '@xh/hoist/desktop/cmp/tab/impl/TabContainer';
|
|
26
26
|
import {zoneMapperDialog as zoneMapper} from '@xh/hoist/desktop/cmp/zoneGrid/impl/ZoneMapperDialog';
|
|
27
27
|
import {useContextMenu, useHotkeys} from '@xh/hoist/desktop/hooks';
|
|
28
|
-
import {installDesktopImpls} from '@xh/hoist/dynamics/desktop';
|
|
28
|
+
import {DynamicTabSwitcherModel, installDesktopImpls} from '@xh/hoist/dynamics/desktop';
|
|
29
29
|
import {inspectorPanel} from '@xh/hoist/inspector/InspectorPanel';
|
|
30
30
|
import {blueprintProvider} from '@xh/hoist/kit/blueprint';
|
|
31
31
|
import {consumeEvent} from '@xh/hoist/utils/js';
|
|
@@ -59,7 +59,8 @@ installDesktopImpls({
|
|
|
59
59
|
useContextMenu,
|
|
60
60
|
ModalSupportModel,
|
|
61
61
|
errorMessageImpl,
|
|
62
|
-
maskImpl
|
|
62
|
+
maskImpl,
|
|
63
|
+
DynamicTabSwitcherModel
|
|
63
64
|
});
|
|
64
65
|
/**
|
|
65
66
|
* Top-level wrapper for Desktop applications.
|
|
@@ -116,7 +116,6 @@ export class HeaderFilterModel extends HoistModel {
|
|
|
116
116
|
this.valuesTabModel = enableValues ? new ValuesTabModel(this) : null;
|
|
117
117
|
this.customTabModel = new CustomTabModel(this);
|
|
118
118
|
this.tabContainerModel = new TabContainerModel({
|
|
119
|
-
switcher: false,
|
|
120
119
|
tabs: [
|
|
121
120
|
{
|
|
122
121
|
id: 'valuesFilter',
|
|
@@ -6,15 +6,19 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import composeRefs from '@seznam/compose-react-refs';
|
|
8
8
|
import {box, div, hframe, span} from '@xh/hoist/cmp/layout';
|
|
9
|
-
import {TabContainerModel
|
|
9
|
+
import {TabContainerModel} from '@xh/hoist/cmp/tab';
|
|
10
|
+
import {TabSwitcherProps} from '@xh/hoist/cmp/tab/Types';
|
|
10
11
|
import {hoistCmp, HoistModel, useLocalModel, uses} from '@xh/hoist/core';
|
|
11
12
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
12
13
|
import '@xh/hoist/desktop/register';
|
|
14
|
+
import {contextMenu} from '@xh/hoist/desktop/cmp/contextmenu';
|
|
15
|
+
import {getContextMenuItem} from '@xh/hoist/desktop/cmp/tab/impl/TabContextMenuItems';
|
|
13
16
|
import {Icon} from '@xh/hoist/icon';
|
|
14
17
|
import {
|
|
15
18
|
menu,
|
|
16
19
|
menuItem,
|
|
17
20
|
popover,
|
|
21
|
+
showContextMenu,
|
|
18
22
|
tab as bpTab,
|
|
19
23
|
tabs as bpTabs,
|
|
20
24
|
tooltip as bpTooltip
|
|
@@ -67,7 +71,7 @@ export const [TabSwitcher, tabSwitcher] = hoistCmp.withFactory<TabSwitcherProps>
|
|
|
67
71
|
'Unsupported value for orientation.'
|
|
68
72
|
);
|
|
69
73
|
|
|
70
|
-
const {tabs, activeTabId} = model,
|
|
74
|
+
const {tabs, activeTabId, switcherConfig} = model,
|
|
71
75
|
layoutProps = getLayoutProps(props),
|
|
72
76
|
vertical = ['left', 'right'].includes(orientation),
|
|
73
77
|
impl = useLocalModel(() => new TabSwitcherLocalModel(model, enableOverflow, vertical));
|
|
@@ -119,7 +123,18 @@ export const [TabSwitcher, tabSwitcher] = hoistCmp.withFactory<TabSwitcherProps>
|
|
|
119
123
|
})
|
|
120
124
|
]
|
|
121
125
|
})
|
|
122
|
-
})
|
|
126
|
+
}),
|
|
127
|
+
onContextMenu: e => {
|
|
128
|
+
const domRect = e.currentTarget.getBoundingClientRect(),
|
|
129
|
+
menuItems = (switcherConfig.extraMenuItems ?? []).map(it =>
|
|
130
|
+
getContextMenuItem(it, {contextMenuEvent: e, tab})
|
|
131
|
+
);
|
|
132
|
+
if (isEmpty(menuItems)) return;
|
|
133
|
+
showContextMenu(contextMenu({menuItems}), {
|
|
134
|
+
left: orientation === 'left' ? domRect.right : domRect.left,
|
|
135
|
+
top: orientation === 'top' ? domRect.bottom : domRect.top
|
|
136
|
+
});
|
|
137
|
+
}
|
|
123
138
|
});
|
|
124
139
|
});
|
|
125
140
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.xh-dynamic-tab-switcher {
|
|
2
|
+
&:not(&--vertical) &__tabs {
|
|
3
|
+
overflow-x: auto;
|
|
4
|
+
|
|
5
|
+
.bp5-tab-list {
|
|
6
|
+
column-gap: var(--xh-pad-double-px);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
&--vertical &__tabs {
|
|
11
|
+
overflow-y: auto;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&__tabs {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: row;
|
|
17
|
+
|
|
18
|
+
&::-webkit-scrollbar {
|
|
19
|
+
display: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&__tab {
|
|
23
|
+
align-items: center;
|
|
24
|
+
user-select: none;
|
|
25
|
+
|
|
26
|
+
.xh-dynamic-tab-switcher--vertical & {
|
|
27
|
+
min-height: fit-content;
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&:not(&--active) {
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&--dragging {
|
|
36
|
+
background-color: var(--xh-menu-item-highlight-bg);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&__icon {
|
|
40
|
+
margin-right: 3px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&__close-button {
|
|
44
|
+
align-self: center;
|
|
45
|
+
padding: 0 !important;
|
|
46
|
+
margin-left: 3px;
|
|
47
|
+
min-height: 15px;
|
|
48
|
+
min-width: 15px;
|
|
49
|
+
border-radius: 100% !important;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|