Skip to content

Commit 5764694

Browse files
committed
feat(other): advanced routing panel in other tab
1 parent bd39629 commit 5764694

File tree

3 files changed

+180
-2
lines changed

3 files changed

+180
-2
lines changed

client-app/src/desktop/AppModel.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,12 @@ export class AppModel extends HoistAppModel {
193193
{name: 'pinPad', path: '/pinPad'},
194194
{name: 'placeholder', path: '/placeholder'},
195195
{name: 'popups', path: '/popups'},
196-
{name: 'timestamp', path: '/timestamp'}
196+
{name: 'timestamp', path: '/timestamp'},
197+
{
198+
name: 'advancedRouting',
199+
path: '/advancedRouting',
200+
children: [{name: 'routeParam', path: '/:routeParam'}]
201+
}
197202
]
198203
},
199204
{

client-app/src/desktop/tabs/other/OtherTab.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {pinPadPanel} from './PinPadPanel';
1717
import {placeholderPanel} from './PlaceholderPanel';
1818
import {popupsPanel} from './PopupsPanel';
1919
import {relativeTimestampPanel} from './relativetimestamp/RelativeTimestampPanel';
20+
import {advancedRoutingPanel} from './routing/AdvancedRoutingPanel';
2021

2122
export const otherTab = hoistCmp.factory(() =>
2223
tabContainer({
@@ -44,7 +45,8 @@ export const otherTab = hoistCmp.factory(() =>
4445
{id: 'pinPad', title: 'PIN Pad', content: pinPadPanel},
4546
{id: 'placeholder', title: 'Placeholder', content: placeholderPanel},
4647
{id: 'popups', content: popupsPanel},
47-
{id: 'timestamp', content: relativeTimestampPanel}
48+
{id: 'timestamp', content: relativeTimestampPanel},
49+
{id: 'advancedRouting', content: advancedRoutingPanel}
4850
]
4951
},
5052
className: 'toolbox-tab'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import {HoistModel, hoistCmp, creates, XH} from '@xh/hoist/core';
2+
import {grid, gridCountLabel, GridModel} from '@xh/hoist/cmp/grid';
3+
import {action, observable, makeObservable} from '@xh/hoist/mobx';
4+
import {panel} from '@xh/hoist/desktop/cmp/panel';
5+
import {wrapper} from '../../../common';
6+
import React from 'react';
7+
import {Icon} from '@xh/hoist/icon';
8+
import {filler, hbox, hframe, span, vbox, vframe} from '@xh/hoist/cmp/layout';
9+
import {colAutosizeButton, refreshButton} from '@xh/hoist/desktop/cmp/button';
10+
import {select} from '@xh/hoist/desktop/cmp/input';
11+
12+
export const advancedRoutingPanel = hoistCmp.factory({
13+
displayName: 'AdvancedRoutingPanel',
14+
model: creates(() => new AdvancedRoutingPanelModel()),
15+
16+
render({model}) {
17+
return wrapper({
18+
description: [
19+
<p>
20+
This example demonstrates how to use URL route parameters to store and restore
21+
the state of a component. The state of the grid (grouping, sorting, and selected
22+
record) is stored in the URL, and the state is restored when the URL is
23+
revisited.
24+
</p>,
25+
<p>
26+
The state is encoded in the URL as a <code>base64</code> string, which is then
27+
decoded and parsed to restore the state.
28+
</p>,
29+
<p>
30+
The current state encoding is: <br />
31+
<code>{'{'}</code>
32+
<br />
33+
<code>groupBy: {model.groupBy || 'None'}</code>
34+
<br />
35+
<code>sortBy: {model.sortBy || 'None'}</code>
36+
<br />
37+
<code>selectedId: {model.gridModel.selectedRecord?.id || 'None'}</code>
38+
<br />
39+
<code>{'}'}</code>
40+
</p>,
41+
<p></p>
42+
],
43+
item: panel({
44+
ref: model.panelRef,
45+
mask: 'onLoad',
46+
item: hframe(
47+
vframe(
48+
grid(),
49+
hbox({
50+
items: [Icon.info()],
51+
className: 'tb-sample-grid__selbar'
52+
})
53+
)
54+
),
55+
tbar: [
56+
refreshButton(),
57+
colAutosizeButton(),
58+
span('Group by:'),
59+
select({
60+
bind: 'groupBy',
61+
options: [
62+
{value: 'city', label: 'City'},
63+
{value: 'trade_date', label: 'Trade Date'},
64+
{value: 'city,trade_date', label: 'City › Trade Date'},
65+
{value: null, label: 'None'}
66+
],
67+
width: 160
68+
}),
69+
span('Sort by:'),
70+
select({
71+
bind: 'sortBy',
72+
options: [
73+
{value: 'id|desc', label: 'Company ID (Desc)'},
74+
{value: 'id|asc', label: 'Company ID (Asc)'},
75+
{value: 'company|desc', label: 'Company Name (Desc)'},
76+
{value: 'company|asc', label: 'Company Name (Asc)'},
77+
{value: 'city|desc', label: 'City (Desc)'},
78+
{value: 'city|asc', label: 'City (Asc)'},
79+
{value: 'trade_date|desc', label: 'Trade Date (Desc)'},
80+
{value: 'trade_date|asc', label: 'Trade Date (Asc)'},
81+
{value: null, label: 'None'}
82+
]
83+
}),
84+
filler(),
85+
gridCountLabel({unit: 'companies'}),
86+
vbox({})
87+
]
88+
})
89+
});
90+
}
91+
});
92+
93+
class AdvancedRoutingPanelModel extends HoistModel {
94+
@observable groupBy = null;
95+
@observable sortBy = null;
96+
97+
constructor() {
98+
super();
99+
makeObservable(this);
100+
101+
this.addReaction({
102+
track: () => XH.routerState.params,
103+
run: () => this.parseRouteParams(),
104+
fireImmediately: true
105+
});
106+
107+
this.addReaction({
108+
track: () => [this.groupBy, this.sortBy, this.gridModel.selectedRecord?.id],
109+
run: () => this.updateRoute(),
110+
fireImmediately: true
111+
});
112+
}
113+
114+
gridModel = new GridModel({
115+
columns: [
116+
{field: 'id', flex: 0},
117+
{field: 'company', flex: 1},
118+
{field: 'city', flex: 1},
119+
{field: 'trade_date', flex: 1}
120+
]
121+
});
122+
123+
@action
124+
private setGroupBy(groupBy: string) {
125+
this.groupBy = groupBy;
126+
127+
// Always select first when regrouping.
128+
const groupByArr = groupBy ? groupBy.split(',') : [];
129+
this.gridModel.setGroupBy(groupByArr);
130+
}
131+
132+
@action
133+
private setSortBy(sortBy: string) {
134+
this.sortBy = sortBy;
135+
136+
// Always select first when resorting.
137+
const sortByArr = sortBy ? sortBy.split(',') : [];
138+
this.gridModel.setSortBy(sortByArr);
139+
}
140+
141+
@action
142+
private async setSelected(recordId: string | number) {
143+
await this.gridModel.selectAsync(Number(recordId));
144+
}
145+
146+
@action
147+
private async parseRouteParams() {
148+
const advParam = XH.routerState.params.routeParam;
149+
if (!advParam) return;
150+
const decodedParam = atob(advParam);
151+
const {groupBy, sortBy, selectedId} = JSON.parse(decodedParam);
152+
if (groupBy) this.setGroupBy(groupBy);
153+
if (sortBy) this.setSortBy(sortBy);
154+
if (selectedId) await this.setSelected(selectedId);
155+
}
156+
157+
@action
158+
private updateRoute() {
159+
if (!XH.routerState.name.startsWith('default.other.advancedRouting')) return;
160+
const {groupBy, sortBy} = this;
161+
const selectedId = this.gridModel.selectedRecord?.id;
162+
const routeParam = btoa(JSON.stringify({groupBy, sortBy, selectedId}));
163+
XH.navigate('default.other.advancedRouting.routeParam', {routeParam});
164+
}
165+
166+
override async doLoadAsync(loadSpec) {
167+
const {trades} = await XH.fetchJson({url: 'trade'});
168+
this.gridModel.loadData(trades);
169+
await this.parseRouteParams();
170+
}
171+
}

0 commit comments

Comments
 (0)