@xh/hoist 86.0.1 → 86.1.0
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 +58 -0
- package/admin/tabs/userData/roles/RoleModel.ts +1 -19
- package/appcontainer/RouterModel.ts +2 -0
- package/appcontainer/ThemeModel.ts +41 -0
- package/appcontainer/login/LoginPanelModel.ts +6 -2
- package/build/types/appcontainer/RouterModel.d.ts +1 -0
- package/build/types/appcontainer/ThemeModel.d.ts +9 -0
- package/build/types/cmp/input/SegmentedControlOption.d.ts +44 -0
- package/build/types/cmp/input/index.d.ts +1 -0
- package/build/types/core/AppSpec.d.ts +8 -2
- package/build/types/core/HoistAuthModel.d.ts +6 -3
- package/build/types/data/filter/BaseFilterFieldSpec.d.ts +7 -1
- package/build/types/data/filter/FieldFilter.d.ts +2 -0
- package/build/types/desktop/cmp/filechooser/FileChooserModel.d.ts +1 -1
- package/build/types/desktop/cmp/input/SegmentedControl.d.ts +1 -41
- package/build/types/icon/Icon.d.ts +1 -0
- package/build/types/mobile/cmp/appOption/SizingModeAppOption.d.ts +3 -3
- package/build/types/mobile/cmp/appOption/ThemeAppOption.d.ts +3 -3
- package/build/types/mobile/cmp/input/SegmentedControl.d.ts +47 -0
- package/build/types/mobile/cmp/input/SwitchInput.d.ts +1 -1
- package/build/types/mobile/cmp/input/TextInput.d.ts +3 -0
- package/build/types/mobile/cmp/input/index.d.ts +1 -0
- package/build/types/svc/JsonBlobService.d.ts +7 -7
- package/cmp/error/ErrorMessage.scss +2 -0
- package/cmp/grid/filter/GridFilterFieldSpec.ts +5 -2
- package/cmp/grid/impl/GridHScrollbar.ts +10 -3
- package/cmp/input/SegmentedControlOption.ts +61 -0
- package/cmp/input/index.ts +1 -0
- package/core/AppSpec.ts +9 -1
- package/core/HoistAuthModel.ts +13 -6
- package/data/Field.ts +3 -1
- package/data/filter/BaseFilterFieldSpec.ts +16 -2
- package/data/filter/FieldFilter.ts +90 -21
- package/desktop/appcontainer/LoginPanel.ts +5 -3
- package/desktop/cmp/filechooser/FileChooserModel.ts +1 -1
- package/desktop/cmp/grid/impl/filter/headerfilter/HeaderFilterModel.ts +9 -4
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRowModel.ts +2 -2
- package/desktop/cmp/input/SegmentedControl.ts +8 -51
- package/desktop/cmp/tab/Tabs.scss +6 -2
- package/desktop/cmp/tab/dynamic/DynamicTabSwitcher.ts +7 -3
- package/docs/error-handling.md +9 -0
- package/icon/Icon.ts +3 -0
- package/icon/index.ts +8 -0
- package/mobile/appcontainer/LoginPanel.scss +53 -11
- package/mobile/appcontainer/LoginPanel.ts +69 -44
- package/mobile/cmp/appOption/SizingModeAppOption.ts +5 -16
- package/mobile/cmp/appOption/ThemeAppOption.ts +8 -25
- package/mobile/cmp/error/impl/ErrorMessage.ts +4 -8
- package/mobile/cmp/input/SegmentedControl.scss +126 -0
- package/mobile/cmp/input/SegmentedControl.ts +210 -0
- package/mobile/cmp/input/SwitchInput.ts +1 -1
- package/mobile/cmp/input/TextInput.scss +7 -0
- package/mobile/cmp/input/TextInput.ts +11 -1
- package/mobile/cmp/input/index.ts +1 -0
- package/mobile/cmp/tab/impl/Tabs.scss +8 -0
- package/mobile/cmp/toolbar/Toolbar.scss +8 -0
- package/package.json +2 -2
- package/styles/vars.scss +6 -1
- package/svc/JsonBlobService.ts +21 -20
|
@@ -0,0 +1,210 @@
|
|
|
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 © 2026 Extremely Heavy Industries Inc.
|
|
6
|
+
*/
|
|
7
|
+
import {
|
|
8
|
+
HoistInputModel,
|
|
9
|
+
HoistInputProps,
|
|
10
|
+
OptionPrimitive,
|
|
11
|
+
SegmentedControlNullOption,
|
|
12
|
+
SegmentedControlOption,
|
|
13
|
+
useHoistInputModel
|
|
14
|
+
} from '@xh/hoist/cmp/input';
|
|
15
|
+
import {hbox, span} from '@xh/hoist/cmp/layout';
|
|
16
|
+
import {hoistCmp, HoistProps, Intent} from '@xh/hoist/core';
|
|
17
|
+
import {button} from '@xh/hoist/mobile/cmp/button';
|
|
18
|
+
import '@xh/hoist/mobile/register';
|
|
19
|
+
import {computed, makeObservable} from '@xh/hoist/mobx';
|
|
20
|
+
import {TEST_ID} from '@xh/hoist/utils/js';
|
|
21
|
+
import {getLayoutProps, getNonLayoutProps} from '@xh/hoist/utils/react';
|
|
22
|
+
import classNames from 'classnames';
|
|
23
|
+
import {filter, isObject} from 'lodash';
|
|
24
|
+
import './SegmentedControl.scss';
|
|
25
|
+
|
|
26
|
+
export interface SegmentedControlProps extends HoistProps, HoistInputProps {
|
|
27
|
+
/**
|
|
28
|
+
* True (default) to render all segments at an equal width when {@link fill} is enabled,
|
|
29
|
+
* dividing the available width into equal parts regardless of label length - the conventional
|
|
30
|
+
* segmented-control appearance. Set false to instead size each segment to its content and
|
|
31
|
+
* share only the leftover space, so a longer label yields a wider segment. No effect when
|
|
32
|
+
* `fill` is false. Either way, a label too wide for its segment truncates with an ellipsis.
|
|
33
|
+
*/
|
|
34
|
+
equalSegmentWidths?: boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* True (default) to stretch the control to fill available width,
|
|
38
|
+
* distributing space equally among options.
|
|
39
|
+
*/
|
|
40
|
+
fill?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default visual intent applied to the selected option, and as a subtle text-color hint
|
|
44
|
+
* to options when not selected. Serves as the default for any option that does not specify
|
|
45
|
+
* its own `intent`. Defaults to `'none'`.
|
|
46
|
+
*/
|
|
47
|
+
intent?: 'none' | Intent;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Array of available options. Each entry may be a SegmentedControlOption object
|
|
51
|
+
* with value/label/icon/disabled properties, or a primitive value used as both
|
|
52
|
+
* the value and the display label.
|
|
53
|
+
*/
|
|
54
|
+
options: Array<SegmentedControlOption | SegmentedControlNullOption | OptionPrimitive>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* True to render with an outlined style - a border around the control tray
|
|
58
|
+
* with no inner background fill. Border color follows the current intent.
|
|
59
|
+
*/
|
|
60
|
+
outlined?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* An input for selecting a single value from a small set of mutually exclusive options,
|
|
65
|
+
* rendered as a group of toggle buttons with clear visual indication of the active
|
|
66
|
+
* selection.
|
|
67
|
+
*
|
|
68
|
+
* Similar to ButtonGroupInput but driven by an `options` prop (like Select) rather than Button
|
|
69
|
+
* children, and with stronger visual differentiation between selected and unselected states.
|
|
70
|
+
* The mobile counterpart to the desktop SegmentedControl, built on Hoist's mobile Button
|
|
71
|
+
* (no Blueprint dependency).
|
|
72
|
+
*/
|
|
73
|
+
export const [SegmentedControl, segmentedControl] = hoistCmp.withFactory<SegmentedControlProps>({
|
|
74
|
+
displayName: 'SegmentedControl',
|
|
75
|
+
className: 'xh-segmented-control',
|
|
76
|
+
render(props, ref) {
|
|
77
|
+
return useHoistInputModel(cmp, props, ref, SegmentedControlModel);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
(SegmentedControl as any).hasLayoutSupport = true;
|
|
81
|
+
|
|
82
|
+
//-----------------------
|
|
83
|
+
// Implementation
|
|
84
|
+
//-----------------------
|
|
85
|
+
interface NormalizedOption extends SegmentedControlOption {
|
|
86
|
+
label: string;
|
|
87
|
+
intent?: Intent;
|
|
88
|
+
_key: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class SegmentedControlModel extends HoistInputModel {
|
|
92
|
+
override xhImpl = true;
|
|
93
|
+
|
|
94
|
+
@computed
|
|
95
|
+
get normalizedOptions(): NormalizedOption[] {
|
|
96
|
+
const options = this.componentProps.options ?? [];
|
|
97
|
+
return options.map((o: any, idx: number) => {
|
|
98
|
+
const key = String(idx);
|
|
99
|
+
if (isObject(o)) {
|
|
100
|
+
const {label, value, icon, disabled, intent} = o as SegmentedControlOption;
|
|
101
|
+
return {
|
|
102
|
+
value: this.toInternal(value),
|
|
103
|
+
label: label ?? (icon ? '' : String(value)),
|
|
104
|
+
icon,
|
|
105
|
+
disabled,
|
|
106
|
+
intent,
|
|
107
|
+
_key: key
|
|
108
|
+
};
|
|
109
|
+
} else {
|
|
110
|
+
return {value: this.toInternal(o), label: String(o), _key: key};
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Map the current render value to the string key used to identify the selected option. */
|
|
116
|
+
@computed
|
|
117
|
+
get selectedKey(): string {
|
|
118
|
+
const {renderValue, normalizedOptions} = this;
|
|
119
|
+
return normalizedOptions.find(o => o.value === renderValue)?._key;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get enabledButtons(): HTMLButtonElement[] {
|
|
123
|
+
const btns = this.domEl?.querySelectorAll('button') ?? [];
|
|
124
|
+
return filter(btns, (b: HTMLButtonElement) => !b.disabled) as HTMLButtonElement[];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
constructor() {
|
|
128
|
+
super();
|
|
129
|
+
makeObservable(this);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
onValueChange = (key: string) => {
|
|
133
|
+
const match = this.normalizedOptions.find(o => o._key === key);
|
|
134
|
+
if (match) this.noteValueChange(match.value);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
override blur() {
|
|
138
|
+
this.enabledButtons.forEach(it => it.blur());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
override focus() {
|
|
142
|
+
this.enabledButtons[0]?.focus();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props}, ref) => {
|
|
147
|
+
const {
|
|
148
|
+
// HoistInput props - consumed here or by the model, not passed to the tray
|
|
149
|
+
bind,
|
|
150
|
+
disabled,
|
|
151
|
+
onChange,
|
|
152
|
+
onCommit,
|
|
153
|
+
tabIndex,
|
|
154
|
+
value,
|
|
155
|
+
commitOnChange,
|
|
156
|
+
options,
|
|
157
|
+
// Consumed by this component
|
|
158
|
+
equalSegmentWidths = true,
|
|
159
|
+
fill = true,
|
|
160
|
+
intent,
|
|
161
|
+
outlined,
|
|
162
|
+
testId,
|
|
163
|
+
...rest
|
|
164
|
+
} = getNonLayoutProps(props);
|
|
165
|
+
|
|
166
|
+
const {selectedKey} = model,
|
|
167
|
+
defaultIntent = intent && intent !== 'none' ? intent : null;
|
|
168
|
+
|
|
169
|
+
const buttons = model.normalizedOptions.map(opt => {
|
|
170
|
+
const optIntent = opt.intent ?? defaultIntent,
|
|
171
|
+
selected = opt._key === selectedKey;
|
|
172
|
+
// Wrap the label so it can truncate with an ellipsis when the segment is too narrow,
|
|
173
|
+
// rather than hard-clipping mid-character. Pass null for icon-only options so the Button
|
|
174
|
+
// renders the icon alone (an empty span would suppress that).
|
|
175
|
+
const label = opt.label
|
|
176
|
+
? span({className: 'xh-segmented-control-option__label', item: opt.label})
|
|
177
|
+
: null;
|
|
178
|
+
|
|
179
|
+
return button({
|
|
180
|
+
key: opt._key,
|
|
181
|
+
text: label,
|
|
182
|
+
icon: opt.icon,
|
|
183
|
+
disabled: disabled || opt.disabled,
|
|
184
|
+
minimal: true,
|
|
185
|
+
className: classNames(
|
|
186
|
+
'xh-segmented-control-option',
|
|
187
|
+
selected && 'xh-segmented-control-option--selected',
|
|
188
|
+
optIntent && `xh-segmented-control-option--${optIntent}`
|
|
189
|
+
),
|
|
190
|
+
onClick: () => model.onValueChange(opt._key)
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
return hbox({
|
|
195
|
+
className: classNames(
|
|
196
|
+
className,
|
|
197
|
+
defaultIntent && `xh-segmented-control--${defaultIntent}`,
|
|
198
|
+
outlined && 'xh-segmented-control--outlined',
|
|
199
|
+
fill && 'xh-segmented-control--fill',
|
|
200
|
+
fill && equalSegmentWidths && 'xh-segmented-control--equal-widths'
|
|
201
|
+
),
|
|
202
|
+
ref,
|
|
203
|
+
onFocus: model.onFocus,
|
|
204
|
+
onBlur: model.onBlur,
|
|
205
|
+
...getLayoutProps(props),
|
|
206
|
+
[TEST_ID]: testId,
|
|
207
|
+
items: buttons,
|
|
208
|
+
...rest
|
|
209
|
+
});
|
|
210
|
+
});
|
|
@@ -12,7 +12,7 @@ import {TEST_ID} from '@xh/hoist/utils/js';
|
|
|
12
12
|
import './SwitchInput.scss';
|
|
13
13
|
|
|
14
14
|
export interface SwitchInputProps extends HoistProps, HoistInputProps, StyleProps {
|
|
15
|
-
value?:
|
|
15
|
+
value?: boolean;
|
|
16
16
|
|
|
17
17
|
/** Onsen modifier string */
|
|
18
18
|
modifier?: string;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
|
|
8
|
-
import {hbox} from '@xh/hoist/cmp/layout';
|
|
8
|
+
import {box, hbox} from '@xh/hoist/cmp/layout';
|
|
9
9
|
import {hoistCmp, HoistProps, LayoutProps, StyleProps} from '@xh/hoist/core';
|
|
10
10
|
import {Icon} from '@xh/hoist/icon';
|
|
11
11
|
import {input} from '@xh/hoist/kit/onsen';
|
|
@@ -15,6 +15,7 @@ import {getTestId, TEST_ID, withDefault} from '@xh/hoist/utils/js';
|
|
|
15
15
|
import {getLayoutProps} from '@xh/hoist/utils/react';
|
|
16
16
|
import type {Property} from 'csstype';
|
|
17
17
|
import {isEmpty} from 'lodash';
|
|
18
|
+
import {ReactElement} from 'react';
|
|
18
19
|
import './TextInput.scss';
|
|
19
20
|
|
|
20
21
|
export interface TextInputProps extends HoistProps, HoistInputProps, StyleProps, LayoutProps {
|
|
@@ -46,6 +47,9 @@ export interface TextInputProps extends HoistProps, HoistInputProps, StyleProps,
|
|
|
46
47
|
/** True to show a "clear" button aligned to the right of the control. Defaults to false. */
|
|
47
48
|
enableClear?: boolean;
|
|
48
49
|
|
|
50
|
+
/** Icon to display inline on the left side of the input. */
|
|
51
|
+
leftIcon?: ReactElement;
|
|
52
|
+
|
|
49
53
|
/** Onsen modifier string */
|
|
50
54
|
modifier?: string;
|
|
51
55
|
|
|
@@ -123,6 +127,7 @@ const cmp = hoistCmp.factory<TextInputModel>(({model, className, ...props}, ref)
|
|
|
123
127
|
width: withDefault(width, null)
|
|
124
128
|
},
|
|
125
129
|
items: [
|
|
130
|
+
leftIcon(),
|
|
126
131
|
input({
|
|
127
132
|
value: model.renderValue || '',
|
|
128
133
|
|
|
@@ -151,6 +156,11 @@ const cmp = hoistCmp.factory<TextInputModel>(({model, className, ...props}, ref)
|
|
|
151
156
|
});
|
|
152
157
|
});
|
|
153
158
|
|
|
159
|
+
const leftIcon = hoistCmp.factory<TextInputModel>(({model}) => {
|
|
160
|
+
const {leftIcon} = model.componentProps;
|
|
161
|
+
return leftIcon ? box({className: 'xh-text-input__left-icon', item: leftIcon}) : null;
|
|
162
|
+
});
|
|
163
|
+
|
|
154
164
|
const clearButton = hoistCmp.factory<TextInputModel>(({model}) =>
|
|
155
165
|
button({
|
|
156
166
|
className: 'xh-text-input__clear-button',
|
|
@@ -11,6 +11,7 @@ export * from './CheckboxButton';
|
|
|
11
11
|
export * from './DateInput';
|
|
12
12
|
export * from './NumberInput';
|
|
13
13
|
export * from './SearchInput';
|
|
14
|
+
export * from './SegmentedControl';
|
|
14
15
|
export * from './Select';
|
|
15
16
|
export * from './SwitchInput';
|
|
16
17
|
export * from './TextInput';
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
transform: translateX(0);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Theme the page backdrop. Onsen renders an opaque, light-by-default `.page__background` behind
|
|
29
|
+
// each page's content, and the content itself is forced transparent (so the app background shows
|
|
30
|
+
// through). That leaves the light backdrop visible for tab content that is not itself a panel,
|
|
31
|
+
// rendering themed text unreadable in dark mode - so paint the backdrop with the app background.
|
|
32
|
+
.xh-tab-page .page__background {
|
|
33
|
+
background-color: var(--xh-bg);
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
.xh-tab-container {
|
|
29
37
|
// Override Onsen's tabbar height CSS var
|
|
30
38
|
--tabbar-height: var(--xh-tbar-min-size-px);
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
.xh-toolbar {
|
|
8
|
+
// Toolbars are the compact-button special case: keep buttons short and tight-cornered so they
|
|
9
|
+
// fit dense bars, overriding the taller / rounder defaults used for buttons in body content
|
|
10
|
+
// (see vars.scss). Derive the button height from the bar size so a single token
|
|
11
|
+
// (--xh-tbar-min-size) drives both - subtracting the bar's vertical padding so buttons fit a
|
|
12
|
+
// horizontal bar. The same height is reused for vertical bars to keep buttons consistent.
|
|
13
|
+
--xh-button-height: calc(var(--xh-tbar-min-size-px) - 2 * var(--xh-pad-half-px));
|
|
14
|
+
--xh-button-border-radius-px: var(--xh-border-radius-px);
|
|
15
|
+
|
|
8
16
|
padding: var(--xh-pad-half-px) var(--xh-pad-px);
|
|
9
17
|
color: var(--xh-tbar-text-color);
|
|
10
18
|
background-color: var(--xh-tbar-bg);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "86.0
|
|
3
|
+
"version": "86.1.0",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@auth0/auth0-spa-js": "~2.21.1",
|
|
42
|
-
"@azure/msal-browser": "~5.
|
|
42
|
+
"@azure/msal-browser": "~5.14.0",
|
|
43
43
|
"@blueprintjs/core": "^6.3.2",
|
|
44
44
|
"@blueprintjs/datetime": "^6.0.6",
|
|
45
45
|
"@codemirror/commands": "~6.10.3",
|
package/styles/vars.scss
CHANGED
|
@@ -344,7 +344,12 @@ body {
|
|
|
344
344
|
--xh-button-active-text-color: var(--button-active-text-color, white);
|
|
345
345
|
--xh-button-bg: var(--button-bg, #{mc('blue-grey', '600')});
|
|
346
346
|
--xh-button-text-color: var(--button-text-color, white);
|
|
347
|
-
|
|
347
|
+
// Default to a comfortable touch height for buttons in body content - a bit more compact than
|
|
348
|
+
// the 44px toolbar/touch-target max, which reads as oversized for XH's denser style. Toolbars
|
|
349
|
+
// derive their own slightly smaller button height from the bar size (see Toolbar.scss).
|
|
350
|
+
--xh-button-height: var(--button-height, 40px);
|
|
351
|
+
// Slightly rounder corners to suit the taller mobile button (the desktop default is tighter).
|
|
352
|
+
--xh-button-border-radius-px: var(--button-border-radius-px, 8px);
|
|
348
353
|
--xh-toolbar-button-bg: var(--toolbar-button-bg, var(--xh-button-bg));
|
|
349
354
|
|
|
350
355
|
// Light theme pressed bg for 'minimal' / 'outlined'
|
package/svc/JsonBlobService.ts
CHANGED
|
@@ -52,8 +52,8 @@ export class JsonBlobService extends HoistService {
|
|
|
52
52
|
static instance: JsonBlobService;
|
|
53
53
|
|
|
54
54
|
/** Retrieve a single JSONBlob by its unique token. */
|
|
55
|
-
async getAsync(token: string): Promise<JsonBlob> {
|
|
56
|
-
return this.runner().span('get').fetchJson({
|
|
55
|
+
async getAsync(token: string, ctx?: CallContextLike): Promise<JsonBlob> {
|
|
56
|
+
return this.runner(ctx).span('get').fetchJson({
|
|
57
57
|
url: 'xh/getJsonBlob',
|
|
58
58
|
params: {token}
|
|
59
59
|
});
|
|
@@ -63,7 +63,7 @@ export class JsonBlobService extends HoistService {
|
|
|
63
63
|
async listAsync(spec: {
|
|
64
64
|
type: string;
|
|
65
65
|
includeValue?: boolean;
|
|
66
|
-
ctx
|
|
66
|
+
ctx?: CallContextLike;
|
|
67
67
|
}): Promise<JsonBlob[]> {
|
|
68
68
|
const {type, includeValue, ctx} = spec;
|
|
69
69
|
return this.runner(ctx)
|
|
@@ -72,15 +72,11 @@ export class JsonBlobService extends HoistService {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Persist a new JSONBlob back to the server. */
|
|
75
|
-
async createAsync(
|
|
76
|
-
acl,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
name,
|
|
81
|
-
value
|
|
82
|
-
}: Partial<JsonBlob>): Promise<JsonBlob> {
|
|
83
|
-
return this.runner()
|
|
75
|
+
async createAsync(
|
|
76
|
+
{acl, description, type, meta, name, value}: Partial<JsonBlob>,
|
|
77
|
+
ctx?: CallContextLike
|
|
78
|
+
): Promise<JsonBlob> {
|
|
79
|
+
return this.runner(ctx)
|
|
84
80
|
.span('create')
|
|
85
81
|
.fetchJson({
|
|
86
82
|
url: 'xh/createJsonBlob',
|
|
@@ -91,8 +87,12 @@ export class JsonBlobService extends HoistService {
|
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
/** Modify mutable properties of an existing JSONBlob, as identified by its unique token. */
|
|
94
|
-
async updateAsync(
|
|
95
|
-
|
|
90
|
+
async updateAsync(
|
|
91
|
+
token: string,
|
|
92
|
+
update: Partial<JsonBlob>,
|
|
93
|
+
ctx?: CallContextLike
|
|
94
|
+
): Promise<JsonBlob> {
|
|
95
|
+
return this.runner(ctx)
|
|
96
96
|
.span('update')
|
|
97
97
|
.run(async ctx => {
|
|
98
98
|
update = pick(update, ['acl', 'description', 'meta', 'name', 'owner', 'value']);
|
|
@@ -110,9 +110,10 @@ export class JsonBlobService extends HoistService {
|
|
|
110
110
|
async createOrUpdateAsync(
|
|
111
111
|
type: string,
|
|
112
112
|
name: string,
|
|
113
|
-
data: Partial<JsonBlob
|
|
113
|
+
data: Partial<JsonBlob>,
|
|
114
|
+
ctx?: CallContextLike
|
|
114
115
|
): Promise<JsonBlob> {
|
|
115
|
-
return this.runner()
|
|
116
|
+
return this.runner(ctx)
|
|
116
117
|
.span('createOrUpdate')
|
|
117
118
|
.run(async ctx => {
|
|
118
119
|
const update = pick(data, ['acl', 'description', 'meta', 'value']);
|
|
@@ -127,16 +128,16 @@ export class JsonBlobService extends HoistService {
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
/** Find a blob owned by this user with a specific type and name. If none exists, return null. */
|
|
130
|
-
async findAsync(type: string, name: string): Promise<JsonBlob> {
|
|
131
|
-
return this.runner().span('find').fetchJson({
|
|
131
|
+
async findAsync(type: string, name: string, ctx?: CallContextLike): Promise<JsonBlob> {
|
|
132
|
+
return this.runner(ctx).span('find').fetchJson({
|
|
132
133
|
url: 'xh/findJsonBlob',
|
|
133
134
|
params: {type, name}
|
|
134
135
|
});
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
/** Archive (soft-delete) an existing JSONBlob, as identified by its unique token. */
|
|
138
|
-
async archiveAsync(token: string): Promise<JsonBlob> {
|
|
139
|
-
return this.runner().span('archive').fetchJson({
|
|
139
|
+
async archiveAsync(token: string, ctx?: CallContextLike): Promise<JsonBlob> {
|
|
140
|
+
return this.runner(ctx).span('archive').fetchJson({
|
|
140
141
|
url: 'xh/archiveJsonBlob',
|
|
141
142
|
params: {token}
|
|
142
143
|
});
|