@xh/hoist 87.1.1 → 87.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/.npmignore +39 -0
- package/CHANGELOG.md +108 -20
- package/admin/AdminUtils.ts +64 -0
- package/admin/App.scss +88 -0
- package/admin/columns/UserData.ts +30 -3
- package/admin/detail/RestDetailModel.ts +64 -0
- package/admin/detail/RestDetailPanel.ts +106 -0
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +27 -94
- package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +23 -60
- package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +1 -2
- package/admin/tabs/general/alertBanner/AlertBannerPanel.ts +2 -13
- package/admin/tabs/general/config/ConfigDetailPanel.ts +72 -0
- package/admin/tabs/general/config/ConfigPanel.ts +41 -53
- package/admin/tabs/general/config/ConfigPanelModel.ts +3 -0
- package/admin/tabs/general/config/ConfigValue.scss +14 -0
- package/admin/tabs/general/config/ConfigValue.ts +104 -65
- package/admin/tabs/userData/jsonblob/JsonBlobDetailPanel.ts +93 -0
- package/admin/tabs/userData/jsonblob/JsonBlobModel.ts +4 -0
- package/admin/tabs/userData/jsonblob/JsonBlobPanel.ts +2 -0
- package/admin/tabs/userData/prefs/UserPreferenceDetailPanel.ts +62 -0
- package/admin/tabs/userData/prefs/UserPreferenceModel.ts +10 -3
- package/admin/tabs/userData/prefs/UserPreferencePanel.ts +3 -1
- package/admin/tabs/userData/roles/details/RoleDetails.scss +2 -19
- package/admin/tabs/userData/roles/details/RoleDetails.ts +13 -13
- package/appcontainer/AboutDialogModel.ts +1 -0
- package/appcontainer/AppContainerModel.ts +3 -0
- package/appcontainer/AppStateModel.ts +1 -0
- package/appcontainer/BannerSourceModel.ts +1 -0
- package/appcontainer/ChangelogDialogModel.ts +1 -0
- package/appcontainer/ColChooserOptionsModel.ts +1 -0
- package/appcontainer/ExceptionDialogModel.ts +1 -0
- package/appcontainer/FeedbackDialogModel.ts +1 -0
- package/appcontainer/ImpersonationBarModel.ts +1 -0
- package/appcontainer/MessageSourceModel.ts +1 -0
- package/appcontainer/OptionsDialogModel.ts +1 -0
- package/appcontainer/PageStateModel.ts +1 -0
- package/appcontainer/RouterModel.ts +2 -0
- package/appcontainer/SizingModeModel.ts +1 -0
- package/appcontainer/ThemeModel.ts +1 -0
- package/appcontainer/ToastSourceModel.ts +1 -0
- package/appcontainer/UserAgentModel.ts +1 -0
- package/appcontainer/ViewportSizeModel.ts +1 -0
- package/build/types/admin/AdminUtils.d.ts +29 -0
- package/build/types/admin/columns/UserData.d.ts +3 -0
- package/build/types/admin/detail/RestDetailModel.d.ts +20 -0
- package/build/types/admin/detail/RestDetailPanel.d.ts +34 -0
- package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +5 -17
- package/build/types/admin/tabs/general/config/ConfigDetailPanel.d.ts +7 -0
- package/build/types/admin/tabs/general/config/ConfigValue.d.ts +10 -4
- package/build/types/admin/tabs/userData/jsonblob/JsonBlobDetailPanel.d.ts +3 -0
- package/build/types/admin/tabs/userData/prefs/UserPreferenceDetailPanel.d.ts +3 -0
- package/build/types/admin/tabs/userData/prefs/UserPreferenceModel.d.ts +3 -0
- package/build/types/appcontainer/AboutDialogModel.d.ts +1 -0
- package/build/types/appcontainer/AppContainerModel.d.ts +1 -0
- package/build/types/appcontainer/AppStateModel.d.ts +1 -0
- package/build/types/appcontainer/BannerSourceModel.d.ts +1 -0
- package/build/types/appcontainer/ChangelogDialogModel.d.ts +1 -0
- package/build/types/appcontainer/ColChooserOptionsModel.d.ts +1 -0
- package/build/types/appcontainer/ExceptionDialogModel.d.ts +1 -0
- package/build/types/appcontainer/FeedbackDialogModel.d.ts +1 -0
- package/build/types/appcontainer/ImpersonationBarModel.d.ts +1 -0
- package/build/types/appcontainer/MessageSourceModel.d.ts +1 -0
- package/build/types/appcontainer/OptionsDialogModel.d.ts +1 -0
- package/build/types/appcontainer/PageStateModel.d.ts +1 -0
- package/build/types/appcontainer/RouterModel.d.ts +1 -0
- package/build/types/appcontainer/SizingModeModel.d.ts +1 -0
- package/build/types/appcontainer/ThemeModel.d.ts +1 -0
- package/build/types/appcontainer/ToastSourceModel.d.ts +1 -0
- package/build/types/appcontainer/UserAgentModel.d.ts +1 -0
- package/build/types/appcontainer/ViewportSizeModel.d.ts +1 -0
- package/build/types/cmp/ag-grid/AgGridModel.d.ts +3 -1
- package/build/types/cmp/card/Card.d.ts +2 -2
- package/build/types/cmp/chart/ChartModel.d.ts +2 -0
- package/build/types/cmp/dataview/DataViewModel.d.ts +2 -0
- package/build/types/cmp/daterange/DateRangePickerModel.d.ts +266 -0
- package/build/types/cmp/daterange/DateRangePresets.d.ts +26 -0
- package/build/types/cmp/daterange/DateRangeUtils.d.ts +96 -0
- package/build/types/cmp/daterange/Types.d.ts +152 -0
- package/build/types/cmp/daterange/index.d.ts +4 -0
- package/build/types/cmp/filter/FilterChooserModel.d.ts +3 -1
- package/build/types/cmp/form/FormModel.d.ts +3 -1
- package/build/types/cmp/grid/GridModel.d.ts +2 -0
- package/build/types/cmp/grid/Types.d.ts +2 -0
- package/build/types/cmp/grid/filter/GridFilterModel.d.ts +1 -1
- package/build/types/cmp/grid/impl/ColumnWidthCalculator.d.ts +1 -1
- package/build/types/cmp/grouping/GroupingChooserModel.d.ts +3 -1
- package/build/types/cmp/input/HoistInputProps.d.ts +49 -2
- package/build/types/cmp/tab/TabContainerModel.d.ts +3 -1
- package/build/types/cmp/tab/TabModel.d.ts +3 -1
- package/build/types/cmp/treemap/TreeMapModel.d.ts +2 -0
- package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +2 -0
- package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +2 -0
- package/build/types/core/HoistBase.d.ts +7 -0
- package/build/types/core/HoistProps.d.ts +26 -1
- package/build/types/core/model/RootRefreshContextModel.d.ts +1 -0
- package/build/types/core/types/Telemetry.d.ts +1 -1
- package/build/types/data/Store.d.ts +3 -1
- package/build/types/data/StoreSelectionModel.d.ts +3 -1
- package/build/types/data/cube/Cube.d.ts +5 -2
- package/build/types/data/cube/View.d.ts +2 -0
- package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +3 -0
- package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +3 -0
- package/build/types/desktop/cmp/button/Button.d.ts +2 -2
- package/build/types/desktop/cmp/button/ButtonGroup.d.ts +2 -2
- package/build/types/desktop/cmp/dash/DashConfig.d.ts +2 -0
- package/build/types/desktop/cmp/dash/DashViewModel.d.ts +3 -1
- package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +1 -1
- package/build/types/desktop/cmp/dash/container/DashContainerModel.d.ts +1 -1
- package/build/types/desktop/cmp/daterange/DateRangePicker.d.ts +66 -0
- package/build/types/desktop/cmp/daterange/impl/CustomTab.d.ts +3 -0
- package/build/types/desktop/cmp/daterange/impl/DateRangePickerLocalModel.d.ts +121 -0
- package/build/types/desktop/cmp/daterange/impl/PeriodTab.d.ts +2 -0
- package/build/types/desktop/cmp/daterange/impl/PresetsTab.d.ts +3 -0
- package/build/types/desktop/cmp/daterange/impl/RelativeTab.d.ts +3 -0
- package/build/types/desktop/cmp/daterange/impl/TabUtils.d.ts +21 -0
- package/build/types/desktop/cmp/daterange/index.d.ts +2 -0
- package/build/types/desktop/cmp/dock/DockContainerModel.d.ts +3 -1
- package/build/types/desktop/cmp/dock/DockViewModel.d.ts +3 -1
- package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +4 -1
- package/build/types/desktop/cmp/input/IntentInput.d.ts +38 -0
- package/build/types/desktop/cmp/input/NumberInput.d.ts +2 -2
- package/build/types/desktop/cmp/input/SegmentedControl.d.ts +25 -4
- package/build/types/desktop/cmp/input/TextArea.d.ts +2 -2
- package/build/types/desktop/cmp/input/TextInput.d.ts +2 -2
- package/build/types/desktop/cmp/input/index.d.ts +1 -0
- package/build/types/desktop/cmp/leftrightchooser/LeftRightChooserModel.d.ts +3 -1
- package/build/types/desktop/cmp/panel/PanelModel.d.ts +3 -1
- package/build/types/desktop/cmp/rest/RestGridModel.d.ts +1 -1
- package/build/types/desktop/cmp/tab/impl/TabContainer.d.ts +1 -1
- package/build/types/desktop/hooks/UseHotkeys.d.ts +4 -3
- package/build/types/icon/Icon.d.ts +3 -0
- package/build/types/inspector/impl/InspectorUtils.d.ts +3 -0
- package/build/types/inspector/instances/InstancesModel.d.ts +4 -0
- package/build/types/kit/react-day-picker/index.d.ts +3 -3
- package/build/types/mobile/cmp/button/Button.d.ts +2 -2
- package/build/types/mobile/cmp/input/NumberInput.d.ts +2 -2
- package/build/types/mobile/cmp/input/SegmentedControl.d.ts +18 -4
- package/build/types/mobile/cmp/input/TextArea.d.ts +2 -2
- package/build/types/mobile/cmp/input/TextInput.d.ts +2 -2
- package/build/types/mobile/cmp/navigator/NavigatorModel.d.ts +8 -2
- package/build/types/mobile/cmp/navigator/PageModel.d.ts +3 -1
- package/build/types/mobile/cmp/tab/impl/TabContainer.d.ts +1 -1
- package/build/types/svc/InspectorService.d.ts +1 -0
- package/build/types/utils/js/LangUtils.d.ts +24 -5
- package/cmp/README.md +1 -0
- package/cmp/ag-grid/AgGridModel.ts +5 -0
- package/cmp/badge/Badge.ts +5 -3
- package/cmp/card/Card.ts +4 -3
- package/cmp/chart/ChartModel.ts +11 -1
- package/cmp/clock/Clock.ts +1 -1
- package/cmp/dataview/DataViewModel.ts +7 -1
- package/cmp/daterange/DateRangePickerModel.ts +628 -0
- package/cmp/daterange/DateRangePresets.ts +274 -0
- package/cmp/daterange/DateRangeUtils.ts +557 -0
- package/cmp/daterange/README.md +434 -0
- package/cmp/daterange/Types.ts +203 -0
- package/cmp/daterange/index.ts +10 -0
- package/cmp/filter/FilterChooserModel.ts +7 -4
- package/cmp/form/FormModel.ts +6 -0
- package/cmp/form/README.md +1 -0
- package/cmp/grid/GridModel.ts +24 -4
- package/cmp/grid/Types.ts +3 -0
- package/cmp/grid/filter/GridFilterModel.ts +3 -1
- package/cmp/grid/impl/ColumnWidthCalculator.ts +5 -10
- package/cmp/grouping/GroupingChooserModel.ts +6 -1
- package/cmp/input/HoistInputProps.ts +50 -2
- package/cmp/input/README.md +42 -0
- package/cmp/layout/Box.ts +3 -2
- package/cmp/tab/TabContainerModel.ts +9 -1
- package/cmp/tab/TabModel.ts +5 -0
- package/cmp/treemap/SplitTreeMapModel.ts +12 -2
- package/cmp/treemap/TreeMapModel.ts +6 -1
- package/cmp/viewmanager/ViewManagerModel.ts +6 -1
- package/cmp/zoneGrid/ZoneGridModel.ts +7 -0
- package/core/HoistBase.ts +11 -0
- package/core/HoistProps.ts +27 -0
- package/core/README.md +46 -0
- package/core/impl/InstallServices.ts +5 -1
- package/core/model/RootRefreshContextModel.ts +2 -0
- package/core/types/Telemetry.ts +1 -1
- package/data/README.md +3 -1
- package/data/Store.ts +7 -0
- package/data/StoreSelectionModel.ts +5 -1
- package/data/cube/Cube.ts +12 -3
- package/data/cube/View.ts +5 -1
- package/desktop/README.md +2 -0
- package/desktop/appcontainer/LoginPanel.ts +2 -0
- package/desktop/cmp/button/Button.scss +71 -42
- package/desktop/cmp/button/Button.ts +10 -1
- package/desktop/cmp/button/ButtonGroup.ts +4 -1
- package/desktop/cmp/button/grid/ColChooserButton.ts +9 -1
- package/desktop/cmp/button/zoneGrid/ZoneMapperButton.ts +1 -0
- package/desktop/cmp/dash/DashConfig.ts +3 -0
- package/desktop/cmp/dash/DashViewModel.ts +14 -1
- package/desktop/cmp/dash/canvas/DashCanvasModel.ts +3 -1
- package/desktop/cmp/dash/container/DashContainerModel.ts +3 -1
- package/desktop/cmp/daterange/DateRangePicker.scss +577 -0
- package/desktop/cmp/daterange/DateRangePicker.ts +357 -0
- package/desktop/cmp/daterange/impl/CustomTab.ts +165 -0
- package/desktop/cmp/daterange/impl/DateRangePickerLocalModel.ts +482 -0
- package/desktop/cmp/daterange/impl/PeriodTab.ts +138 -0
- package/desktop/cmp/daterange/impl/PresetsTab.ts +58 -0
- package/desktop/cmp/daterange/impl/RelativeTab.ts +108 -0
- package/desktop/cmp/daterange/impl/TabUtils.ts +34 -0
- package/desktop/cmp/daterange/index.ts +8 -0
- package/desktop/cmp/dock/DockContainerModel.ts +6 -1
- package/desktop/cmp/dock/DockViewModel.ts +6 -1
- package/desktop/cmp/filechooser/FileChooser.ts +4 -0
- package/desktop/cmp/form/FormField.ts +1 -0
- package/desktop/cmp/grid/editors/DateEditor.ts +11 -2
- package/desktop/cmp/grouping/GroupingChooser.ts +8 -2
- package/desktop/cmp/input/Checkbox.ts +1 -0
- package/desktop/cmp/input/CodeInput.ts +1 -0
- package/desktop/cmp/input/DateInput.scss +1 -1
- package/desktop/cmp/input/DateInput.ts +1 -0
- package/desktop/cmp/input/IntentInput.scss +202 -0
- package/desktop/cmp/input/IntentInput.ts +213 -0
- package/desktop/cmp/input/NumberInput.ts +11 -2
- package/desktop/cmp/input/Picker.ts +1 -0
- package/desktop/cmp/input/RadioInput.ts +2 -1
- package/desktop/cmp/input/SegmentedControl.scss +57 -5
- package/desktop/cmp/input/SegmentedControl.ts +66 -11
- package/desktop/cmp/input/Select.ts +9 -7
- package/desktop/cmp/input/Slider.ts +2 -0
- package/desktop/cmp/input/SwitchInput.ts +1 -0
- package/desktop/cmp/input/TextArea.ts +11 -2
- package/desktop/cmp/input/TextInput.ts +11 -2
- package/desktop/cmp/input/index.ts +1 -0
- package/desktop/cmp/leftrightchooser/LeftRightChooserModel.ts +5 -0
- package/desktop/cmp/panel/Panel.ts +8 -5
- package/desktop/cmp/panel/PanelModel.ts +8 -0
- package/desktop/cmp/panel/impl/ResizeContainer.ts +2 -1
- package/desktop/cmp/rest/RestGridModel.ts +4 -0
- package/desktop/cmp/tab/impl/TabContainer.ts +2 -0
- package/desktop/cmp/toolbar/Toolbar.scss +20 -6
- package/desktop/hooks/UseHotkeys.ts +41 -12
- package/docs/README.md +2 -0
- package/docs/build-and-publish.md +23 -7
- package/docs/doc-registry.json +8 -0
- package/docs/version-compatibility.md +2 -0
- package/icon/Icon.ts +9 -0
- package/icon/index.ts +24 -0
- package/inspector/README.md +8 -2
- package/inspector/impl/InspectorUtils.ts +12 -0
- package/inspector/instances/DiagnosticsModel.ts +3 -2
- package/inspector/instances/InstancesModel.ts +81 -14
- package/inspector/instances/InstancesPanel.ts +6 -0
- package/kit/react-day-picker/index.ts +3 -3
- package/mobile/appcontainer/LoginPanel.ts +2 -0
- package/mobile/cmp/button/Button.ts +16 -3
- package/mobile/cmp/form/FormField.ts +1 -0
- package/mobile/cmp/input/Checkbox.ts +1 -0
- package/mobile/cmp/input/DateInput.ts +1 -0
- package/mobile/cmp/input/Label.ts +13 -8
- package/mobile/cmp/input/NumberInput.ts +11 -2
- package/mobile/cmp/input/SearchInput.ts +1 -0
- package/mobile/cmp/input/SegmentedControl.scss +36 -6
- package/mobile/cmp/input/SegmentedControl.ts +37 -11
- package/mobile/cmp/input/Select.ts +2 -0
- package/mobile/cmp/input/SwitchInput.ts +1 -0
- package/mobile/cmp/input/TextArea.ts +11 -2
- package/mobile/cmp/input/TextInput.ts +11 -2
- package/mobile/cmp/navigator/NavigatorModel.ts +20 -8
- package/mobile/cmp/navigator/PageModel.ts +6 -1
- package/mobile/cmp/tab/impl/TabContainer.ts +9 -1
- package/package.json +19 -19
- package/styles/vars.scss +32 -2
- package/svc/InspectorService.ts +2 -0
- package/svc/TraceService.ts +5 -2
- package/utils/js/LangUtils.ts +37 -5
|
@@ -0,0 +1,213 @@
|
|
|
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 {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
|
|
8
|
+
import {div} from '@xh/hoist/cmp/layout';
|
|
9
|
+
import {elementFactory, hoistCmp, HoistProps, Intent} from '@xh/hoist/core';
|
|
10
|
+
import '@xh/hoist/desktop/register';
|
|
11
|
+
import {Icon} from '@xh/hoist/icon';
|
|
12
|
+
import {computed, makeObservable} from '@xh/hoist/mobx';
|
|
13
|
+
import {getTestId, TEST_ID} from '@xh/hoist/utils/js';
|
|
14
|
+
import {getLayoutProps, getNonLayoutProps} from '@xh/hoist/utils/react';
|
|
15
|
+
import classNames from 'classnames';
|
|
16
|
+
import {capitalize, filter, isEmpty} from 'lodash';
|
|
17
|
+
import {KeyboardEvent} from 'react';
|
|
18
|
+
import './IntentInput.scss';
|
|
19
|
+
|
|
20
|
+
export const INTENTS: Intent[] = ['primary', 'success', 'warning', 'danger'];
|
|
21
|
+
|
|
22
|
+
export interface IntentInputProps extends HoistProps, HoistInputProps {
|
|
23
|
+
/** True to render in a compact mode with reduced sizing for space-constrained contexts. */
|
|
24
|
+
compact?: boolean;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* True to offer a null value, rendered as an additional outlined swatch ahead of the intents
|
|
28
|
+
* and holding the check while the value is null. Defaults to false, leaving the control a
|
|
29
|
+
* required single-select.
|
|
30
|
+
*/
|
|
31
|
+
enableClear?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Intents to offer, in render order. Defaults to all four Hoist intents - specify a subset
|
|
35
|
+
* to restrict or reorder the available choices.
|
|
36
|
+
*/
|
|
37
|
+
intents?: Intent[];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* True to render each intent's name alongside its swatch. Defaults to false, for the most
|
|
41
|
+
* compact presentation - note swatches always carry their intent name as a tooltip and as
|
|
42
|
+
* their accessible label, so an unlabelled control remains identifiable.
|
|
43
|
+
*/
|
|
44
|
+
showNames?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An input for selecting a Hoist Intent, rendered as a compact row of color swatches drawn in
|
|
49
|
+
* the intent colors themselves. The selected swatch is marked with a check and a surrounding
|
|
50
|
+
* ring; names can optionally be rendered alongside each swatch.
|
|
51
|
+
*
|
|
52
|
+
* Set `enableClear` to offer a null value, rendered as an additional outlined swatch ahead of
|
|
53
|
+
* the intents.
|
|
54
|
+
*
|
|
55
|
+
* Designed to fit within a toolbar or a dense form without additional chrome. Swatches are
|
|
56
|
+
* rendered as an ARIA radiogroup, supporting selection via click, enter/space, and arrow keys.
|
|
57
|
+
*/
|
|
58
|
+
export const [IntentInput, intentInput] = hoistCmp.withFactory<IntentInputProps>({
|
|
59
|
+
displayName: 'IntentInput',
|
|
60
|
+
className: 'xh-intent-input',
|
|
61
|
+
render(props, ref) {
|
|
62
|
+
return useHoistInputModel(cmp, props, ref, IntentInputModel);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
(IntentInput as any).hasLayoutSupport = true;
|
|
66
|
+
|
|
67
|
+
//-----------------------
|
|
68
|
+
// Implementation
|
|
69
|
+
//-----------------------
|
|
70
|
+
const buttonEl = elementFactory('button');
|
|
71
|
+
|
|
72
|
+
class IntentInputModel extends HoistInputModel {
|
|
73
|
+
override xhImpl = true;
|
|
74
|
+
|
|
75
|
+
@computed
|
|
76
|
+
get intents(): Intent[] {
|
|
77
|
+
const {intents} = this.componentProps;
|
|
78
|
+
return isEmpty(intents) ? INTENTS : intents;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Selectable values in render order - the offered intents, preceded by a null entry when
|
|
83
|
+
* `enableClear` is set. Null is a value here like any other, so it selects, renders a check,
|
|
84
|
+
* and takes part in keyboard nav without any special-casing.
|
|
85
|
+
*/
|
|
86
|
+
@computed
|
|
87
|
+
get options(): Intent[] {
|
|
88
|
+
const {intents} = this;
|
|
89
|
+
return this.componentProps.enableClear ? [null, ...intents] : intents;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get enabledButtons(): HTMLButtonElement[] {
|
|
93
|
+
const btns = this.domEl?.querySelectorAll('button') ?? [];
|
|
94
|
+
return filter(btns, (b: HTMLButtonElement) => !b.disabled) as HTMLButtonElement[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
constructor() {
|
|
98
|
+
super();
|
|
99
|
+
makeObservable(this);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
onSwatchClick = (intent: Intent) => {
|
|
103
|
+
this.noteValueChange(intent);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Arrow keys move the selection to the adjacent option, as expected of a radiogroup. Wraps
|
|
108
|
+
* around at either end, and starts from the first option when the current value is not among
|
|
109
|
+
* those offered.
|
|
110
|
+
*/
|
|
111
|
+
onKeyDown = (e: KeyboardEvent) => {
|
|
112
|
+
const delta = e.key === 'ArrowRight' ? 1 : e.key === 'ArrowLeft' ? -1 : null;
|
|
113
|
+
if (delta == null) return;
|
|
114
|
+
|
|
115
|
+
const {options, renderValue} = this,
|
|
116
|
+
currIdx = options.indexOf(renderValue);
|
|
117
|
+
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
|
|
120
|
+
const nextIdx = currIdx === -1 ? 0 : (currIdx + delta + options.length) % options.length;
|
|
121
|
+
this.noteValueChange(options[nextIdx]);
|
|
122
|
+
this.enabledButtons[nextIdx]?.focus();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
override blur() {
|
|
126
|
+
this.enabledButtons.forEach(it => it.blur());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override focus() {
|
|
130
|
+
this.enabledButtons[0]?.focus();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const cmp = hoistCmp.factory<IntentInputModel>(({model, className, ...props}, ref) => {
|
|
135
|
+
const {
|
|
136
|
+
// HoistInput props - exclude from passthrough to the container
|
|
137
|
+
bind,
|
|
138
|
+
disabled,
|
|
139
|
+
onChange,
|
|
140
|
+
onCommit,
|
|
141
|
+
tabIndex,
|
|
142
|
+
value,
|
|
143
|
+
commitOnChange,
|
|
144
|
+
// Consumed by model
|
|
145
|
+
enableClear,
|
|
146
|
+
intents,
|
|
147
|
+
// Consumed by this component
|
|
148
|
+
compact,
|
|
149
|
+
showNames,
|
|
150
|
+
testId,
|
|
151
|
+
domAttrs,
|
|
152
|
+
// Remainder passed to the container div
|
|
153
|
+
...rest
|
|
154
|
+
} = getNonLayoutProps(props);
|
|
155
|
+
|
|
156
|
+
const {renderValue} = model;
|
|
157
|
+
|
|
158
|
+
return div({
|
|
159
|
+
...rest,
|
|
160
|
+
className: classNames(
|
|
161
|
+
className,
|
|
162
|
+
compact && 'xh-intent-input--compact',
|
|
163
|
+
showNames && 'xh-intent-input--with-names'
|
|
164
|
+
),
|
|
165
|
+
ref,
|
|
166
|
+
role: 'radiogroup',
|
|
167
|
+
onFocus: model.onFocus,
|
|
168
|
+
onBlur: model.onBlur,
|
|
169
|
+
onKeyDown: disabled ? null : model.onKeyDown,
|
|
170
|
+
...getLayoutProps(props),
|
|
171
|
+
[TEST_ID]: testId,
|
|
172
|
+
...domAttrs,
|
|
173
|
+
items: model.options.map(intent => {
|
|
174
|
+
const isSelected = renderValue === intent,
|
|
175
|
+
isNull = intent == null,
|
|
176
|
+
name = isNull ? 'None' : capitalize(intent);
|
|
177
|
+
|
|
178
|
+
return buttonEl({
|
|
179
|
+
key: intent ?? 'none',
|
|
180
|
+
type: 'button',
|
|
181
|
+
className: classNames(
|
|
182
|
+
'xh-intent-input__swatch',
|
|
183
|
+
`xh-intent-input__swatch--${intent ?? 'none'}`,
|
|
184
|
+
isSelected && 'xh-intent-input__swatch--selected'
|
|
185
|
+
),
|
|
186
|
+
role: 'radio',
|
|
187
|
+
'aria-checked': isSelected,
|
|
188
|
+
'aria-label': name,
|
|
189
|
+
title: name,
|
|
190
|
+
disabled,
|
|
191
|
+
tabIndex,
|
|
192
|
+
onClick: () => model.onSwatchClick(intent),
|
|
193
|
+
[TEST_ID]: getTestId(testId, intent ?? 'none'),
|
|
194
|
+
items: [
|
|
195
|
+
div({
|
|
196
|
+
className: 'xh-intent-input__swatch__fill',
|
|
197
|
+
// Check is always rendered, and hidden via `visibility` when unselected.
|
|
198
|
+
// Omitting it instead would change the swatch's baseline, which shifts
|
|
199
|
+
// baseline-aligned siblings (e.g. an inline FormField label) on every
|
|
200
|
+
// selection change - see the note in IntentInput.scss.
|
|
201
|
+
item: Icon.check({
|
|
202
|
+
className: classNames(
|
|
203
|
+
'xh-intent-input__swatch__check',
|
|
204
|
+
!isSelected && 'xh-intent-input__swatch__check--hidden'
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
}),
|
|
208
|
+
div({omit: !showNames, className: 'xh-intent-input__swatch__name', item: name})
|
|
209
|
+
]
|
|
210
|
+
});
|
|
211
|
+
})
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getPasswordManagerAttrs,
|
|
9
|
+
HoistInputModel,
|
|
10
|
+
HoistInputProps,
|
|
11
|
+
PasswordManagerSupportProps,
|
|
12
|
+
useHoistInputModel
|
|
13
|
+
} from '@xh/hoist/cmp/input';
|
|
8
14
|
import {hoistCmp, HoistProps, LayoutProps, StyleProps} from '@xh/hoist/core';
|
|
9
15
|
import '@xh/hoist/desktop/register';
|
|
10
16
|
import {fmtNumber, NumericPrecision, parseNumber, Precision, ZeroPad} from '@xh/hoist/format';
|
|
@@ -16,7 +22,8 @@ import type {Property} from 'csstype';
|
|
|
16
22
|
import {debounce, isNaN, isNil, isNumber, round} from 'lodash';
|
|
17
23
|
import {KeyboardEventHandler, ReactElement, ReactNode, Ref, useLayoutEffect} from 'react';
|
|
18
24
|
|
|
19
|
-
export interface NumberInputProps
|
|
25
|
+
export interface NumberInputProps
|
|
26
|
+
extends HoistProps, LayoutProps, StyleProps, HoistInputProps, PasswordManagerSupportProps {
|
|
20
27
|
value?: number;
|
|
21
28
|
|
|
22
29
|
/** True to focus the control on render. */
|
|
@@ -278,6 +285,8 @@ const cmp = hoistCmp.factory<NumberInputModel>(({model, className, ...props}, re
|
|
|
278
285
|
textAlign: withDefault(props.textAlign, 'right')
|
|
279
286
|
},
|
|
280
287
|
[TEST_ID]: props.testId,
|
|
288
|
+
...getPasswordManagerAttrs(props.enablePasswordManagers),
|
|
289
|
+
...props.domAttrs,
|
|
281
290
|
onBlur: model.onBlur,
|
|
282
291
|
onFocus: model.onFocus,
|
|
283
292
|
onKeyDown: model.onKeyDown,
|
|
@@ -478,6 +478,7 @@ const triggerButton = hoistCmp.factory<PickerModel>(({model, props, nothingSelec
|
|
|
478
478
|
width: withDefault(width, 160),
|
|
479
479
|
style: props.style,
|
|
480
480
|
[TEST_ID]: getTestId(props, 'trigger'),
|
|
481
|
+
...props.domAttrs,
|
|
481
482
|
onClick: () => {
|
|
482
483
|
if (model.popoverIsOpen) {
|
|
483
484
|
model.closePopover();
|
|
@@ -113,6 +113,7 @@ const cmp = hoistCmp.factory<RadioInputModel>(({model, className, ...props}, ref
|
|
|
113
113
|
inline: props.inline,
|
|
114
114
|
selectedValue: model.renderValue,
|
|
115
115
|
onChange: model.onChange,
|
|
116
|
-
[TEST_ID]: props.testId
|
|
116
|
+
[TEST_ID]: props.testId,
|
|
117
|
+
...props.domAttrs
|
|
117
118
|
});
|
|
118
119
|
});
|
|
@@ -59,20 +59,71 @@
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// Outlined mode - border around control tray
|
|
62
|
+
// Outlined mode (default) - a border around the control tray. Independent of the tray fill,
|
|
63
|
+
// which is governed by --no-tray-bg below.
|
|
63
64
|
&--outlined .bp6-segmented-control {
|
|
64
|
-
background-color: transparent;
|
|
65
65
|
border: 1px solid var(--xh-border-color);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// Not filling - shrink the tray to its options. The tray is a block-level flex container, so it
|
|
69
|
+
// spans its parent regardless; BP's fill class governs only whether the options themselves grow.
|
|
70
|
+
// Without this, an unfilled control leaves an empty run of tray to the right of its options.
|
|
71
|
+
&:not(#{&}--fill) .bp6-segmented-control {
|
|
72
|
+
width: fit-content;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Equal segment widths - zeroing the flex basis makes each option take an identical share of the
|
|
76
|
+
// tray. BP's flex-grow alone divides only the leftover space, leaving options at differing widths.
|
|
77
|
+
// The min-width keeps that equal share from shrinking an option below its own label. A zero
|
|
78
|
+
// basis otherwise lets each option fall to BP's fixed 30px floor, which is unrelated to its
|
|
79
|
+
// content: the horizontal padding is eaten first, then a single-word label spills past the
|
|
80
|
+
// border, since it can neither wrap nor ellipsize. Options only stop sharing equally once there
|
|
81
|
+
// is genuinely not enough room, and then only the ones that need the space keep it.
|
|
82
|
+
&--fill#{&}--equal-widths .bp6-segmented-control > .bp6-button {
|
|
83
|
+
flex-basis: 0;
|
|
84
|
+
min-width: min-content;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Tray background suppressed - a lighter presentation, with no fill behind the options.
|
|
88
|
+
&--no-tray-bg .bp6-segmented-control {
|
|
89
|
+
background-color: transparent;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Option dividers - internal structure for a tray whose options are otherwise undifferentiated,
|
|
93
|
+
// most notably in the empty state where no selected option breaks up the row. Drawn in the gap
|
|
94
|
+
// to the left of each option after the first, and suppressed on either side of the selected
|
|
95
|
+
// option so they never crowd it.
|
|
96
|
+
&--dividers .bp6-segmented-control {
|
|
97
|
+
> .bp6-button {
|
|
98
|
+
position: relative;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
> .bp6-button + .bp6-button::before {
|
|
102
|
+
content: '';
|
|
103
|
+
position: absolute;
|
|
104
|
+
left: calc(var(--xh-segmented-control-pad-px) / -2);
|
|
105
|
+
top: 20%;
|
|
106
|
+
bottom: 20%;
|
|
107
|
+
width: 1px;
|
|
108
|
+
background-color: var(--xh-border-color);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
> .bp6-button:not(.bp6-minimal)::before,
|
|
112
|
+
> .bp6-button:not(.bp6-minimal) + .bp6-button::before {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
68
117
|
@each $intent in (primary, success, warning, danger) {
|
|
69
118
|
&--outlined#{&}--#{$intent} .bp6-segmented-control {
|
|
70
119
|
border-color: var(--xh-intent-#{$intent});
|
|
71
120
|
}
|
|
72
121
|
}
|
|
73
122
|
|
|
74
|
-
// Compact mode -
|
|
75
|
-
|
|
123
|
+
// Compact mode - via the `compact` prop, or implicitly within a compact Toolbar, whose
|
|
124
|
+
// item height the standard sizing above would overflow.
|
|
125
|
+
&--compact .bp6-segmented-control,
|
|
126
|
+
.xh-toolbar--compact & .bp6-segmented-control {
|
|
76
127
|
padding: 0;
|
|
77
128
|
gap: 0;
|
|
78
129
|
|
|
@@ -89,7 +140,8 @@
|
|
|
89
140
|
}
|
|
90
141
|
|
|
91
142
|
// Compact + outlined - shrink buttons to account for the border.
|
|
92
|
-
&--compact#{&}--outlined .bp6-segmented-control > .bp6-button
|
|
143
|
+
&--compact#{&}--outlined .bp6-segmented-control > .bp6-button,
|
|
144
|
+
.xh-toolbar--compact &#{&}--outlined .bp6-segmented-control > .bp6-button {
|
|
93
145
|
--compact-btn-h: calc(var(--xh-tbar-compact-item-height-px) - 2px);
|
|
94
146
|
min-height: var(--compact-btn-h);
|
|
95
147
|
max-height: var(--compact-btn-h);
|
|
@@ -28,8 +28,17 @@ export interface SegmentedControlProps extends HoistProps, HoistInputProps {
|
|
|
28
28
|
compact?: boolean;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* True (default) to
|
|
32
|
-
*
|
|
31
|
+
* True (default) to render all segments at an equal width when {@link fill} is enabled,
|
|
32
|
+
* dividing the available width into equal parts regardless of label length - the conventional
|
|
33
|
+
* segmented-control appearance. Set false to instead size each segment to its content and
|
|
34
|
+
* share only the leftover space, so a longer label yields a wider segment. No effect when
|
|
35
|
+
* `fill` is false.
|
|
36
|
+
*/
|
|
37
|
+
equalSegmentWidths?: boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* True (default) to stretch the control to fill available width. Set false to size the control
|
|
41
|
+
* to its options. See `equalSegmentWidths` for how the filled width is divided up.
|
|
33
42
|
*/
|
|
34
43
|
fill?: boolean;
|
|
35
44
|
|
|
@@ -48,10 +57,25 @@ export interface SegmentedControlProps extends HoistProps, HoistInputProps {
|
|
|
48
57
|
options: Array<SegmentedControlOption | SegmentedControlNullOption | OptionPrimitive>;
|
|
49
58
|
|
|
50
59
|
/**
|
|
51
|
-
* True to render
|
|
52
|
-
*
|
|
60
|
+
* True (default) to render a border around the control tray. Border color follows the current
|
|
61
|
+
* intent. Note this controls the tray border only - it does not remove the tray background,
|
|
62
|
+
* which is governed independently by `showTrayBackground`.
|
|
53
63
|
*/
|
|
54
64
|
outlined?: boolean;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* True (default) to fill the control tray with a background, setting it off from the surface
|
|
68
|
+
* behind it. Set false to omit that fill for a lighter presentation.
|
|
69
|
+
*/
|
|
70
|
+
showTrayBackground?: boolean;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* True to render dividers between options, adding internal structure to the tray. Dividers
|
|
74
|
+
* are suppressed on either side of the selected option so they do not compete with it.
|
|
75
|
+
* Defaults to 'auto', showing dividers only while no option is selected - the state in which
|
|
76
|
+
* the tray offers no other cue that its options are separate and selectable.
|
|
77
|
+
*/
|
|
78
|
+
showOptionDividers?: boolean | 'auto';
|
|
55
79
|
}
|
|
56
80
|
|
|
57
81
|
/**
|
|
@@ -77,6 +101,14 @@ export const [SegmentedControl, segmentedControl] = hoistCmp.withFactory<Segment
|
|
|
77
101
|
//-----------------------
|
|
78
102
|
// Implementation
|
|
79
103
|
//-----------------------
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Key handed to the Blueprint control when no option is selected. Option keys are always
|
|
107
|
+
* `String(index)`, so this cannot collide with a real option - and unlike a nullish value, it
|
|
108
|
+
* keeps Blueprint from falling back to its own internal selection state. See usage note below.
|
|
109
|
+
*/
|
|
110
|
+
const NO_SELECTION_KEY = '__xh_no_selection__';
|
|
111
|
+
|
|
80
112
|
interface NormalizedOption extends SegmentedControlOption {
|
|
81
113
|
label: string;
|
|
82
114
|
intent?: Intent;
|
|
@@ -109,11 +141,15 @@ class SegmentedControlModel extends HoistInputModel {
|
|
|
109
141
|
});
|
|
110
142
|
}
|
|
111
143
|
|
|
112
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Key of the option matching the current render value, or null if no option matches -
|
|
146
|
+
* including whenever the bound value is itself null. Note this is the model's own view of
|
|
147
|
+
* the selection: the key actually handed to Blueprint is resolved at its point of use below.
|
|
148
|
+
*/
|
|
113
149
|
@computed
|
|
114
|
-
get selectedKey(): string {
|
|
150
|
+
get selectedKey(): string | null {
|
|
115
151
|
const {renderValue, normalizedOptions} = this;
|
|
116
|
-
return normalizedOptions.find(o => o.value === renderValue)?._key;
|
|
152
|
+
return normalizedOptions.find(o => o.value === renderValue)?._key ?? null;
|
|
117
153
|
}
|
|
118
154
|
|
|
119
155
|
get enabledButtons(): HTMLButtonElement[] {
|
|
@@ -154,13 +190,21 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
|
|
|
154
190
|
options,
|
|
155
191
|
// Consumed by this component
|
|
156
192
|
compact,
|
|
193
|
+
equalSegmentWidths = true,
|
|
194
|
+
fill = true,
|
|
157
195
|
intent,
|
|
158
|
-
outlined,
|
|
196
|
+
outlined = true,
|
|
197
|
+
showTrayBackground = true,
|
|
198
|
+
showOptionDividers = 'auto',
|
|
159
199
|
testId,
|
|
200
|
+
domAttrs,
|
|
160
201
|
// Remainder passed to BP SegmentedControl
|
|
161
202
|
...bpProps
|
|
162
203
|
} = getNonLayoutProps(props);
|
|
163
204
|
|
|
205
|
+
// 'auto' dividers track the empty state - shown only while nothing is selected.
|
|
206
|
+
const dividers = showOptionDividers === 'auto' ? model.selectedKey == null : showOptionDividers;
|
|
207
|
+
|
|
164
208
|
// Resolve the effective intent per option (own intent wins over control-level default),
|
|
165
209
|
// applied via a per-button className that our SCSS keys its solid/hint coloring off of.
|
|
166
210
|
const defaultIntent = intent && intent !== 'none' ? intent : null,
|
|
@@ -181,20 +225,31 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
|
|
|
181
225
|
className: classNames(
|
|
182
226
|
className,
|
|
183
227
|
compact && 'xh-segmented-control--compact',
|
|
228
|
+
fill && 'xh-segmented-control--fill',
|
|
229
|
+
fill && equalSegmentWidths && 'xh-segmented-control--equal-widths',
|
|
184
230
|
defaultIntent && `xh-segmented-control--${defaultIntent}`,
|
|
185
|
-
outlined && 'xh-segmented-control--outlined'
|
|
231
|
+
outlined && 'xh-segmented-control--outlined',
|
|
232
|
+
!showTrayBackground && 'xh-segmented-control--no-tray-bg',
|
|
233
|
+
dividers && 'xh-segmented-control--dividers'
|
|
186
234
|
),
|
|
187
235
|
ref,
|
|
188
236
|
onFocus: model.onFocus,
|
|
189
237
|
onBlur: model.onBlur,
|
|
190
238
|
...getLayoutProps(props),
|
|
191
239
|
[TEST_ID]: props.testId,
|
|
240
|
+
...domAttrs,
|
|
192
241
|
item: bpSegmentedControl({
|
|
193
242
|
...bpProps,
|
|
194
|
-
fill
|
|
243
|
+
fill,
|
|
195
244
|
size: compact ? 'small' : undefined,
|
|
196
245
|
options: bpOptions,
|
|
197
|
-
value
|
|
246
|
+
// Blueprint resolves its selection as `value ?? internalState`, where the internal
|
|
247
|
+
// state is set on every click even when controlled. Passing a nullish value for a
|
|
248
|
+
// selection matching no option would fall back to the last-clicked segment,
|
|
249
|
+
// leaving it highlighted after the bound value is cleared. Pass a sentinel key
|
|
250
|
+
// instead, keeping the control fully controlled - Blueprint handles a value matching
|
|
251
|
+
// no option natively (see `isAnySelected`) and emits no warning.
|
|
252
|
+
value: model.selectedKey ?? NO_SELECTION_KEY,
|
|
198
253
|
onValueChange: model.onValueChange,
|
|
199
254
|
disabled
|
|
200
255
|
})
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
reactWindowedSelect
|
|
28
28
|
} from '@xh/hoist/kit/react-select';
|
|
29
29
|
import {action, bindable, makeObservable, observable, override} from '@xh/hoist/mobx';
|
|
30
|
-
import {debouncePromise, wait} from '@xh/hoist/promise';
|
|
30
|
+
import {debouncePromise, wait, waitFor} from '@xh/hoist/promise';
|
|
31
31
|
import {elemWithin, getTestId, mergeDeep, TEST_ID, throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
32
32
|
import {createObservableRef, getLayoutProps} from '@xh/hoist/utils/react';
|
|
33
33
|
import classNames from 'classnames';
|
|
@@ -820,13 +820,14 @@ const cmp = hoistCmp.factory<SelectInputModel>(({model, className, ...props}, re
|
|
|
820
820
|
rsProps.inputValue = model.inputValue || '';
|
|
821
821
|
rsProps.onInputChange = model.onInputChange;
|
|
822
822
|
rsProps.controlShouldRenderValue = !model.hasFocus;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Scroll selected option into view on open - react-select's own does not fire for portalled menus.
|
|
826
|
+
if (!model.multiMode && model.renderValue) {
|
|
823
827
|
rsProps.onMenuOpen = () => {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
)[0];
|
|
828
|
-
selectedEl?.scrollIntoView({block: 'end'});
|
|
829
|
-
});
|
|
828
|
+
const getSel = () =>
|
|
829
|
+
document.getElementsByClassName('xh-select__option--is-selected')[0];
|
|
830
|
+
waitFor(() => !!getSel()).then(() => getSel().scrollIntoView({block: 'end'}));
|
|
830
831
|
};
|
|
831
832
|
}
|
|
832
833
|
|
|
@@ -874,6 +875,7 @@ const cmp = hoistCmp.factory<SelectInputModel>(({model, className, ...props}, re
|
|
|
874
875
|
}
|
|
875
876
|
},
|
|
876
877
|
testId: props.testId,
|
|
878
|
+
domAttrs: props.domAttrs,
|
|
877
879
|
...layoutProps,
|
|
878
880
|
width: withDefault(width, 200),
|
|
879
881
|
height: height,
|
|
@@ -114,6 +114,8 @@ const cmp = hoistCmp.factory<SliderInputModel>(({model, className, ...props}, re
|
|
|
114
114
|
...layoutProps,
|
|
115
115
|
width: withDefault(width, 200),
|
|
116
116
|
className,
|
|
117
|
+
testId: props.testId,
|
|
118
|
+
domAttrs: props.domAttrs,
|
|
117
119
|
|
|
118
120
|
onBlur: model.onBlur,
|
|
119
121
|
onFocus: model.onFocus,
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getPasswordManagerAttrs,
|
|
9
|
+
HoistInputModel,
|
|
10
|
+
HoistInputProps,
|
|
11
|
+
PasswordManagerSupportProps,
|
|
12
|
+
useHoistInputModel
|
|
13
|
+
} from '@xh/hoist/cmp/input';
|
|
8
14
|
import {hoistCmp, HoistProps, LayoutProps, StyleProps} from '@xh/hoist/core';
|
|
9
15
|
import '@xh/hoist/desktop/register';
|
|
10
16
|
import {textArea as bpTextarea} from '@xh/hoist/kit/blueprint';
|
|
@@ -13,7 +19,8 @@ import {useComposedRefs, getLayoutProps} from '@xh/hoist/utils/react';
|
|
|
13
19
|
import {Ref} from 'react';
|
|
14
20
|
import './TextArea.scss';
|
|
15
21
|
|
|
16
|
-
export interface TextAreaProps
|
|
22
|
+
export interface TextAreaProps
|
|
23
|
+
extends HoistProps, HoistInputProps, PasswordManagerSupportProps, LayoutProps, StyleProps {
|
|
17
24
|
value?: string;
|
|
18
25
|
|
|
19
26
|
/** True to focus the control on render. */
|
|
@@ -91,6 +98,8 @@ const cmp = hoistCmp.factory<TextAreaInputModel>(({model, className, ...props},
|
|
|
91
98
|
spellCheck: withDefault(props.spellCheck, false),
|
|
92
99
|
tabIndex: props.tabIndex,
|
|
93
100
|
[TEST_ID]: props.testId,
|
|
101
|
+
...getPasswordManagerAttrs(props.enablePasswordManagers),
|
|
102
|
+
...props.domAttrs,
|
|
94
103
|
id: props.id,
|
|
95
104
|
className,
|
|
96
105
|
style: {
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getPasswordManagerAttrs,
|
|
9
|
+
HoistInputModel,
|
|
10
|
+
HoistInputProps,
|
|
11
|
+
PasswordManagerSupportProps,
|
|
12
|
+
useHoistInputModel
|
|
13
|
+
} from '@xh/hoist/cmp/input';
|
|
8
14
|
import {div} from '@xh/hoist/cmp/layout';
|
|
9
15
|
import {hoistCmp, HoistProps, LayoutProps, StyleProps} from '@xh/hoist/core';
|
|
10
16
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
@@ -17,7 +23,8 @@ import type {Property} from 'csstype';
|
|
|
17
23
|
import {isEmpty} from 'lodash';
|
|
18
24
|
import {FocusEvent, KeyboardEventHandler, ReactElement, ReactNode, Ref} from 'react';
|
|
19
25
|
|
|
20
|
-
export interface TextInputProps
|
|
26
|
+
export interface TextInputProps
|
|
27
|
+
extends HoistProps, HoistInputProps, PasswordManagerSupportProps, LayoutProps, StyleProps {
|
|
21
28
|
value?: string;
|
|
22
29
|
|
|
23
30
|
/**
|
|
@@ -155,6 +162,8 @@ const cmp = hoistCmp.factory<TextInputProps & {model: TextInputModel}>(
|
|
|
155
162
|
textAlign: withDefault(props.textAlign, 'left')
|
|
156
163
|
},
|
|
157
164
|
[TEST_ID]: props.testId,
|
|
165
|
+
...getPasswordManagerAttrs(props.enablePasswordManagers),
|
|
166
|
+
...props.domAttrs,
|
|
158
167
|
onChange: model.onChange,
|
|
159
168
|
onKeyDown: model.onKeyDown
|
|
160
169
|
}),
|
|
@@ -49,6 +49,9 @@ export interface LeftRightChooserConfig {
|
|
|
49
49
|
|
|
50
50
|
/** @internal */
|
|
51
51
|
xhImpl?: boolean;
|
|
52
|
+
|
|
53
|
+
/** See {@link HoistBase.xhName}. */
|
|
54
|
+
xhName?: string;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/** Data record object for a LeftRightChooser value item. */
|
|
@@ -146,11 +149,13 @@ export class LeftRightChooserModel extends HoistModel {
|
|
|
146
149
|
rightGroupingExpanded = true,
|
|
147
150
|
rightEmptyText = null,
|
|
148
151
|
showCounts = true,
|
|
152
|
+
xhName = null,
|
|
149
153
|
xhImpl = false
|
|
150
154
|
}: LeftRightChooserConfig) {
|
|
151
155
|
super();
|
|
152
156
|
makeObservable(this);
|
|
153
157
|
this.xhImpl = xhImpl;
|
|
158
|
+
this.xhName = xhName;
|
|
154
159
|
|
|
155
160
|
this.onChange = onChange;
|
|
156
161
|
this._ungroupedName = ungroupedName;
|