@xh/hoist 86.1.0 → 86.3.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/.gitattributes +4 -0
- package/CHANGELOG.md +104 -0
- package/admin/AdminJsonDisplay.ts +31 -0
- package/admin/App.scss +3 -11
- package/admin/AppModel.ts +3 -4
- package/admin/jsonsearch/impl/JsonSearchImplModel.ts +4 -4
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +56 -6
- package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +9 -26
- package/admin/tabs/clients/ClientsModel.ts +4 -1
- package/admin/tabs/clients/activity/ClientDetailModel.ts +5 -1
- package/admin/tabs/cluster/instances/connpool/ConnPoolMonitorPanel.ts +3 -11
- package/admin/tabs/cluster/instances/services/DetailsPanel.ts +2 -13
- package/admin/tabs/cluster/objects/DetailPanel.ts +3 -12
- package/admin/tabs/userData/roles/RolePanel.ts +2 -2
- package/appcontainer/RouterModel.ts +6 -6
- package/build/types/admin/AdminJsonDisplay.d.ts +7 -0
- package/build/types/admin/AppModel.d.ts +3 -4
- package/build/types/admin/jsonsearch/impl/JsonSearchImplModel.d.ts +2 -2
- package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +14 -3
- package/build/types/admin/tabs/clients/ClientsModel.d.ts +3 -0
- package/build/types/appcontainer/RouterModel.d.ts +5 -5
- package/build/types/cmp/grid/Grid.d.ts +8 -4
- package/build/types/cmp/grid/GridModel.d.ts +13 -2
- package/build/types/cmp/grid/Types.d.ts +8 -1
- package/build/types/cmp/grid/filter/GridFilterFieldSpec.d.ts +24 -6
- package/build/types/cmp/input/SegmentedControlOption.d.ts +12 -0
- package/build/types/core/HoistAppModel.d.ts +2 -3
- package/build/types/core/types/Types.d.ts +12 -0
- package/build/types/desktop/cmp/button/grid/ExpandToLevelButton.d.ts +2 -1
- package/build/types/desktop/cmp/filter/FilterChooser.d.ts +5 -0
- package/build/types/desktop/cmp/filter/PopoverFilterChooser.d.ts +3 -1
- package/build/types/desktop/cmp/grid/impl/colchooser/ColChooserModel.d.ts +2 -2
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRowModel.d.ts +9 -1
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTabModel.d.ts +8 -2
- package/build/types/desktop/cmp/input/CodeInput.d.ts +7 -0
- package/build/types/desktop/cmp/input/NumberInput.d.ts +1 -1
- package/build/types/desktop/cmp/input/Select.d.ts +8 -0
- package/build/types/desktop/cmp/input/impl/CalcWindowedMenuWidth.d.ts +20 -0
- package/build/types/desktop/cmp/rest/impl/RestFormModel.d.ts +1 -1
- package/build/types/format/FormatNumber.d.ts +11 -3
- package/build/types/icon/Icon.d.ts +4 -3
- package/build/types/kit/react-dropzone/index.d.ts +1 -0
- package/build/types/kit/swiper/index.d.ts +4 -3
- package/build/types/mobile/cmp/button/grid/ExpandToLevelButton.d.ts +2 -1
- package/build/types/mobile/cmp/grid/impl/ColChooserModel.d.ts +2 -2
- package/build/types/mobile/cmp/input/NumberInput.d.ts +1 -1
- package/build/types/mobile/cmp/input/Select.d.ts +8 -0
- package/cmp/grid/Grid.ts +27 -2
- package/cmp/grid/GridModel.ts +21 -8
- package/cmp/grid/Types.ts +9 -1
- package/cmp/grid/filter/GridFilterFieldSpec.ts +28 -5
- package/cmp/grid/impl/MenuSupport.ts +3 -5
- package/cmp/input/SegmentedControlOption.ts +14 -0
- package/cmp/store/impl/StoreFilterFieldImplModel.ts +5 -6
- package/core/HoistAppModel.ts +2 -3
- package/core/types/Types.ts +14 -5
- package/desktop/cmp/button/grid/ExpandToLevelButton.ts +4 -5
- package/desktop/cmp/filechooser/FileChooser.ts +6 -3
- package/desktop/cmp/filechooser/FileChooserModel.ts +12 -5
- package/desktop/cmp/filter/FilterChooser.scss +41 -0
- package/desktop/cmp/filter/FilterChooser.ts +147 -7
- package/desktop/cmp/filter/PopoverFilterChooser.ts +7 -84
- package/desktop/cmp/grid/editors/NumberEditor.ts +16 -17
- package/desktop/cmp/grid/editors/SelectEditor.ts +15 -10
- package/desktop/cmp/grid/editors/impl/InlineEditorModel.ts +6 -1
- package/desktop/cmp/grid/find/impl/GridFindFieldImplModel.ts +3 -5
- package/desktop/cmp/grid/impl/colchooser/ColChooserModel.ts +2 -2
- package/desktop/cmp/grid/impl/filter/GridFilterDialog.ts +2 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRow.ts +12 -12
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRowModel.ts +18 -0
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTab.scss +5 -0
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTab.ts +13 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTabModel.ts +97 -14
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.ts +46 -7
- package/desktop/cmp/input/CheckboxButton.ts +21 -1
- package/desktop/cmp/input/CodeInput.ts +20 -3
- package/desktop/cmp/input/NumberInput.ts +1 -1
- package/desktop/cmp/input/SegmentedControl.ts +7 -3
- package/desktop/cmp/input/Select.ts +37 -18
- package/desktop/cmp/input/impl/CalcWindowedMenuWidth.ts +106 -0
- package/desktop/cmp/rest/impl/RestFormField.ts +1 -1
- package/desktop/cmp/tab/Tabs.scss +72 -25
- package/docs/routing.md +4 -2
- package/docs/upgrade-notes/v86-upgrade-notes.md +73 -8
- package/format/FormatNumber.ts +69 -32
- package/icon/Icon.scss +13 -0
- package/icon/Icon.ts +4 -3
- package/icon/impl/IconHtml.ts +1 -1
- package/kit/react-dropzone/index.ts +1 -0
- package/mcp/README.md +12 -0
- package/mcp/data/ts-registry.spec.ts +156 -0
- package/mcp/data/ts-registry.ts +14 -7
- package/mcp/formatters/typescript.ts +7 -3
- package/mcp/util/paths.ts +21 -0
- package/mobile/cmp/button/grid/ExpandToLevelButton.ts +4 -5
- package/mobile/cmp/grid/impl/ColChooserModel.ts +2 -2
- package/mobile/cmp/input/CheckboxButton.ts +18 -1
- package/mobile/cmp/input/NumberInput.ts +1 -1
- package/mobile/cmp/input/SegmentedControl.ts +7 -3
- package/mobile/cmp/input/Select.ts +21 -15
- package/mobx/README.md +2 -1
- package/package.json +10 -10
- package/styles/vars.scss +15 -0
- package/desktop/cmp/filter/PopoverFilterChooser.scss +0 -42
package/mcp/data/ts-registry.ts
CHANGED
|
@@ -24,7 +24,7 @@ import type {
|
|
|
24
24
|
import {resolve} from 'node:path';
|
|
25
25
|
|
|
26
26
|
import {log} from '../util/logger.js';
|
|
27
|
-
import {resolveRepoRoot} from '../util/paths.js';
|
|
27
|
+
import {resolveRepoRoot, resolveRepoRootPosix, toPosixPath} from '../util/paths.js';
|
|
28
28
|
import {computeFingerprint, loadCache, writeCache} from './index-cache.js';
|
|
29
29
|
|
|
30
30
|
//------------------------------------------------------------------
|
|
@@ -295,7 +295,9 @@ function buildSymbolIndex(proj: Project): {
|
|
|
295
295
|
} {
|
|
296
296
|
const index = new Map<string, SymbolEntry[]>();
|
|
297
297
|
const mIndex = new Map<string, MemberIndexEntry[]>();
|
|
298
|
-
|
|
298
|
+
// POSIX form: compared against ts-morph `getFilePath()`, which always uses
|
|
299
|
+
// forward slashes even on Windows.
|
|
300
|
+
const repoRoot = resolveRepoRootPosix();
|
|
299
301
|
|
|
300
302
|
const counts = {total: 0, exported: 0, byKind: {} as Record<string, number>};
|
|
301
303
|
let memberCount = 0;
|
|
@@ -549,7 +551,7 @@ function buildSymbolIndex(proj: Project): {
|
|
|
549
551
|
// Index Promise prototype extensions from promise/Promise.ts
|
|
550
552
|
const promiseFile = proj.getSourceFile(sf => sf.getFilePath().endsWith('/promise/Promise.ts'));
|
|
551
553
|
if (promiseFile) {
|
|
552
|
-
indexPromiseExtensions(promiseFile, index, mIndex,
|
|
554
|
+
indexPromiseExtensions(promiseFile, index, mIndex, resolveRepoRootPosix());
|
|
553
555
|
}
|
|
554
556
|
|
|
555
557
|
// Populate `memberNames` on symbol entries for every member-indexed owner
|
|
@@ -799,7 +801,8 @@ function ensureProject(): Project {
|
|
|
799
801
|
* JSDoc).
|
|
800
802
|
*/
|
|
801
803
|
function enrichMemberIndexFromImplements(proj: Project): void {
|
|
802
|
-
|
|
804
|
+
// POSIX form: compared against ts-morph `getFilePath()` (see buildSymbolIndex).
|
|
805
|
+
const repoRoot = resolveRepoRootPosix();
|
|
803
806
|
|
|
804
807
|
for (const sourceFile of proj.getSourceFiles()) {
|
|
805
808
|
const filePath = sourceFile.getFilePath();
|
|
@@ -1374,8 +1377,12 @@ function findIndexEntry(name: string, filePath?: string): SymbolEntry | null {
|
|
|
1374
1377
|
|
|
1375
1378
|
if (filePath) {
|
|
1376
1379
|
// Resolve relative paths against repo root -- search results display repo-relative
|
|
1377
|
-
// paths, so callers will typically pass those rather than absolute paths.
|
|
1378
|
-
|
|
1380
|
+
// paths, so callers will typically pass those rather than absolute paths. Normalize
|
|
1381
|
+
// to POSIX so the comparison matches entry.filePath (ts-morph forward slashes) on
|
|
1382
|
+
// Windows, and recognize Windows drive-letter absolutes (e.g. `D:/...`) as absolute.
|
|
1383
|
+
const posix = toPosixPath(filePath);
|
|
1384
|
+
const isAbsolute = posix.startsWith('/') || /^[a-zA-Z]:\//.test(posix);
|
|
1385
|
+
const resolved = isAbsolute ? posix : toPosixPath(resolve(resolveRepoRoot(), posix));
|
|
1379
1386
|
return exact.find(e => e.filePath === resolved) ?? null;
|
|
1380
1387
|
}
|
|
1381
1388
|
|
|
@@ -1432,7 +1439,7 @@ function extractSymbolDetail(entry: SymbolEntry): SymbolDetail | null {
|
|
|
1432
1439
|
*
|
|
1433
1440
|
* `JSDoc.getDescription()` returns the text before the first JSDoc block tag,
|
|
1434
1441
|
* but the TS parser treats ANY line whose first non-whitespace char is `@` as a
|
|
1435
|
-
* tag boundary - even inside a fenced
|
|
1442
|
+
* tag boundary - even inside a fenced code block. So a canonical-usage
|
|
1436
1443
|
* example containing e.g. `@observable.ref` silently truncates the description
|
|
1437
1444
|
* mid-example, dropping everything after it (further examples, "SEE ALSO"
|
|
1438
1445
|
* lists, trailing prose). See #4352.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import {z} from 'zod';
|
|
13
13
|
import type {MemberInfo, MemberIndexEntry, SymbolEntry, SymbolDetail} from '../data/ts-registry.js';
|
|
14
|
-
import {
|
|
14
|
+
import {resolveRepoRootPosix, toPosixPath} from '../util/paths.js';
|
|
15
15
|
|
|
16
16
|
/** Remove blank lines from a JSDoc string to produce more compact output. */
|
|
17
17
|
function collapseJsDoc(jsDoc: string): string {
|
|
@@ -31,8 +31,12 @@ export function truncateType(typeStr: string): string {
|
|
|
31
31
|
|
|
32
32
|
/** Convert an absolute file path to a repo-relative path. */
|
|
33
33
|
export function toRelativePath(filePath: string): string {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// Symbol filePaths originate from ts-morph (forward slashes on all platforms);
|
|
35
|
+
// compare in POSIX form so the repo-root prefix strips correctly on Windows,
|
|
36
|
+
// where `resolveRepoRoot()` would otherwise yield a backslash path.
|
|
37
|
+
const root = resolveRepoRootPosix();
|
|
38
|
+
const posix = toPosixPath(filePath);
|
|
39
|
+
return posix.startsWith(root) ? posix.slice(root.length + 1) : posix;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
package/mcp/util/paths.ts
CHANGED
|
@@ -39,6 +39,27 @@ export function resolveRepoRoot(): string {
|
|
|
39
39
|
return repoRoot;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** Convert a filesystem path to POSIX (forward-slash) form. */
|
|
43
|
+
export function toPosixPath(p: string): string {
|
|
44
|
+
return p.replace(/\\/g, '/');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Repo root in POSIX (forward-slash) form, matching ts-morph's `getFilePath()`
|
|
49
|
+
* convention, which always reports forward slashes on every platform.
|
|
50
|
+
*
|
|
51
|
+
* On Windows, {@link resolveRepoRoot} returns a backslash path (e.g.
|
|
52
|
+
* `D:\hoist-react`) while ts-morph reports `D:/hoist-react/...`; comparing or
|
|
53
|
+
* slicing the two directly fails, silently filtering out every source file and
|
|
54
|
+
* yielding an empty symbol index. Use this whenever comparing against or
|
|
55
|
+
* slicing a ts-morph source-file path (see `data/ts-registry.ts` and
|
|
56
|
+
* `formatters/typescript.ts`). Filesystem access that stays within Node's
|
|
57
|
+
* `path` APIs should keep using {@link resolveRepoRoot}.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveRepoRootPosix(): string {
|
|
60
|
+
return toPosixPath(resolveRepoRoot());
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
/**
|
|
43
64
|
* Resolve the `@xh/hoist` library version from the repo root `package.json`.
|
|
44
65
|
*
|
|
@@ -21,7 +21,8 @@ export interface ExpandToLevelButtonProps extends MenuButtonProps {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* A menu button to expand a multi-level grouped or tree grid out to a desired level.
|
|
24
|
-
* Requires {@link GridConfig.levelLabels} to be configured on the bound GridModel
|
|
24
|
+
* Requires {@link GridConfig.levelLabels} to be configured on the bound GridModel - the menu offers
|
|
25
|
+
* one entry per labelled level, so a partial array will limit the available expand-to targets.
|
|
25
26
|
*/
|
|
26
27
|
export const [ExpandToLevelButton, expandToLevelButton] =
|
|
27
28
|
hoistCmp.withFactory<ExpandToLevelButtonProps>({
|
|
@@ -51,13 +52,11 @@ export const [ExpandToLevelButton, expandToLevelButton] =
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// Render a disabled button if requested or if we have a flat grid, or no level labels
|
|
54
|
-
const {maxDepth,
|
|
55
|
+
const {maxDepth, resolvedLevelLabels} = gridModel;
|
|
55
56
|
if (disabled || !maxDepth || !resolvedLevelLabels) return disabledButton();
|
|
56
57
|
|
|
57
58
|
const menuItems: MenuItemLike[] = resolvedLevelLabels.map((label, idx) => {
|
|
58
|
-
const isCurrLevel =
|
|
59
|
-
expandLevel === idx ||
|
|
60
|
-
(expandLevel > maxDepth && idx === resolvedLevelLabels.length - 1);
|
|
59
|
+
const isCurrLevel = gridModel.isCurrentExpandLevel(idx);
|
|
61
60
|
|
|
62
61
|
return {
|
|
63
62
|
icon: isCurrLevel ? Icon.check() : Icon.placeholder(),
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
7
|
+
import {GridModel, IColChooserModel} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {HoistModel, HSide, XH} from '@xh/hoist/core';
|
|
9
9
|
import '@xh/hoist/mobile/register';
|
|
10
10
|
import {bindable, action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
@@ -17,7 +17,7 @@ import {clone, find, sortBy} from 'lodash';
|
|
|
17
17
|
* It is not necessary to manually create instances of this class within an application.
|
|
18
18
|
* @internal
|
|
19
19
|
*/
|
|
20
|
-
export class ColChooserModel extends HoistModel {
|
|
20
|
+
export class ColChooserModel extends HoistModel implements IColChooserModel {
|
|
21
21
|
override xhImpl = true;
|
|
22
22
|
|
|
23
23
|
gridModel: GridModel;
|
|
@@ -44,7 +44,24 @@ class CheckboxButtonInputModel extends HoistInputModel {
|
|
|
44
44
|
// Implementation
|
|
45
45
|
//----------------------------------
|
|
46
46
|
const cmp = hoistCmp.factory<CheckboxButtonInputModel>(
|
|
47
|
-
(
|
|
47
|
+
(
|
|
48
|
+
{
|
|
49
|
+
// HoistInput props - exclude from passthrough to Onsen
|
|
50
|
+
bind,
|
|
51
|
+
value,
|
|
52
|
+
commitOnChange,
|
|
53
|
+
onChange,
|
|
54
|
+
onCommit,
|
|
55
|
+
// Consumed by this component
|
|
56
|
+
model,
|
|
57
|
+
text,
|
|
58
|
+
checkedIcon,
|
|
59
|
+
uncheckedIcon,
|
|
60
|
+
// Remainder passed to hoist button & Onsen button
|
|
61
|
+
...props
|
|
62
|
+
},
|
|
63
|
+
ref
|
|
64
|
+
) => {
|
|
48
65
|
const checked = !!model.renderValue;
|
|
49
66
|
return button({
|
|
50
67
|
text: withDefault(text, model.getField()?.displayName),
|
|
@@ -46,7 +46,7 @@ export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProp
|
|
|
46
46
|
/** Text to display when control is empty. */
|
|
47
47
|
placeholder?: string;
|
|
48
48
|
|
|
49
|
-
/** Max decimal precision of the value, defaults to 4. */
|
|
49
|
+
/** Max decimal precision of the value, defaults to 4. Set to null for full, unrestricted precision. */
|
|
50
50
|
precision?: NumericPrecision;
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -85,6 +85,7 @@ export const [SegmentedControl, segmentedControl] = hoistCmp.withFactory<Segment
|
|
|
85
85
|
interface NormalizedOption extends SegmentedControlOption {
|
|
86
86
|
label: string;
|
|
87
87
|
intent?: Intent;
|
|
88
|
+
testId?: string;
|
|
88
89
|
_key: string;
|
|
89
90
|
}
|
|
90
91
|
|
|
@@ -97,13 +98,14 @@ class SegmentedControlModel extends HoistInputModel {
|
|
|
97
98
|
return options.map((o: any, idx: number) => {
|
|
98
99
|
const key = String(idx);
|
|
99
100
|
if (isObject(o)) {
|
|
100
|
-
const {label, value, icon, disabled, intent} = o as SegmentedControlOption;
|
|
101
|
+
const {label, value, icon, disabled, intent, testId} = o as SegmentedControlOption;
|
|
101
102
|
return {
|
|
102
103
|
value: this.toInternal(value),
|
|
103
104
|
label: label ?? (icon ? '' : String(value)),
|
|
104
105
|
icon,
|
|
105
106
|
disabled,
|
|
106
107
|
intent,
|
|
108
|
+
testId,
|
|
107
109
|
_key: key
|
|
108
110
|
};
|
|
109
111
|
} else {
|
|
@@ -168,7 +170,8 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
|
|
|
168
170
|
|
|
169
171
|
const buttons = model.normalizedOptions.map(opt => {
|
|
170
172
|
const optIntent = opt.intent ?? defaultIntent,
|
|
171
|
-
selected = opt._key === selectedKey
|
|
173
|
+
selected = opt._key === selectedKey,
|
|
174
|
+
optTestId = opt.testId ?? (testId ? `${testId}-${String(opt.value)}` : null);
|
|
172
175
|
// Wrap the label so it can truncate with an ellipsis when the segment is too narrow,
|
|
173
176
|
// rather than hard-clipping mid-character. Pass null for icon-only options so the Button
|
|
174
177
|
// renders the icon alone (an empty span would suppress that).
|
|
@@ -187,7 +190,8 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
|
|
|
187
190
|
selected && 'xh-segmented-control-option--selected',
|
|
188
191
|
optIntent && `xh-segmented-control-option--${optIntent}`
|
|
189
192
|
),
|
|
190
|
-
onClick: () => model.onValueChange(opt._key)
|
|
193
|
+
onClick: () => model.onValueChange(opt._key),
|
|
194
|
+
testId: optTestId
|
|
191
195
|
});
|
|
192
196
|
});
|
|
193
197
|
|
|
@@ -21,7 +21,7 @@ import {action, bindable, makeObservable, observable, override} from '@xh/hoist/
|
|
|
21
21
|
import {debouncePromise, wait} from '@xh/hoist/promise';
|
|
22
22
|
import {throwIf, withDefault, mergeDeep} from '@xh/hoist/utils/js';
|
|
23
23
|
import {createObservableRef, getLayoutProps} from '@xh/hoist/utils/react';
|
|
24
|
-
import {escapeRegExp, isEqual, isNil, isPlainObject,
|
|
24
|
+
import {escapeRegExp, isEqual, isNil, isPlainObject, unionWith} from 'lodash';
|
|
25
25
|
import {Children, ReactNode, ReactPortal} from 'react';
|
|
26
26
|
import ReactDom from 'react-dom';
|
|
27
27
|
import './Select.scss';
|
|
@@ -157,6 +157,15 @@ export interface SelectProps extends HoistProps, HoistInputProps, LayoutProps {
|
|
|
157
157
|
|
|
158
158
|
/** Field on provided options for sourcing each option's value (default `value`). */
|
|
159
159
|
valueField?: string;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Function to generate a `SelectOption` for a (non-null) selected value not present in the
|
|
163
|
+
* current options list. Return null to fall back to the default value-as-label behavior.
|
|
164
|
+
*
|
|
165
|
+
* Useful with queryFn-based selects, readonly forms, or any case where options may not be
|
|
166
|
+
* loaded when a value is set, ensuring the value renders with its proper label.
|
|
167
|
+
*/
|
|
168
|
+
generateOptionFn?: (value: any) => SelectOption;
|
|
160
169
|
}
|
|
161
170
|
|
|
162
171
|
/**
|
|
@@ -377,7 +386,8 @@ class SelectInputModel extends HoistInputModel {
|
|
|
377
386
|
|
|
378
387
|
// Convert external value into option object(s). Options created if missing - this takes the
|
|
379
388
|
// external value from the model, and we will respect that even if we don't know about it.
|
|
380
|
-
// (Exception for a null value, which
|
|
389
|
+
// (Exception for a null value, which is never synthesized - accepted only if provided via
|
|
390
|
+
// options.)
|
|
381
391
|
override toInternal(external) {
|
|
382
392
|
return this.findOption(external, !isNil(external));
|
|
383
393
|
}
|
|
@@ -393,7 +403,10 @@ class SelectInputModel extends HoistInputModel {
|
|
|
393
403
|
}
|
|
394
404
|
}
|
|
395
405
|
|
|
396
|
-
|
|
406
|
+
if (!createIfNotFound) return null;
|
|
407
|
+
|
|
408
|
+
// Value not among options - let the app generate an option for it, else synthesize one.
|
|
409
|
+
return this.componentProps.generateOptionFn?.(value) ?? this.valueToOption(value);
|
|
397
410
|
}
|
|
398
411
|
|
|
399
412
|
override toExternal(internal) {
|
|
@@ -453,15 +466,9 @@ class SelectInputModel extends HoistInputModel {
|
|
|
453
466
|
|
|
454
467
|
// Carry forward and add to any existing internalOpts to allow our value
|
|
455
468
|
// converters to continue all selected values in multiMode.
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
this.internalOptions.forEach(currOpt => {
|
|
460
|
-
const matchOpt = matchesByVal[currOpt.value];
|
|
461
|
-
if (!matchOpt) newOpts.push(currOpt); // avoiding dupes
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
this.internalOptions = newOpts;
|
|
469
|
+
this.internalOptions = unionWith(matchOpts, this.internalOptions, (a, b) =>
|
|
470
|
+
isEqual(a.value, b.value)
|
|
471
|
+
);
|
|
465
472
|
|
|
466
473
|
// But only return the matching options back to the combo.
|
|
467
474
|
return matchOpts;
|
|
@@ -484,8 +491,7 @@ class SelectInputModel extends HoistInputModel {
|
|
|
484
491
|
// Option Rendering
|
|
485
492
|
//----------------------
|
|
486
493
|
formatOptionLabel = (opt, params) => {
|
|
487
|
-
//
|
|
488
|
-
// If we need to expose customization here, we could consider a dedicated prop.
|
|
494
|
+
// Display the standard label string in the value container (context == 'value').
|
|
489
495
|
if (params.context !== 'menu') {
|
|
490
496
|
return opt.label;
|
|
491
497
|
}
|
|
@@ -501,7 +507,7 @@ class SelectInputModel extends HoistInputModel {
|
|
|
501
507
|
return div(opt.label);
|
|
502
508
|
}
|
|
503
509
|
|
|
504
|
-
return this.externalValue
|
|
510
|
+
return isEqual(this.externalValue, opt.value)
|
|
505
511
|
? hbox({
|
|
506
512
|
items: [
|
|
507
513
|
div({
|
package/mobx/README.md
CHANGED
|
@@ -45,7 +45,8 @@ The following are re-exported from MobX and mobx-react-lite:
|
|
|
45
45
|
|
|
46
46
|
The package configures MobX with `enforceActions: 'observed'`, meaning any modification to
|
|
47
47
|
an observable property that is currently being observed must occur inside an `@action` method,
|
|
48
|
-
`runInAction()` block,
|
|
48
|
+
`runInAction()` block, `@bindable` setter, or a `reaction`/`when` run callback (MobX
|
|
49
|
+
auto-wraps these in an action). MobX logs a warning if this rule is violated.
|
|
49
50
|
|
|
50
51
|
This enforcement prevents accidental state mutations and makes data flow predictable.
|
|
51
52
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "86.
|
|
3
|
+
"version": "86.3.0",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,15 +38,15 @@
|
|
|
38
38
|
]
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@auth0/auth0-spa-js": "~2.
|
|
42
|
-
"@azure/msal-browser": "~5.
|
|
41
|
+
"@auth0/auth0-spa-js": "~2.23.0",
|
|
42
|
+
"@azure/msal-browser": "~5.16.0",
|
|
43
43
|
"@blueprintjs/core": "^6.3.2",
|
|
44
44
|
"@blueprintjs/datetime": "^6.0.6",
|
|
45
|
-
"@codemirror/commands": "
|
|
46
|
-
"@codemirror/language": "
|
|
47
|
-
"@codemirror/language-data": "
|
|
48
|
-
"@codemirror/lint": "
|
|
49
|
-
"@codemirror/state": "
|
|
45
|
+
"@codemirror/commands": "^6.10.3",
|
|
46
|
+
"@codemirror/language": "^6.12.3",
|
|
47
|
+
"@codemirror/language-data": "^6.5.2",
|
|
48
|
+
"@codemirror/lint": "^6.9.6",
|
|
49
|
+
"@codemirror/state": "^6.7.0",
|
|
50
50
|
"@codemirror/view": "^6.43.0",
|
|
51
51
|
"@fortawesome/fontawesome-pro": "^7.2.0",
|
|
52
52
|
"@fortawesome/fontawesome-svg-core": "^7.2.0",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"semver": "~7.8.0",
|
|
91
91
|
"short-unique-id": "~5.3.2",
|
|
92
92
|
"store2": "~2.14.3",
|
|
93
|
-
"swiper": "^
|
|
93
|
+
"swiper": "^14.0.0",
|
|
94
94
|
"ts-morph": "^27.0.2",
|
|
95
95
|
"tsx": "^4.21.0",
|
|
96
96
|
"ua-parser-js": "~2.0.4",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"react-dom": "~18.2.0",
|
|
119
119
|
"stylelint": "17.x",
|
|
120
120
|
"stylelint-config-standard-scss": "17.x",
|
|
121
|
-
"type-fest": "
|
|
121
|
+
"type-fest": "5.x",
|
|
122
122
|
"typescript": "~5.9.3"
|
|
123
123
|
},
|
|
124
124
|
"resolutions": {
|
package/styles/vars.scss
CHANGED
|
@@ -844,6 +844,21 @@ body {
|
|
|
844
844
|
--xh-tab-font-size: var(--tab-font-size, var(--xh-font-size));
|
|
845
845
|
--xh-tab-font-size-px: calc(var(--xh-tab-font-size) * 1px);
|
|
846
846
|
|
|
847
|
+
// Vertical (left/right) TabSwitcher - the styled "rail" of rounded, hoverable items with a
|
|
848
|
+
// filled active "pill". All overridable via the unprefixed hooks.
|
|
849
|
+
--xh-tab-switcher-vertical-min-width: var(--tab-switcher-vertical-min-width, auto);
|
|
850
|
+
--xh-tab-switcher-vertical-padding: var(--tab-switcher-vertical-padding, var(--xh-pad-half-px));
|
|
851
|
+
--xh-tab-switcher-vertical-border: var(--tab-switcher-vertical-border, var(--xh-border-solid));
|
|
852
|
+
--xh-tab-switcher-vertical-item-gap: var(--tab-switcher-vertical-item-gap, 2px);
|
|
853
|
+
--xh-tab-switcher-vertical-item-padding: var(--tab-switcher-vertical-item-padding, var(--xh-pad-half-px) var(--xh-pad-px));
|
|
854
|
+
--xh-tab-switcher-vertical-item-border-radius: var(--tab-switcher-vertical-item-border-radius, var(--xh-border-radius-px));
|
|
855
|
+
--xh-tab-switcher-vertical-item-text-color: var(--tab-switcher-vertical-item-text-color, var(--xh-text-color-muted));
|
|
856
|
+
--xh-tab-switcher-vertical-item-active-text-color: var(--tab-switcher-vertical-item-active-text-color, var(--xh-tab-active-text-color));
|
|
857
|
+
--xh-tab-switcher-vertical-item-hover-bg: var(--tab-switcher-vertical-item-hover-bg, var(--xh-bg-alt));
|
|
858
|
+
--xh-tab-switcher-vertical-item-active-bg: var(--tab-switcher-vertical-item-active-bg, var(--xh-bg-highlight));
|
|
859
|
+
--xh-tab-switcher-vertical-item-font-weight: var(--tab-switcher-vertical-item-font-weight, 500);
|
|
860
|
+
--xh-tab-switcher-vertical-item-active-font-weight: var(--tab-switcher-vertical-item-active-font-weight, 600);
|
|
861
|
+
|
|
847
862
|
&.xh-dark {
|
|
848
863
|
--xh-tab-active-text-color: var(--tab-active-text-color, var(--xh-intent-primary-lighter));
|
|
849
864
|
}
|
|
@@ -1,42 +0,0 @@
|
|
|
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
|
-
.xh-popover-filter-chooser {
|
|
8
|
-
& > .bp6-popover-target {
|
|
9
|
-
display: flex;
|
|
10
|
-
flex: 1;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Extra class names required to override the default styles of the popover
|
|
14
|
-
&__popover.bp6-popover.bp6-minimal {
|
|
15
|
-
margin-top: -15px !important;
|
|
16
|
-
box-shadow: none;
|
|
17
|
-
|
|
18
|
-
.bp6-popover-content {
|
|
19
|
-
background: transparent;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.xh-select__value-container--is-multi {
|
|
23
|
-
height: unset;
|
|
24
|
-
line-height: unset;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
&__filter-chooser {
|
|
29
|
-
.xh-select__value-container--is-multi {
|
|
30
|
-
overflow-y: hidden !important;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.xh-select {
|
|
34
|
-
.xh-select__control--is-disabled {
|
|
35
|
-
background: var(--xh-input-bg);
|
|
36
|
-
}
|
|
37
|
-
.xh-select__multi-value--is-disabled .xh-select__multi-value__label {
|
|
38
|
-
color: unset;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|