@xh/hoist 73.0.0-SNAPSHOT.1738003454248 → 73.0.0-SNAPSHOT.1738073624805
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/admin/jsonsearch/JsonSearchPanel.ts +243 -0
- package/admin/jsonsearch/impl/JsonSearchPanelImplModel.ts +159 -0
- package/admin/tabs/userData/jsonblob/JsonBlobPanel.ts +48 -10
- package/admin/tabs/userData/prefs/UserPreferencePanel.ts +42 -15
- package/build/types/admin/jsonsearch/JsonSearchPanel.d.ts +18 -0
- package/build/types/admin/jsonsearch/impl/JsonSearchPanelImplModel.d.ts +33 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,243 @@
|
|
|
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 {startCase} from 'lodash';
|
|
9
|
+
import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
|
|
10
|
+
import {errorMessage} from '@xh/hoist/cmp/error';
|
|
11
|
+
import {grid, GridConfig, gridCountLabel} from '@xh/hoist/cmp/grid';
|
|
12
|
+
import {a, box, filler, h4, hframe, label, li, span, ul, vbox} from '@xh/hoist/cmp/layout';
|
|
13
|
+
import {hoistCmp, SelectOption, useLocalModel} from '@xh/hoist/core';
|
|
14
|
+
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
15
|
+
import {buttonGroupInput, jsonInput, select, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
16
|
+
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
17
|
+
import {Icon} from '@xh/hoist/icon';
|
|
18
|
+
import {popover} from '@xh/hoist/kit/blueprint';
|
|
19
|
+
import {clipboardButton} from '@xh/hoist/desktop/cmp/clipboard';
|
|
20
|
+
import {JsonSearchPanelImplModel} from './impl/JsonSearchPanelImplModel';
|
|
21
|
+
|
|
22
|
+
export interface JsonSearchPanelProps {
|
|
23
|
+
/** Url to endpoint for searching for matching JSON documents */
|
|
24
|
+
docSearchUrl: string;
|
|
25
|
+
|
|
26
|
+
/** Url to endpoint for listing matching JSON nodes */
|
|
27
|
+
matchingNodesUrl: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Config for GridModel used to display search results.
|
|
31
|
+
*/
|
|
32
|
+
gridModelConfig: GridConfig;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Names of field(s) that can be used to group by.
|
|
36
|
+
*/
|
|
37
|
+
groupByOptions: SelectOption[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const [JsonSearchPanel, jsonSearchPanel] = hoistCmp.withFactory({
|
|
41
|
+
displayName: 'JsonSearchPanel',
|
|
42
|
+
|
|
43
|
+
render() {
|
|
44
|
+
const impl = useLocalModel(JsonSearchPanelImplModel),
|
|
45
|
+
{error} = impl;
|
|
46
|
+
|
|
47
|
+
return panel({
|
|
48
|
+
title: 'JSON Path Search',
|
|
49
|
+
icon: Icon.json(),
|
|
50
|
+
modelConfig: {
|
|
51
|
+
side: 'right',
|
|
52
|
+
defaultSize: '75%',
|
|
53
|
+
collapsible: true,
|
|
54
|
+
defaultCollapsed: true
|
|
55
|
+
},
|
|
56
|
+
compactHeader: true,
|
|
57
|
+
tbar: searchTbar({model: impl}),
|
|
58
|
+
flex: 1,
|
|
59
|
+
item: panel({
|
|
60
|
+
mask: impl.docLoadTask,
|
|
61
|
+
items: [
|
|
62
|
+
errorMessage({
|
|
63
|
+
error,
|
|
64
|
+
title: error?.name ? startCase(error.name) : undefined
|
|
65
|
+
}),
|
|
66
|
+
hframe({
|
|
67
|
+
omit: impl.error,
|
|
68
|
+
items: [
|
|
69
|
+
panel({
|
|
70
|
+
item: grid({model: impl.gridModel})
|
|
71
|
+
}),
|
|
72
|
+
panel({
|
|
73
|
+
mask: impl.nodeLoadTask,
|
|
74
|
+
tbar: readerTbar({model: impl}),
|
|
75
|
+
bbar: nodeBbar({
|
|
76
|
+
omit: !impl.asPathList,
|
|
77
|
+
model: impl
|
|
78
|
+
}),
|
|
79
|
+
item: jsonInput({
|
|
80
|
+
model: impl,
|
|
81
|
+
bind: 'readerContent',
|
|
82
|
+
flex: 1,
|
|
83
|
+
width: '100%',
|
|
84
|
+
readonly: true,
|
|
85
|
+
showCopyButton: true
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const searchTbar = hoistCmp.factory<JsonSearchPanelImplModel>(({model}) => {
|
|
97
|
+
return toolbar(
|
|
98
|
+
pathField({model}),
|
|
99
|
+
helpButton(),
|
|
100
|
+
toolbarSep(),
|
|
101
|
+
span('Group by:'),
|
|
102
|
+
select({
|
|
103
|
+
bind: 'groupBy',
|
|
104
|
+
options: model.groupByOptions,
|
|
105
|
+
width: 160,
|
|
106
|
+
enableFilter: false
|
|
107
|
+
}),
|
|
108
|
+
toolbarSep(),
|
|
109
|
+
gridCountLabel({
|
|
110
|
+
gridModel: model.gridModel,
|
|
111
|
+
unit: 'document'
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const pathField = hoistCmp.factory<JsonSearchPanelImplModel>({
|
|
117
|
+
render({model}) {
|
|
118
|
+
return textInput({
|
|
119
|
+
bind: 'path',
|
|
120
|
+
autoFocus: true,
|
|
121
|
+
commitOnChange: true,
|
|
122
|
+
leftIcon: Icon.search(),
|
|
123
|
+
enableClear: true,
|
|
124
|
+
placeholder:
|
|
125
|
+
"JSON Path - e.g. $..[?(@.colId == 'trader')] - type a path and hit ENTER to search",
|
|
126
|
+
width: null,
|
|
127
|
+
flex: 1,
|
|
128
|
+
onKeyDown: e => {
|
|
129
|
+
if (e.key === 'Enter') model.loadJsonDocsAsync();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const helpButton = hoistCmp.factory({
|
|
136
|
+
model: false,
|
|
137
|
+
render() {
|
|
138
|
+
return popover({
|
|
139
|
+
item: button({
|
|
140
|
+
icon: Icon.questionCircle(),
|
|
141
|
+
outlined: true
|
|
142
|
+
}),
|
|
143
|
+
content: vbox({
|
|
144
|
+
style: {
|
|
145
|
+
padding: '0px 20px 10px 20px'
|
|
146
|
+
},
|
|
147
|
+
items: [
|
|
148
|
+
h4('Sample Queries'),
|
|
149
|
+
ul({
|
|
150
|
+
style: {listStyleType: 'none'},
|
|
151
|
+
items: [
|
|
152
|
+
{
|
|
153
|
+
query: "$..[?(@.colId == 'trader')]",
|
|
154
|
+
explanation:
|
|
155
|
+
'Find all nodes with a property "colId" equal to "trader"'
|
|
156
|
+
}
|
|
157
|
+
].map(({query, explanation}) =>
|
|
158
|
+
li({
|
|
159
|
+
key: query,
|
|
160
|
+
items: [
|
|
161
|
+
span({
|
|
162
|
+
className:
|
|
163
|
+
'xh-border xh-pad-half xh-bg-alt xh-font-family-mono',
|
|
164
|
+
item: query
|
|
165
|
+
}),
|
|
166
|
+
' ',
|
|
167
|
+
clipboardButton({
|
|
168
|
+
text: null,
|
|
169
|
+
icon: Icon.copy(),
|
|
170
|
+
getCopyText: () => query,
|
|
171
|
+
successMessage: 'Query copied to clipboard.'
|
|
172
|
+
}),
|
|
173
|
+
' ',
|
|
174
|
+
explanation
|
|
175
|
+
]
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
}),
|
|
179
|
+
a({
|
|
180
|
+
href: 'https://github.com/json-path/JsonPath?tab=readme-ov-file#operators',
|
|
181
|
+
target: '_blank',
|
|
182
|
+
item: 'Path Syntax Docs & More Examples'
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
})
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const readerTbar = hoistCmp.factory<JsonSearchPanelImplModel>(({model}) => {
|
|
191
|
+
return toolbar({
|
|
192
|
+
items: [
|
|
193
|
+
buttonGroupInput({
|
|
194
|
+
model,
|
|
195
|
+
bind: 'readerContentType',
|
|
196
|
+
minimal: true,
|
|
197
|
+
outlined: true,
|
|
198
|
+
disabled: !model.selectedRecord,
|
|
199
|
+
items: [
|
|
200
|
+
button({
|
|
201
|
+
text: 'Document',
|
|
202
|
+
value: 'document'
|
|
203
|
+
}),
|
|
204
|
+
button({
|
|
205
|
+
text: 'Matching Paths',
|
|
206
|
+
value: 'paths'
|
|
207
|
+
}),
|
|
208
|
+
button({
|
|
209
|
+
text: 'Matching Values',
|
|
210
|
+
value: 'values'
|
|
211
|
+
})
|
|
212
|
+
]
|
|
213
|
+
}),
|
|
214
|
+
filler(),
|
|
215
|
+
box({
|
|
216
|
+
omit: !model.matchingNodeCount,
|
|
217
|
+
item: `${model.matchingNodeCount} ${model.matchingNodeCount === 1 ? 'match' : 'matches'}`
|
|
218
|
+
})
|
|
219
|
+
]
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const nodeBbar = hoistCmp.factory<JsonSearchPanelImplModel>(({model}) => {
|
|
224
|
+
return toolbar(
|
|
225
|
+
label('Path Format:'),
|
|
226
|
+
buttonGroupInput({
|
|
227
|
+
model,
|
|
228
|
+
bind: 'pathFormat',
|
|
229
|
+
minimal: true,
|
|
230
|
+
outlined: true,
|
|
231
|
+
items: [
|
|
232
|
+
button({
|
|
233
|
+
text: 'XPath',
|
|
234
|
+
value: 'XPath'
|
|
235
|
+
}),
|
|
236
|
+
button({
|
|
237
|
+
text: 'JSONPath',
|
|
238
|
+
value: 'JSONPath'
|
|
239
|
+
})
|
|
240
|
+
]
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
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 {GridModel} from '@xh/hoist/cmp/grid';
|
|
9
|
+
import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
|
|
10
|
+
import {HoistModel, managed, TaskObserver, XH} from '@xh/hoist/core';
|
|
11
|
+
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
12
|
+
import {isEmpty} from 'lodash';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export class JsonSearchPanelImplModel extends HoistModel {
|
|
18
|
+
override xhImpl = true;
|
|
19
|
+
|
|
20
|
+
@managed gridModel: GridModel;
|
|
21
|
+
@managed groupingChooserModel: GroupingChooserModel;
|
|
22
|
+
@managed docLoadTask: TaskObserver = TaskObserver.trackLast();
|
|
23
|
+
@managed nodeLoadTask: TaskObserver = TaskObserver.trackLast();
|
|
24
|
+
|
|
25
|
+
@observable groupBy: string = null;
|
|
26
|
+
|
|
27
|
+
@bindable.ref error = null;
|
|
28
|
+
@bindable path: string = '';
|
|
29
|
+
@bindable readerContentType: 'document' | 'paths' | 'values' = 'values';
|
|
30
|
+
@bindable pathFormat: 'XPath' | 'JSONPath' = 'XPath';
|
|
31
|
+
@bindable readerContent: string = '';
|
|
32
|
+
@bindable matchingNodeCount: number = 0;
|
|
33
|
+
|
|
34
|
+
get asPathList(): boolean {
|
|
35
|
+
return this.readerContentType === 'paths';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get queryBuffer(): number {
|
|
39
|
+
return this.componentProps.queryBuffer ?? 200;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get docSearchUrl(): string {
|
|
43
|
+
return this.componentProps.docSearchUrl;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get matchingNodesUrl(): string {
|
|
47
|
+
return this.componentProps.matchingNodesUrl;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get selectedRecord() {
|
|
51
|
+
return this.gridModel.selectedRecord;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get gridModelConfig() {
|
|
55
|
+
return this.componentProps.gridModelConfig;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get groupByOptions() {
|
|
59
|
+
return [...this.componentProps.groupByOptions, {value: null, label: 'None'}];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
super();
|
|
64
|
+
makeObservable(this);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override onLinked() {
|
|
68
|
+
this.gridModel = new GridModel({
|
|
69
|
+
...this.gridModelConfig,
|
|
70
|
+
selModel: 'single'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
this.addReaction(
|
|
74
|
+
{
|
|
75
|
+
track: () => this.path,
|
|
76
|
+
run: path => {
|
|
77
|
+
if (isEmpty(path)) {
|
|
78
|
+
this.error = null;
|
|
79
|
+
this.gridModel.clear();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
track: () => [this.selectedRecord, this.readerContentType, this.pathFormat],
|
|
85
|
+
run: () => this.loadreaderContentTypeAsync(),
|
|
86
|
+
debounce: 300
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async loadJsonDocsAsync() {
|
|
92
|
+
if (isEmpty(this.path)) {
|
|
93
|
+
this.error = null;
|
|
94
|
+
this.gridModel.clear();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const data = await XH.fetchJson({
|
|
100
|
+
url: this.docSearchUrl,
|
|
101
|
+
params: {path: this.path}
|
|
102
|
+
}).linkTo(this.docLoadTask);
|
|
103
|
+
|
|
104
|
+
this.error = null;
|
|
105
|
+
this.gridModel.loadData(data);
|
|
106
|
+
this.gridModel.selectFirstAsync();
|
|
107
|
+
} catch (e) {
|
|
108
|
+
this.gridModel.clear();
|
|
109
|
+
this.error = e;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async loadreaderContentTypeAsync() {
|
|
114
|
+
if (!this.selectedRecord) {
|
|
115
|
+
this.matchingNodeCount = 0;
|
|
116
|
+
this.readerContent = '';
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const {json} = this.selectedRecord.data;
|
|
121
|
+
|
|
122
|
+
if (this.readerContentType === 'document') {
|
|
123
|
+
this.readerContent = JSON.stringify(JSON.parse(json), null, 2);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let nodes = await XH.fetchJson({
|
|
128
|
+
url: this.matchingNodesUrl,
|
|
129
|
+
params: {
|
|
130
|
+
path: this.path,
|
|
131
|
+
asPathList: this.readerContentType === 'paths',
|
|
132
|
+
json
|
|
133
|
+
}
|
|
134
|
+
}).linkTo(this.nodeLoadTask);
|
|
135
|
+
|
|
136
|
+
this.matchingNodeCount = nodes.length;
|
|
137
|
+
if (this.asPathList && this.pathFormat === 'XPath') {
|
|
138
|
+
nodes = nodes.map(it => this.convertToPath(it));
|
|
139
|
+
}
|
|
140
|
+
this.readerContent = JSON.stringify(nodes, null, 2);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private convertToPath(JSONPath: string): string {
|
|
144
|
+
return JSONPath.replaceAll(/^\$\['?/g, '/')
|
|
145
|
+
.replaceAll(/^\$/g, '')
|
|
146
|
+
.replaceAll(/'?]\['?/g, '/')
|
|
147
|
+
.replaceAll(/'?]$/g, '');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@action
|
|
151
|
+
private setGroupBy(groupBy: string) {
|
|
152
|
+
this.groupBy = groupBy;
|
|
153
|
+
|
|
154
|
+
// Always select first when regrouping.
|
|
155
|
+
const groupByArr = groupBy ? groupBy.split(',') : [];
|
|
156
|
+
this.gridModel.setGroupBy(groupByArr);
|
|
157
|
+
this.gridModel.preSelectFirstAsync();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -5,9 +5,13 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as Col from '@xh/hoist/admin/columns/Rest';
|
|
9
|
+
import * as AdminCol from '@xh/hoist/admin/columns';
|
|
10
|
+
import {hframe} from '@xh/hoist/cmp/layout';
|
|
9
11
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
10
12
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
13
|
+
import {jsonSearchPanel} from '@xh/hoist/admin/jsonsearch/JsonSearchPanel';
|
|
14
|
+
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
11
15
|
import {restGrid} from '@xh/hoist/desktop/cmp/rest';
|
|
12
16
|
import {Icon} from '@xh/hoist/icon';
|
|
13
17
|
import {differ} from '../../../differ/Differ';
|
|
@@ -17,15 +21,49 @@ export const jsonBlobPanel = hoistCmp.factory({
|
|
|
17
21
|
model: creates(JsonBlobModel),
|
|
18
22
|
|
|
19
23
|
render({model}) {
|
|
20
|
-
return
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
return hframe(
|
|
25
|
+
panel({
|
|
26
|
+
item: restGrid({
|
|
27
|
+
extraToolbarItems: () => {
|
|
28
|
+
return button({
|
|
29
|
+
icon: Icon.diff(),
|
|
30
|
+
text: 'Compare w/ Remote',
|
|
31
|
+
onClick: () => model.openDiffer()
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}),
|
|
36
|
+
jsonSearchPanel({
|
|
37
|
+
docSearchUrl: 'jsonSearch/searchBlobs',
|
|
38
|
+
matchingNodesUrl: 'jsonSearch/getMatchingNodes',
|
|
39
|
+
gridModelConfig: {
|
|
40
|
+
sortBy: ['owner', 'name'],
|
|
41
|
+
store: {
|
|
42
|
+
idSpec: 'token'
|
|
43
|
+
},
|
|
44
|
+
columns: [
|
|
45
|
+
{
|
|
46
|
+
field: {name: 'token', type: 'string'},
|
|
47
|
+
hidden: true,
|
|
48
|
+
width: 100
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
field: {name: 'type', type: 'string'},
|
|
52
|
+
width: 200
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
field: {name: 'owner', type: 'string'},
|
|
56
|
+
width: 200
|
|
57
|
+
},
|
|
58
|
+
{...AdminCol.name},
|
|
59
|
+
{
|
|
60
|
+
field: {name: 'json', type: 'string'},
|
|
61
|
+
hidden: true
|
|
62
|
+
},
|
|
63
|
+
{...Col.lastUpdated}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
groupByOptions: ['owner', 'type', 'name']
|
|
29
67
|
}),
|
|
30
68
|
differ({omit: !model.differModel})
|
|
31
69
|
);
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import * as Col from '@xh/hoist/admin/columns/Rest';
|
|
8
|
+
import * as AdminCol from '@xh/hoist/admin/columns';
|
|
7
9
|
import {prefEditorDialog} from '@xh/hoist/admin/tabs/userData/prefs/editor/PrefEditorDialog';
|
|
8
10
|
import {UserPreferenceModel} from '@xh/hoist/admin/tabs/userData/prefs/UserPreferenceModel';
|
|
11
|
+
import {hframe} from '@xh/hoist/cmp/layout';
|
|
9
12
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
10
13
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
14
|
+
import {jsonSearchPanel} from '@xh/hoist/admin/jsonsearch/JsonSearchPanel';
|
|
11
15
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
12
16
|
import {restGrid} from '@xh/hoist/desktop/cmp/rest';
|
|
13
17
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -16,20 +20,43 @@ export const userPreferencePanel = hoistCmp.factory({
|
|
|
16
20
|
model: creates(UserPreferenceModel),
|
|
17
21
|
|
|
18
22
|
render({model}) {
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
return hframe(
|
|
24
|
+
panel({
|
|
25
|
+
items: [
|
|
26
|
+
restGrid({
|
|
27
|
+
extraToolbarItems: () => {
|
|
28
|
+
return button({
|
|
29
|
+
icon: Icon.gear(),
|
|
30
|
+
text: 'Configure',
|
|
31
|
+
onClick: () => (model.showEditorDialog = true)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
prefEditorDialog()
|
|
36
|
+
],
|
|
37
|
+
mask: 'onLoad'
|
|
38
|
+
}),
|
|
39
|
+
jsonSearchPanel({
|
|
40
|
+
docSearchUrl: 'jsonSearch/searchUserPreferences',
|
|
41
|
+
matchingNodesUrl: 'jsonSearch/getMatchingNodes',
|
|
42
|
+
gridModelConfig: {
|
|
43
|
+
sortBy: ['name'],
|
|
44
|
+
columns: [
|
|
45
|
+
{
|
|
46
|
+
field: {name: 'owner', type: 'string'},
|
|
47
|
+
width: 200
|
|
48
|
+
},
|
|
49
|
+
{...AdminCol.groupName},
|
|
50
|
+
{...AdminCol.name},
|
|
51
|
+
{
|
|
52
|
+
field: {name: 'json', type: 'string'},
|
|
53
|
+
hidden: true
|
|
54
|
+
},
|
|
55
|
+
{...Col.lastUpdated}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
groupByOptions: ['owner', 'groupName', 'name']
|
|
59
|
+
})
|
|
60
|
+
);
|
|
34
61
|
}
|
|
35
62
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { GridConfig } from '@xh/hoist/cmp/grid';
|
|
3
|
+
import { SelectOption } from '@xh/hoist/core';
|
|
4
|
+
export interface JsonSearchPanelProps {
|
|
5
|
+
/** Url to endpoint for searching for matching JSON documents */
|
|
6
|
+
docSearchUrl: string;
|
|
7
|
+
/** Url to endpoint for listing matching JSON nodes */
|
|
8
|
+
matchingNodesUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* Config for GridModel used to display search results.
|
|
11
|
+
*/
|
|
12
|
+
gridModelConfig: GridConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Names of field(s) that can be used to group by.
|
|
15
|
+
*/
|
|
16
|
+
groupByOptions: SelectOption[];
|
|
17
|
+
}
|
|
18
|
+
export declare const JsonSearchPanel: import("react").FC<import("@xh/hoist/core").DefaultHoistProps<import("@xh/hoist/core").HoistModel>>, jsonSearchPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<import("@xh/hoist/core").HoistModel>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
2
|
+
import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
|
|
3
|
+
import { HoistModel, TaskObserver } from '@xh/hoist/core';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export declare class JsonSearchPanelImplModel extends HoistModel {
|
|
8
|
+
xhImpl: boolean;
|
|
9
|
+
gridModel: GridModel;
|
|
10
|
+
groupingChooserModel: GroupingChooserModel;
|
|
11
|
+
docLoadTask: TaskObserver;
|
|
12
|
+
nodeLoadTask: TaskObserver;
|
|
13
|
+
groupBy: string;
|
|
14
|
+
error: any;
|
|
15
|
+
path: string;
|
|
16
|
+
readerContentType: 'document' | 'paths' | 'values';
|
|
17
|
+
pathFormat: 'XPath' | 'JSONPath';
|
|
18
|
+
readerContent: string;
|
|
19
|
+
matchingNodeCount: number;
|
|
20
|
+
get asPathList(): boolean;
|
|
21
|
+
get queryBuffer(): number;
|
|
22
|
+
get docSearchUrl(): string;
|
|
23
|
+
get matchingNodesUrl(): string;
|
|
24
|
+
get selectedRecord(): import("../../../data").StoreRecord;
|
|
25
|
+
get gridModelConfig(): any;
|
|
26
|
+
get groupByOptions(): any[];
|
|
27
|
+
constructor();
|
|
28
|
+
onLinked(): void;
|
|
29
|
+
loadJsonDocsAsync(): Promise<void>;
|
|
30
|
+
private loadreaderContentTypeAsync;
|
|
31
|
+
private convertToPath;
|
|
32
|
+
private setGroupBy;
|
|
33
|
+
}
|
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.1738073624805",
|
|
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",
|