@xh/hoist 72.0.0-SNAPSHOT.1737472364423 → 72.0.0-SNAPSHOT.1737670838944
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/columns/Tracking.ts +10 -0
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +2 -0
- package/admin/tabs/activity/tracking/detail/ActivityDetailModel.ts +1 -0
- package/admin/tabs/userData/jsonblob/JsonBlobPanel.ts +47 -9
- package/admin/tabs/userData/prefs/UserPreferencePanel.ts +46 -15
- package/build/types/admin/columns/Tracking.d.ts +1 -0
- package/build/types/core/types/Interfaces.d.ts +12 -3
- package/build/types/desktop/cmp/jsonsearch/JsonSearchPanel.d.ts +3 -0
- package/build/types/desktop/cmp/jsonsearch/impl/JsonSearchPanelImplModel.d.ts +27 -0
- package/core/types/Interfaces.ts +13 -3
- package/desktop/cmp/jsonsearch/JsonSearchPanel.ts +209 -0
- package/desktop/cmp/jsonsearch/impl/JsonSearchPanelImplModel.ts +130 -0
- package/package.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -37,6 +37,16 @@ export const browser: ColumnSpec = {
|
|
|
37
37
|
width: 100
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
export const severity: ColumnSpec = {
|
|
41
|
+
field: {
|
|
42
|
+
name: 'severity',
|
|
43
|
+
type: 'string',
|
|
44
|
+
isDimension: true,
|
|
45
|
+
aggregator: 'UNIQUE'
|
|
46
|
+
},
|
|
47
|
+
width: 80
|
|
48
|
+
};
|
|
49
|
+
|
|
40
50
|
export const category: ColumnSpec = {
|
|
41
51
|
field: {
|
|
42
52
|
name: 'category',
|
|
@@ -87,6 +87,7 @@ export class ActivityTrackingModel extends HoistModel {
|
|
|
87
87
|
fields: [
|
|
88
88
|
Col.browser.field,
|
|
89
89
|
Col.category.field,
|
|
90
|
+
Col.severity.field,
|
|
90
91
|
Col.correlationId.field,
|
|
91
92
|
Col.data.field,
|
|
92
93
|
{...(Col.dateCreated.field as FieldSpec), displayName: 'Timestamp'},
|
|
@@ -131,6 +132,7 @@ export class ActivityTrackingModel extends HoistModel {
|
|
|
131
132
|
{field: 'userAgent'},
|
|
132
133
|
{field: 'url', displayName: 'URL'},
|
|
133
134
|
{field: 'instance'},
|
|
135
|
+
{field: 'severity'},
|
|
134
136
|
{field: 'appVersion'},
|
|
135
137
|
{field: 'appEnvironment', displayName: 'Environment'}
|
|
136
138
|
]
|
|
@@ -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/desktop/cmp/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,14 +21,48 @@ 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
|
-
|
|
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: 'jsonBlobSearchAdmin/searchByJsonPath',
|
|
38
|
+
matchingNodesUrl: 'jsonBlobSearchAdmin/getMatchingNodes',
|
|
39
|
+
gridModelConfig: {
|
|
40
|
+
sortBy: ['owner', 'name'],
|
|
41
|
+
store: {
|
|
42
|
+
idSpec: 'token'
|
|
43
|
+
},
|
|
44
|
+
groupBy: 'type',
|
|
45
|
+
columns: [
|
|
46
|
+
{
|
|
47
|
+
field: {name: 'token', type: 'string'},
|
|
48
|
+
hidden: true,
|
|
49
|
+
width: 100
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
field: {name: 'type', type: 'string'},
|
|
53
|
+
width: 200
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
field: {name: 'owner', type: 'string'},
|
|
57
|
+
width: 200
|
|
58
|
+
},
|
|
59
|
+
{...Col.lastUpdated},
|
|
60
|
+
{
|
|
61
|
+
field: {name: 'json', type: 'string'},
|
|
62
|
+
hidden: true
|
|
63
|
+
},
|
|
64
|
+
{...AdminCol.name}
|
|
65
|
+
]
|
|
28
66
|
}
|
|
29
67
|
}),
|
|
30
68
|
differ({omit: !model.differModel})
|
|
@@ -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/desktop/cmp/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,47 @@ 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: 'preferenceJsonSearchAdmin/searchByJsonPath',
|
|
41
|
+
matchingNodesUrl: 'preferenceJsonSearchAdmin/getMatchingNodes',
|
|
42
|
+
gridModelConfig: {
|
|
43
|
+
sortBy: ['name'],
|
|
44
|
+
groupBy: 'groupName',
|
|
45
|
+
columns: [
|
|
46
|
+
{
|
|
47
|
+
field: {name: 'type', type: 'string'},
|
|
48
|
+
width: 200
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
field: {name: 'owner', type: 'string'},
|
|
52
|
+
width: 200
|
|
53
|
+
},
|
|
54
|
+
{...Col.lastUpdated},
|
|
55
|
+
{
|
|
56
|
+
field: {name: 'json', type: 'string'},
|
|
57
|
+
hidden: true
|
|
58
|
+
},
|
|
59
|
+
{...AdminCol.name},
|
|
60
|
+
{...AdminCol.groupName}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
);
|
|
34
65
|
}
|
|
35
66
|
});
|
|
@@ -2,6 +2,7 @@ import { ColumnSpec } from '@xh/hoist/cmp/grid/columns';
|
|
|
2
2
|
export declare const appEnvironment: ColumnSpec;
|
|
3
3
|
export declare const appVersion: ColumnSpec;
|
|
4
4
|
export declare const browser: ColumnSpec;
|
|
5
|
+
export declare const severity: ColumnSpec;
|
|
5
6
|
export declare const category: ColumnSpec;
|
|
6
7
|
export declare const data: ColumnSpec;
|
|
7
8
|
export declare const day: ColumnSpec;
|
|
@@ -153,6 +153,10 @@ export interface AppOptionSpec {
|
|
|
153
153
|
/** Optional flag to omit displaying option. */
|
|
154
154
|
omit?: Thunkable<boolean>;
|
|
155
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Severity levels for tracking. Default is 'INFO'.
|
|
158
|
+
*/
|
|
159
|
+
export type TrackSeverity = 'DEBUG' | 'INFO' | 'WARN';
|
|
156
160
|
/**
|
|
157
161
|
* Options for tracking activity on the server via TrackService.
|
|
158
162
|
*/
|
|
@@ -174,11 +178,16 @@ export interface TrackOptions {
|
|
|
174
178
|
*/
|
|
175
179
|
logData?: boolean | string[];
|
|
176
180
|
/**
|
|
177
|
-
* Flag to indicate relative importance of activity.
|
|
178
|
-
*
|
|
181
|
+
* Flag to indicate relative importance of activity. Default 'INFO'.
|
|
182
|
+
*
|
|
183
|
+
* Allows conditional saving of messages depending on the currently active
|
|
184
|
+
* level configuration for the category/user. See HoistCore's 'TrackService' for
|
|
185
|
+
* more information.
|
|
186
|
+
*
|
|
187
|
+
* Note, errors should be tracked via {@link XH.handleException}, which
|
|
179
188
|
* will post to the server for dedicated logging if requested.
|
|
180
189
|
*/
|
|
181
|
-
severity?:
|
|
190
|
+
severity?: TrackSeverity;
|
|
182
191
|
/**
|
|
183
192
|
* Set to true to log this message only once during the current session. The category and
|
|
184
193
|
* message text will be used as a compound key to identify repeated messages.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { JsonBlobModel } from '@xh/hoist/admin/tabs/userData/jsonblob/JsonBlobModel';
|
|
3
|
+
export declare const JsonSearchPanel: import("react").FC<import("@xh/hoist/core").DefaultHoistProps<JsonBlobModel>>, jsonSearchPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<JsonBlobModel>>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
2
|
+
import { HoistModel, TaskObserver } from '@xh/hoist/core';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export declare class JsonSearchPanelImplModel extends HoistModel {
|
|
7
|
+
xhImpl: boolean;
|
|
8
|
+
gridModel: GridModel;
|
|
9
|
+
docLoadTask: TaskObserver;
|
|
10
|
+
nodeLoadTask: TaskObserver;
|
|
11
|
+
error: any;
|
|
12
|
+
path: string;
|
|
13
|
+
pathOrValue: 'path' | 'value';
|
|
14
|
+
pathFormat: 'XPath' | 'JSONPath';
|
|
15
|
+
matchingNodes: string;
|
|
16
|
+
matchingNodeCount: number;
|
|
17
|
+
get asPathList(): boolean;
|
|
18
|
+
get queryBuffer(): number;
|
|
19
|
+
get docSearchUrl(): string;
|
|
20
|
+
get matchingNodesUrl(): string;
|
|
21
|
+
get gridModelConfig(): any;
|
|
22
|
+
constructor();
|
|
23
|
+
onLinked(): void;
|
|
24
|
+
loadJsonDocsAsync(): Promise<void>;
|
|
25
|
+
private loadJsonNodesAsync;
|
|
26
|
+
private convertToPath;
|
|
27
|
+
}
|
package/core/types/Interfaces.ts
CHANGED
|
@@ -193,6 +193,11 @@ export interface AppOptionSpec {
|
|
|
193
193
|
omit?: Thunkable<boolean>;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Severity levels for tracking. Default is 'INFO'.
|
|
198
|
+
*/
|
|
199
|
+
export type TrackSeverity = 'DEBUG' | 'INFO' | 'WARN';
|
|
200
|
+
|
|
196
201
|
/**
|
|
197
202
|
* Options for tracking activity on the server via TrackService.
|
|
198
203
|
*/
|
|
@@ -219,11 +224,16 @@ export interface TrackOptions {
|
|
|
219
224
|
logData?: boolean | string[];
|
|
220
225
|
|
|
221
226
|
/**
|
|
222
|
-
* Flag to indicate relative importance of activity.
|
|
223
|
-
*
|
|
227
|
+
* Flag to indicate relative importance of activity. Default 'INFO'.
|
|
228
|
+
*
|
|
229
|
+
* Allows conditional saving of messages depending on the currently active
|
|
230
|
+
* level configuration for the category/user. See HoistCore's 'TrackService' for
|
|
231
|
+
* more information.
|
|
232
|
+
*
|
|
233
|
+
* Note, errors should be tracked via {@link XH.handleException}, which
|
|
224
234
|
* will post to the server for dedicated logging if requested.
|
|
225
235
|
*/
|
|
226
|
-
severity?:
|
|
236
|
+
severity?: TrackSeverity;
|
|
227
237
|
|
|
228
238
|
/**
|
|
229
239
|
* Set to true to log this message only once during the current session. The category and
|
|
@@ -0,0 +1,209 @@
|
|
|
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 {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
|
|
9
|
+
import {errorMessage} from '@xh/hoist/cmp/error';
|
|
10
|
+
import {JsonBlobModel} from '@xh/hoist/admin/tabs/userData/jsonblob/JsonBlobModel';
|
|
11
|
+
import {grid, 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, useLocalModel} from '@xh/hoist/core';
|
|
14
|
+
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
15
|
+
import {buttonGroupInput, jsonInput, 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 {startCase} from 'lodash';
|
|
21
|
+
|
|
22
|
+
import {JsonSearchPanelImplModel} from './impl/JsonSearchPanelImplModel';
|
|
23
|
+
|
|
24
|
+
export const [JsonSearchPanel, jsonSearchPanel] = hoistCmp.withFactory<JsonBlobModel>({
|
|
25
|
+
render() {
|
|
26
|
+
const impl = useLocalModel(JsonSearchPanelImplModel),
|
|
27
|
+
{error} = impl;
|
|
28
|
+
|
|
29
|
+
return panel({
|
|
30
|
+
title: 'JSON Path Search',
|
|
31
|
+
icon: Icon.json(),
|
|
32
|
+
modelConfig: {
|
|
33
|
+
side: 'right',
|
|
34
|
+
defaultSize: '75%',
|
|
35
|
+
collapsible: true,
|
|
36
|
+
defaultCollapsed: true
|
|
37
|
+
},
|
|
38
|
+
compactHeader: true,
|
|
39
|
+
tbar: searchTbar({model: impl}),
|
|
40
|
+
flex: 1,
|
|
41
|
+
item: panel({
|
|
42
|
+
mask: impl.docLoadTask,
|
|
43
|
+
items: [
|
|
44
|
+
errorMessage({
|
|
45
|
+
error,
|
|
46
|
+
title: error?.name ? startCase(error.name) : undefined
|
|
47
|
+
}),
|
|
48
|
+
hframe({
|
|
49
|
+
omit: impl.error,
|
|
50
|
+
items: [
|
|
51
|
+
panel({
|
|
52
|
+
item: grid({model: impl.gridModel})
|
|
53
|
+
}),
|
|
54
|
+
panel({
|
|
55
|
+
mask: impl.nodeLoadTask,
|
|
56
|
+
tbar: nodeTbar({model: impl}),
|
|
57
|
+
bbar: nodeBbar({
|
|
58
|
+
omit: !impl.asPathList,
|
|
59
|
+
model: impl
|
|
60
|
+
}),
|
|
61
|
+
item: jsonInput({
|
|
62
|
+
model: impl,
|
|
63
|
+
bind: 'matchingNodes',
|
|
64
|
+
flex: 1,
|
|
65
|
+
width: '100%',
|
|
66
|
+
readonly: true,
|
|
67
|
+
showCopyButton: true
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const searchTbar = hoistCmp.factory<JsonSearchPanelImplModel>(({model}) => {
|
|
79
|
+
return toolbar(
|
|
80
|
+
pathField({model}),
|
|
81
|
+
helpButton(),
|
|
82
|
+
toolbarSep(),
|
|
83
|
+
gridCountLabel({
|
|
84
|
+
gridModel: model.gridModel,
|
|
85
|
+
unit: 'document'
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const pathField = hoistCmp.factory<JsonSearchPanelImplModel>({
|
|
91
|
+
render({model}) {
|
|
92
|
+
return textInput({
|
|
93
|
+
bind: 'path',
|
|
94
|
+
autoFocus: true,
|
|
95
|
+
commitOnChange: true,
|
|
96
|
+
leftIcon: Icon.search(),
|
|
97
|
+
enableClear: true,
|
|
98
|
+
placeholder:
|
|
99
|
+
"JSON Path - e.g. $..[?(@.colId == 'trader')] - type a path and hit ENTER to search",
|
|
100
|
+
width: null,
|
|
101
|
+
flex: 1,
|
|
102
|
+
onKeyDown: e => {
|
|
103
|
+
if (e.key === 'Enter') model.loadJsonDocsAsync();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const helpButton = hoistCmp.factory({
|
|
110
|
+
model: false,
|
|
111
|
+
render() {
|
|
112
|
+
return popover({
|
|
113
|
+
item: button({
|
|
114
|
+
icon: Icon.questionCircle(),
|
|
115
|
+
outlined: true
|
|
116
|
+
}),
|
|
117
|
+
content: vbox({
|
|
118
|
+
style: {
|
|
119
|
+
padding: '0px 20px 10px 20px'
|
|
120
|
+
},
|
|
121
|
+
items: [
|
|
122
|
+
h4('Sample Queries'),
|
|
123
|
+
ul({
|
|
124
|
+
style: {listStyleType: 'none'},
|
|
125
|
+
items: [
|
|
126
|
+
{
|
|
127
|
+
query: "$..[?(@.colId == 'trader')]",
|
|
128
|
+
explanation:
|
|
129
|
+
'Find all nodes with a property "colId" equal to "trader"'
|
|
130
|
+
}
|
|
131
|
+
].map(({query, explanation}) =>
|
|
132
|
+
li({
|
|
133
|
+
key: query,
|
|
134
|
+
items: [
|
|
135
|
+
span({
|
|
136
|
+
className: 'xh-bg-alt xh-gray-dark xh-font-family-mono',
|
|
137
|
+
item: query
|
|
138
|
+
}),
|
|
139
|
+
' ',
|
|
140
|
+
clipboardButton({
|
|
141
|
+
text: null,
|
|
142
|
+
icon: Icon.copy(),
|
|
143
|
+
getCopyText: () => query,
|
|
144
|
+
successMessage: 'Query copied to clipboard.'
|
|
145
|
+
}),
|
|
146
|
+
' ',
|
|
147
|
+
explanation
|
|
148
|
+
]
|
|
149
|
+
})
|
|
150
|
+
)
|
|
151
|
+
}),
|
|
152
|
+
a({
|
|
153
|
+
href: 'https://github.com/json-path/JsonPath?tab=readme-ov-file#operators',
|
|
154
|
+
target: '_blank',
|
|
155
|
+
item: 'Path Syntax Docs & More Examples'
|
|
156
|
+
})
|
|
157
|
+
]
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const nodeTbar = hoistCmp.factory<JsonSearchPanelImplModel>(({model}) => {
|
|
164
|
+
return toolbar(
|
|
165
|
+
buttonGroupInput({
|
|
166
|
+
model,
|
|
167
|
+
bind: 'pathOrValue',
|
|
168
|
+
minimal: true,
|
|
169
|
+
outlined: true,
|
|
170
|
+
items: [
|
|
171
|
+
button({
|
|
172
|
+
text: 'Path',
|
|
173
|
+
value: 'path'
|
|
174
|
+
}),
|
|
175
|
+
button({
|
|
176
|
+
text: 'Value',
|
|
177
|
+
value: 'value'
|
|
178
|
+
})
|
|
179
|
+
]
|
|
180
|
+
}),
|
|
181
|
+
filler(),
|
|
182
|
+
box({
|
|
183
|
+
omit: !model.matchingNodeCount,
|
|
184
|
+
item: `${model.matchingNodeCount} ${model.matchingNodeCount === 1 ? 'match' : 'matches'}`
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const nodeBbar = hoistCmp.factory<JsonSearchPanelImplModel>(({model}) => {
|
|
190
|
+
return toolbar(
|
|
191
|
+
label('Path Format:'),
|
|
192
|
+
buttonGroupInput({
|
|
193
|
+
model,
|
|
194
|
+
bind: 'pathFormat',
|
|
195
|
+
minimal: true,
|
|
196
|
+
outlined: true,
|
|
197
|
+
items: [
|
|
198
|
+
button({
|
|
199
|
+
text: 'XPath',
|
|
200
|
+
value: 'XPath'
|
|
201
|
+
}),
|
|
202
|
+
button({
|
|
203
|
+
text: 'JSONPath',
|
|
204
|
+
value: 'JSONPath'
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
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 {HoistModel, managed, TaskObserver, XH} from '@xh/hoist/core';
|
|
10
|
+
import {bindable, makeObservable} from '@xh/hoist/mobx';
|
|
11
|
+
import {isEmpty} from 'lodash';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export class JsonSearchPanelImplModel extends HoistModel {
|
|
17
|
+
override xhImpl = true;
|
|
18
|
+
|
|
19
|
+
@managed gridModel: GridModel;
|
|
20
|
+
@managed docLoadTask: TaskObserver = TaskObserver.trackLast();
|
|
21
|
+
@managed nodeLoadTask: TaskObserver = TaskObserver.trackLast();
|
|
22
|
+
|
|
23
|
+
@bindable.ref error = null;
|
|
24
|
+
@bindable path: string = '';
|
|
25
|
+
@bindable pathOrValue: 'path' | 'value' = 'value';
|
|
26
|
+
@bindable pathFormat: 'XPath' | 'JSONPath' = 'XPath';
|
|
27
|
+
@bindable matchingNodes: string = '';
|
|
28
|
+
@bindable matchingNodeCount: number = 0;
|
|
29
|
+
|
|
30
|
+
get asPathList(): boolean {
|
|
31
|
+
return this.pathOrValue === 'path';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get queryBuffer(): number {
|
|
35
|
+
return this.componentProps.queryBuffer ?? 200;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get docSearchUrl(): string {
|
|
39
|
+
return this.componentProps.docSearchUrl;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get matchingNodesUrl(): string {
|
|
43
|
+
return this.componentProps.matchingNodesUrl;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get gridModelConfig() {
|
|
47
|
+
return this.componentProps.gridModelConfig;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
constructor() {
|
|
51
|
+
super();
|
|
52
|
+
makeObservable(this);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override onLinked() {
|
|
56
|
+
this.gridModel = new GridModel({
|
|
57
|
+
...this.gridModelConfig,
|
|
58
|
+
selModel: 'single'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.addReaction(
|
|
62
|
+
{
|
|
63
|
+
track: () => this.path,
|
|
64
|
+
run: path => {
|
|
65
|
+
if (isEmpty(path)) {
|
|
66
|
+
this.error = null;
|
|
67
|
+
this.gridModel.clear();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
track: () => [this.gridModel.selectedRecord, this.pathOrValue, this.pathFormat],
|
|
73
|
+
run: () => this.loadJsonNodesAsync(),
|
|
74
|
+
debounce: 300
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async loadJsonDocsAsync() {
|
|
80
|
+
if (isEmpty(this.path)) {
|
|
81
|
+
this.error = null;
|
|
82
|
+
this.gridModel.clear();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const data = await XH.fetchJson({
|
|
88
|
+
url: this.docSearchUrl,
|
|
89
|
+
params: {path: this.path}
|
|
90
|
+
}).linkTo(this.docLoadTask);
|
|
91
|
+
|
|
92
|
+
this.error = null;
|
|
93
|
+
this.gridModel.loadData(data);
|
|
94
|
+
this.gridModel.selectFirstAsync();
|
|
95
|
+
} catch (e) {
|
|
96
|
+
this.gridModel.clear();
|
|
97
|
+
this.error = e;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private async loadJsonNodesAsync() {
|
|
102
|
+
if (!this.gridModel.selectedRecord) {
|
|
103
|
+
this.matchingNodeCount = 0;
|
|
104
|
+
this.matchingNodes = '';
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let nodes = await XH.fetchJson({
|
|
109
|
+
url: this.matchingNodesUrl,
|
|
110
|
+
params: {
|
|
111
|
+
path: this.path,
|
|
112
|
+
asPathList: this.pathOrValue === 'path',
|
|
113
|
+
json: this.gridModel.selectedRecord.data.json
|
|
114
|
+
}
|
|
115
|
+
}).linkTo(this.nodeLoadTask);
|
|
116
|
+
|
|
117
|
+
this.matchingNodeCount = nodes.length;
|
|
118
|
+
if (this.asPathList && this.pathFormat === 'XPath') {
|
|
119
|
+
nodes = nodes.map(it => this.convertToPath(it));
|
|
120
|
+
}
|
|
121
|
+
this.matchingNodes = JSON.stringify(nodes, null, 2);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private convertToPath(JSONPath: string): string {
|
|
125
|
+
return JSONPath.replaceAll(/^\$\['?/g, '/')
|
|
126
|
+
.replaceAll(/^\$/g, '')
|
|
127
|
+
.replaceAll(/'?]\['?/g, '/')
|
|
128
|
+
.replaceAll(/'?]$/g, '');
|
|
129
|
+
}
|
|
130
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "72.0.0-SNAPSHOT.
|
|
3
|
+
"version": "72.0.0-SNAPSHOT.1737670838944",
|
|
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",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"@ag-grid-community/react": "31.x",
|
|
98
98
|
"@types/react": "18.x",
|
|
99
99
|
"@types/react-dom": "18.x",
|
|
100
|
-
"@xh/hoist-dev-utils": "
|
|
100
|
+
"@xh/hoist-dev-utils": "10.x",
|
|
101
101
|
"csstype": "3.x",
|
|
102
102
|
"eslint": "8.x",
|
|
103
103
|
"eslint-config-prettier": "9.x",
|