@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.
Files changed (270) hide show
  1. package/.npmignore +39 -0
  2. package/CHANGELOG.md +108 -20
  3. package/admin/AdminUtils.ts +64 -0
  4. package/admin/App.scss +88 -0
  5. package/admin/columns/UserData.ts +30 -3
  6. package/admin/detail/RestDetailModel.ts +64 -0
  7. package/admin/detail/RestDetailPanel.ts +106 -0
  8. package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +27 -94
  9. package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +23 -60
  10. package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +1 -2
  11. package/admin/tabs/general/alertBanner/AlertBannerPanel.ts +2 -13
  12. package/admin/tabs/general/config/ConfigDetailPanel.ts +72 -0
  13. package/admin/tabs/general/config/ConfigPanel.ts +41 -53
  14. package/admin/tabs/general/config/ConfigPanelModel.ts +3 -0
  15. package/admin/tabs/general/config/ConfigValue.scss +14 -0
  16. package/admin/tabs/general/config/ConfigValue.ts +104 -65
  17. package/admin/tabs/userData/jsonblob/JsonBlobDetailPanel.ts +93 -0
  18. package/admin/tabs/userData/jsonblob/JsonBlobModel.ts +4 -0
  19. package/admin/tabs/userData/jsonblob/JsonBlobPanel.ts +2 -0
  20. package/admin/tabs/userData/prefs/UserPreferenceDetailPanel.ts +62 -0
  21. package/admin/tabs/userData/prefs/UserPreferenceModel.ts +10 -3
  22. package/admin/tabs/userData/prefs/UserPreferencePanel.ts +3 -1
  23. package/admin/tabs/userData/roles/details/RoleDetails.scss +2 -19
  24. package/admin/tabs/userData/roles/details/RoleDetails.ts +13 -13
  25. package/appcontainer/AboutDialogModel.ts +1 -0
  26. package/appcontainer/AppContainerModel.ts +3 -0
  27. package/appcontainer/AppStateModel.ts +1 -0
  28. package/appcontainer/BannerSourceModel.ts +1 -0
  29. package/appcontainer/ChangelogDialogModel.ts +1 -0
  30. package/appcontainer/ColChooserOptionsModel.ts +1 -0
  31. package/appcontainer/ExceptionDialogModel.ts +1 -0
  32. package/appcontainer/FeedbackDialogModel.ts +1 -0
  33. package/appcontainer/ImpersonationBarModel.ts +1 -0
  34. package/appcontainer/MessageSourceModel.ts +1 -0
  35. package/appcontainer/OptionsDialogModel.ts +1 -0
  36. package/appcontainer/PageStateModel.ts +1 -0
  37. package/appcontainer/RouterModel.ts +2 -0
  38. package/appcontainer/SizingModeModel.ts +1 -0
  39. package/appcontainer/ThemeModel.ts +1 -0
  40. package/appcontainer/ToastSourceModel.ts +1 -0
  41. package/appcontainer/UserAgentModel.ts +1 -0
  42. package/appcontainer/ViewportSizeModel.ts +1 -0
  43. package/build/types/admin/AdminUtils.d.ts +29 -0
  44. package/build/types/admin/columns/UserData.d.ts +3 -0
  45. package/build/types/admin/detail/RestDetailModel.d.ts +20 -0
  46. package/build/types/admin/detail/RestDetailPanel.d.ts +34 -0
  47. package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +5 -17
  48. package/build/types/admin/tabs/general/config/ConfigDetailPanel.d.ts +7 -0
  49. package/build/types/admin/tabs/general/config/ConfigValue.d.ts +10 -4
  50. package/build/types/admin/tabs/userData/jsonblob/JsonBlobDetailPanel.d.ts +3 -0
  51. package/build/types/admin/tabs/userData/prefs/UserPreferenceDetailPanel.d.ts +3 -0
  52. package/build/types/admin/tabs/userData/prefs/UserPreferenceModel.d.ts +3 -0
  53. package/build/types/appcontainer/AboutDialogModel.d.ts +1 -0
  54. package/build/types/appcontainer/AppContainerModel.d.ts +1 -0
  55. package/build/types/appcontainer/AppStateModel.d.ts +1 -0
  56. package/build/types/appcontainer/BannerSourceModel.d.ts +1 -0
  57. package/build/types/appcontainer/ChangelogDialogModel.d.ts +1 -0
  58. package/build/types/appcontainer/ColChooserOptionsModel.d.ts +1 -0
  59. package/build/types/appcontainer/ExceptionDialogModel.d.ts +1 -0
  60. package/build/types/appcontainer/FeedbackDialogModel.d.ts +1 -0
  61. package/build/types/appcontainer/ImpersonationBarModel.d.ts +1 -0
  62. package/build/types/appcontainer/MessageSourceModel.d.ts +1 -0
  63. package/build/types/appcontainer/OptionsDialogModel.d.ts +1 -0
  64. package/build/types/appcontainer/PageStateModel.d.ts +1 -0
  65. package/build/types/appcontainer/RouterModel.d.ts +1 -0
  66. package/build/types/appcontainer/SizingModeModel.d.ts +1 -0
  67. package/build/types/appcontainer/ThemeModel.d.ts +1 -0
  68. package/build/types/appcontainer/ToastSourceModel.d.ts +1 -0
  69. package/build/types/appcontainer/UserAgentModel.d.ts +1 -0
  70. package/build/types/appcontainer/ViewportSizeModel.d.ts +1 -0
  71. package/build/types/cmp/ag-grid/AgGridModel.d.ts +3 -1
  72. package/build/types/cmp/card/Card.d.ts +2 -2
  73. package/build/types/cmp/chart/ChartModel.d.ts +2 -0
  74. package/build/types/cmp/dataview/DataViewModel.d.ts +2 -0
  75. package/build/types/cmp/daterange/DateRangePickerModel.d.ts +266 -0
  76. package/build/types/cmp/daterange/DateRangePresets.d.ts +26 -0
  77. package/build/types/cmp/daterange/DateRangeUtils.d.ts +96 -0
  78. package/build/types/cmp/daterange/Types.d.ts +152 -0
  79. package/build/types/cmp/daterange/index.d.ts +4 -0
  80. package/build/types/cmp/filter/FilterChooserModel.d.ts +3 -1
  81. package/build/types/cmp/form/FormModel.d.ts +3 -1
  82. package/build/types/cmp/grid/GridModel.d.ts +2 -0
  83. package/build/types/cmp/grid/Types.d.ts +2 -0
  84. package/build/types/cmp/grid/filter/GridFilterModel.d.ts +1 -1
  85. package/build/types/cmp/grid/impl/ColumnWidthCalculator.d.ts +1 -1
  86. package/build/types/cmp/grouping/GroupingChooserModel.d.ts +3 -1
  87. package/build/types/cmp/input/HoistInputProps.d.ts +49 -2
  88. package/build/types/cmp/tab/TabContainerModel.d.ts +3 -1
  89. package/build/types/cmp/tab/TabModel.d.ts +3 -1
  90. package/build/types/cmp/treemap/TreeMapModel.d.ts +2 -0
  91. package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +2 -0
  92. package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +2 -0
  93. package/build/types/core/HoistBase.d.ts +7 -0
  94. package/build/types/core/HoistProps.d.ts +26 -1
  95. package/build/types/core/model/RootRefreshContextModel.d.ts +1 -0
  96. package/build/types/core/types/Telemetry.d.ts +1 -1
  97. package/build/types/data/Store.d.ts +3 -1
  98. package/build/types/data/StoreSelectionModel.d.ts +3 -1
  99. package/build/types/data/cube/Cube.d.ts +5 -2
  100. package/build/types/data/cube/View.d.ts +2 -0
  101. package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +3 -0
  102. package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +3 -0
  103. package/build/types/desktop/cmp/button/Button.d.ts +2 -2
  104. package/build/types/desktop/cmp/button/ButtonGroup.d.ts +2 -2
  105. package/build/types/desktop/cmp/dash/DashConfig.d.ts +2 -0
  106. package/build/types/desktop/cmp/dash/DashViewModel.d.ts +3 -1
  107. package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +1 -1
  108. package/build/types/desktop/cmp/dash/container/DashContainerModel.d.ts +1 -1
  109. package/build/types/desktop/cmp/daterange/DateRangePicker.d.ts +66 -0
  110. package/build/types/desktop/cmp/daterange/impl/CustomTab.d.ts +3 -0
  111. package/build/types/desktop/cmp/daterange/impl/DateRangePickerLocalModel.d.ts +121 -0
  112. package/build/types/desktop/cmp/daterange/impl/PeriodTab.d.ts +2 -0
  113. package/build/types/desktop/cmp/daterange/impl/PresetsTab.d.ts +3 -0
  114. package/build/types/desktop/cmp/daterange/impl/RelativeTab.d.ts +3 -0
  115. package/build/types/desktop/cmp/daterange/impl/TabUtils.d.ts +21 -0
  116. package/build/types/desktop/cmp/daterange/index.d.ts +2 -0
  117. package/build/types/desktop/cmp/dock/DockContainerModel.d.ts +3 -1
  118. package/build/types/desktop/cmp/dock/DockViewModel.d.ts +3 -1
  119. package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +4 -1
  120. package/build/types/desktop/cmp/input/IntentInput.d.ts +38 -0
  121. package/build/types/desktop/cmp/input/NumberInput.d.ts +2 -2
  122. package/build/types/desktop/cmp/input/SegmentedControl.d.ts +25 -4
  123. package/build/types/desktop/cmp/input/TextArea.d.ts +2 -2
  124. package/build/types/desktop/cmp/input/TextInput.d.ts +2 -2
  125. package/build/types/desktop/cmp/input/index.d.ts +1 -0
  126. package/build/types/desktop/cmp/leftrightchooser/LeftRightChooserModel.d.ts +3 -1
  127. package/build/types/desktop/cmp/panel/PanelModel.d.ts +3 -1
  128. package/build/types/desktop/cmp/rest/RestGridModel.d.ts +1 -1
  129. package/build/types/desktop/cmp/tab/impl/TabContainer.d.ts +1 -1
  130. package/build/types/desktop/hooks/UseHotkeys.d.ts +4 -3
  131. package/build/types/icon/Icon.d.ts +3 -0
  132. package/build/types/inspector/impl/InspectorUtils.d.ts +3 -0
  133. package/build/types/inspector/instances/InstancesModel.d.ts +4 -0
  134. package/build/types/kit/react-day-picker/index.d.ts +3 -3
  135. package/build/types/mobile/cmp/button/Button.d.ts +2 -2
  136. package/build/types/mobile/cmp/input/NumberInput.d.ts +2 -2
  137. package/build/types/mobile/cmp/input/SegmentedControl.d.ts +18 -4
  138. package/build/types/mobile/cmp/input/TextArea.d.ts +2 -2
  139. package/build/types/mobile/cmp/input/TextInput.d.ts +2 -2
  140. package/build/types/mobile/cmp/navigator/NavigatorModel.d.ts +8 -2
  141. package/build/types/mobile/cmp/navigator/PageModel.d.ts +3 -1
  142. package/build/types/mobile/cmp/tab/impl/TabContainer.d.ts +1 -1
  143. package/build/types/svc/InspectorService.d.ts +1 -0
  144. package/build/types/utils/js/LangUtils.d.ts +24 -5
  145. package/cmp/README.md +1 -0
  146. package/cmp/ag-grid/AgGridModel.ts +5 -0
  147. package/cmp/badge/Badge.ts +5 -3
  148. package/cmp/card/Card.ts +4 -3
  149. package/cmp/chart/ChartModel.ts +11 -1
  150. package/cmp/clock/Clock.ts +1 -1
  151. package/cmp/dataview/DataViewModel.ts +7 -1
  152. package/cmp/daterange/DateRangePickerModel.ts +628 -0
  153. package/cmp/daterange/DateRangePresets.ts +274 -0
  154. package/cmp/daterange/DateRangeUtils.ts +557 -0
  155. package/cmp/daterange/README.md +434 -0
  156. package/cmp/daterange/Types.ts +203 -0
  157. package/cmp/daterange/index.ts +10 -0
  158. package/cmp/filter/FilterChooserModel.ts +7 -4
  159. package/cmp/form/FormModel.ts +6 -0
  160. package/cmp/form/README.md +1 -0
  161. package/cmp/grid/GridModel.ts +24 -4
  162. package/cmp/grid/Types.ts +3 -0
  163. package/cmp/grid/filter/GridFilterModel.ts +3 -1
  164. package/cmp/grid/impl/ColumnWidthCalculator.ts +5 -10
  165. package/cmp/grouping/GroupingChooserModel.ts +6 -1
  166. package/cmp/input/HoistInputProps.ts +50 -2
  167. package/cmp/input/README.md +42 -0
  168. package/cmp/layout/Box.ts +3 -2
  169. package/cmp/tab/TabContainerModel.ts +9 -1
  170. package/cmp/tab/TabModel.ts +5 -0
  171. package/cmp/treemap/SplitTreeMapModel.ts +12 -2
  172. package/cmp/treemap/TreeMapModel.ts +6 -1
  173. package/cmp/viewmanager/ViewManagerModel.ts +6 -1
  174. package/cmp/zoneGrid/ZoneGridModel.ts +7 -0
  175. package/core/HoistBase.ts +11 -0
  176. package/core/HoistProps.ts +27 -0
  177. package/core/README.md +46 -0
  178. package/core/impl/InstallServices.ts +5 -1
  179. package/core/model/RootRefreshContextModel.ts +2 -0
  180. package/core/types/Telemetry.ts +1 -1
  181. package/data/README.md +3 -1
  182. package/data/Store.ts +7 -0
  183. package/data/StoreSelectionModel.ts +5 -1
  184. package/data/cube/Cube.ts +12 -3
  185. package/data/cube/View.ts +5 -1
  186. package/desktop/README.md +2 -0
  187. package/desktop/appcontainer/LoginPanel.ts +2 -0
  188. package/desktop/cmp/button/Button.scss +71 -42
  189. package/desktop/cmp/button/Button.ts +10 -1
  190. package/desktop/cmp/button/ButtonGroup.ts +4 -1
  191. package/desktop/cmp/button/grid/ColChooserButton.ts +9 -1
  192. package/desktop/cmp/button/zoneGrid/ZoneMapperButton.ts +1 -0
  193. package/desktop/cmp/dash/DashConfig.ts +3 -0
  194. package/desktop/cmp/dash/DashViewModel.ts +14 -1
  195. package/desktop/cmp/dash/canvas/DashCanvasModel.ts +3 -1
  196. package/desktop/cmp/dash/container/DashContainerModel.ts +3 -1
  197. package/desktop/cmp/daterange/DateRangePicker.scss +577 -0
  198. package/desktop/cmp/daterange/DateRangePicker.ts +357 -0
  199. package/desktop/cmp/daterange/impl/CustomTab.ts +165 -0
  200. package/desktop/cmp/daterange/impl/DateRangePickerLocalModel.ts +482 -0
  201. package/desktop/cmp/daterange/impl/PeriodTab.ts +138 -0
  202. package/desktop/cmp/daterange/impl/PresetsTab.ts +58 -0
  203. package/desktop/cmp/daterange/impl/RelativeTab.ts +108 -0
  204. package/desktop/cmp/daterange/impl/TabUtils.ts +34 -0
  205. package/desktop/cmp/daterange/index.ts +8 -0
  206. package/desktop/cmp/dock/DockContainerModel.ts +6 -1
  207. package/desktop/cmp/dock/DockViewModel.ts +6 -1
  208. package/desktop/cmp/filechooser/FileChooser.ts +4 -0
  209. package/desktop/cmp/form/FormField.ts +1 -0
  210. package/desktop/cmp/grid/editors/DateEditor.ts +11 -2
  211. package/desktop/cmp/grouping/GroupingChooser.ts +8 -2
  212. package/desktop/cmp/input/Checkbox.ts +1 -0
  213. package/desktop/cmp/input/CodeInput.ts +1 -0
  214. package/desktop/cmp/input/DateInput.scss +1 -1
  215. package/desktop/cmp/input/DateInput.ts +1 -0
  216. package/desktop/cmp/input/IntentInput.scss +202 -0
  217. package/desktop/cmp/input/IntentInput.ts +213 -0
  218. package/desktop/cmp/input/NumberInput.ts +11 -2
  219. package/desktop/cmp/input/Picker.ts +1 -0
  220. package/desktop/cmp/input/RadioInput.ts +2 -1
  221. package/desktop/cmp/input/SegmentedControl.scss +57 -5
  222. package/desktop/cmp/input/SegmentedControl.ts +66 -11
  223. package/desktop/cmp/input/Select.ts +9 -7
  224. package/desktop/cmp/input/Slider.ts +2 -0
  225. package/desktop/cmp/input/SwitchInput.ts +1 -0
  226. package/desktop/cmp/input/TextArea.ts +11 -2
  227. package/desktop/cmp/input/TextInput.ts +11 -2
  228. package/desktop/cmp/input/index.ts +1 -0
  229. package/desktop/cmp/leftrightchooser/LeftRightChooserModel.ts +5 -0
  230. package/desktop/cmp/panel/Panel.ts +8 -5
  231. package/desktop/cmp/panel/PanelModel.ts +8 -0
  232. package/desktop/cmp/panel/impl/ResizeContainer.ts +2 -1
  233. package/desktop/cmp/rest/RestGridModel.ts +4 -0
  234. package/desktop/cmp/tab/impl/TabContainer.ts +2 -0
  235. package/desktop/cmp/toolbar/Toolbar.scss +20 -6
  236. package/desktop/hooks/UseHotkeys.ts +41 -12
  237. package/docs/README.md +2 -0
  238. package/docs/build-and-publish.md +23 -7
  239. package/docs/doc-registry.json +8 -0
  240. package/docs/version-compatibility.md +2 -0
  241. package/icon/Icon.ts +9 -0
  242. package/icon/index.ts +24 -0
  243. package/inspector/README.md +8 -2
  244. package/inspector/impl/InspectorUtils.ts +12 -0
  245. package/inspector/instances/DiagnosticsModel.ts +3 -2
  246. package/inspector/instances/InstancesModel.ts +81 -14
  247. package/inspector/instances/InstancesPanel.ts +6 -0
  248. package/kit/react-day-picker/index.ts +3 -3
  249. package/mobile/appcontainer/LoginPanel.ts +2 -0
  250. package/mobile/cmp/button/Button.ts +16 -3
  251. package/mobile/cmp/form/FormField.ts +1 -0
  252. package/mobile/cmp/input/Checkbox.ts +1 -0
  253. package/mobile/cmp/input/DateInput.ts +1 -0
  254. package/mobile/cmp/input/Label.ts +13 -8
  255. package/mobile/cmp/input/NumberInput.ts +11 -2
  256. package/mobile/cmp/input/SearchInput.ts +1 -0
  257. package/mobile/cmp/input/SegmentedControl.scss +36 -6
  258. package/mobile/cmp/input/SegmentedControl.ts +37 -11
  259. package/mobile/cmp/input/Select.ts +2 -0
  260. package/mobile/cmp/input/SwitchInput.ts +1 -0
  261. package/mobile/cmp/input/TextArea.ts +11 -2
  262. package/mobile/cmp/input/TextInput.ts +11 -2
  263. package/mobile/cmp/navigator/NavigatorModel.ts +20 -8
  264. package/mobile/cmp/navigator/PageModel.ts +6 -1
  265. package/mobile/cmp/tab/impl/TabContainer.ts +9 -1
  266. package/package.json +19 -19
  267. package/styles/vars.scss +32 -2
  268. package/svc/InspectorService.ts +2 -0
  269. package/svc/TraceService.ts +5 -2
  270. package/utils/js/LangUtils.ts +37 -5
@@ -16,6 +16,7 @@ import {action, bindable, isObservableProp, makeObservable, runInAction} from '@
16
16
  import {wait} from '@xh/hoist/promise';
17
17
  import {trimToDepth} from '@xh/hoist/utils/js';
18
18
  import {compact, find, forIn, head, without} from 'lodash';
19
+ import {instanceLabel} from '../impl/InspectorUtils';
19
20
  import {StatsModel} from '../stats/StatsModel';
20
21
  import {DiagnosticsModel} from './DiagnosticsModel';
21
22
 
@@ -48,6 +49,9 @@ export class InstancesModel extends HoistModel {
48
49
  @bindable @persist instancesStoreFilter;
49
50
  @bindable @persist propertiesStoreFilter;
50
51
 
52
+ /** Keys of favorited instances (`{className}:{xhName}`) - requires an `xhName` to pin. */
53
+ @bindable.ref @persist favorites: string[] = [];
54
+
51
55
  @bindable @persist instQuickFilters = ['showInGroups'];
52
56
  get showInGroups() {
53
57
  return this.instQuickFilters?.includes('showInGroups');
@@ -55,6 +59,9 @@ export class InstancesModel extends HoistModel {
55
59
  get showXhImpl() {
56
60
  return this.instQuickFilters?.includes('showXhImpl');
57
61
  }
62
+ get favoritesOnly() {
63
+ return this.instQuickFilters?.includes('favoritesOnly');
64
+ }
58
65
 
59
66
  @bindable @persist propQuickFilters = [];
60
67
  get showUnderscoreProps() {
@@ -68,7 +75,9 @@ export class InstancesModel extends HoistModel {
68
75
  }
69
76
 
70
77
  get selectedInstances(): HoistBase[] {
71
- return this.instancesGridModel.selectedIds.map((it: string) => this.getInstance(it));
78
+ return compact(
79
+ this.instancesGridModel.selectedIds.map((it: string) => this.getInstance(it))
80
+ );
72
81
  }
73
82
 
74
83
  constructor() {
@@ -160,6 +169,13 @@ export class InstancesModel extends HoistModel {
160
169
  }
161
170
  }
162
171
 
172
+ toggleFavorite(record: StoreRecord) {
173
+ const key = favoriteKey(record?.data);
174
+ if (!key) return;
175
+ const {favorites} = this;
176
+ this.favorites = favorites.includes(key) ? without(favorites, key) : [...favorites, key];
177
+ }
178
+
163
179
  togglePropsWatchlistItem(record: StoreRecord) {
164
180
  const {instanceXhId, property, isGetter} = record.data,
165
181
  {propsWatchlist} = this,
@@ -195,6 +211,9 @@ export class InstancesModel extends HoistModel {
195
211
  store: {
196
212
  fields: [
197
213
  {name: 'className', type: 'string'},
214
+ {name: 'xhName', type: 'string'},
215
+ {name: 'isFavorite', type: 'bool'},
216
+ {name: 'alive', type: 'bool'},
198
217
  {name: 'displayGroup', type: 'string'},
199
218
  {name: 'created', type: 'date'},
200
219
  {name: 'syncRun', type: 'number'},
@@ -218,12 +237,29 @@ export class InstancesModel extends HoistModel {
218
237
  columns: [
219
238
  {
220
239
  ...actionCol,
221
- width: calcActionColWidth(2),
240
+ width: calcActionColWidth(3),
222
241
  actions: [
223
242
  {
224
243
  icon: Icon.terminal(),
225
244
  tooltip: 'Log to console',
226
- actionFn: ({record}) => this.logInstanceToConsole(record)
245
+ actionFn: ({record}) => this.logInstanceToConsole(record),
246
+ displayFn: ({record}) => ({hidden: !record.data.alive})
247
+ },
248
+ {
249
+ icon: Icon.favorite(),
250
+ actionFn: ({record}) => this.toggleFavorite(record),
251
+ displayFn: ({record}) => {
252
+ const {xhName, isFavorite} = record.data;
253
+ return {
254
+ disabled: !xhName,
255
+ tooltip: xhName
256
+ ? 'Toggle Favorite'
257
+ : 'Set xhName to enable favorites',
258
+ icon: isFavorite
259
+ ? Icon.favorite({intent: 'warning', prefix: 'fas'})
260
+ : Icon.favorite({className: 'xh-text-color-muted'})
261
+ };
262
+ }
227
263
  },
228
264
  {
229
265
  icon: Icon.refresh({intent: 'success'}),
@@ -234,7 +270,11 @@ export class InstancesModel extends HoistModel {
234
270
  }
235
271
  ]
236
272
  },
237
- {field: 'id', displayName: 'xhId'},
273
+ {
274
+ field: 'id',
275
+ displayName: 'xhId',
276
+ renderer: (v, {record}) => (record.data.alive ? v : null)
277
+ },
238
278
  {
239
279
  field: 'syncRun',
240
280
  displayName: 'Sync',
@@ -252,6 +292,8 @@ export class InstancesModel extends HoistModel {
252
292
  renderer: v => (v ? Icon.link() : null)
253
293
  },
254
294
  {field: 'displayGroup', hidden: true},
295
+ {field: 'isFavorite', ...boolCheckCol, hidden: true},
296
+ {field: 'xhName', flex: 1, minWidth: 150},
255
297
  {field: 'className', flex: 1, minWidth: 150},
256
298
  {
257
299
  field: 'lastLoadCompleted',
@@ -263,10 +305,8 @@ export class InstancesModel extends HoistModel {
263
305
  },
264
306
  {field: 'created', align: 'right', renderer: timestampRenderer}
265
307
  ],
266
- rowClassFn: rec => {
267
- return rec?.data.isXhImpl ? 'xh-impl-row' : null;
268
- },
269
- onRowDoubleClicked: ({data: rec}) => this.logInstanceToConsole(rec),
308
+ rowClassFn: rec => (rec?.data.isXhImpl || !rec?.data.alive ? 'xh-impl-row' : null),
309
+ onRowDoubleClicked: ({data: rec}) => rec?.data.alive && this.logInstanceToConsole(rec),
270
310
  xhImpl: true
271
311
  });
272
312
  }
@@ -387,11 +427,15 @@ export class InstancesModel extends HoistModel {
387
427
  private autoLoadInstancesGrid() {
388
428
  this.addAutorun({
389
429
  run: () => {
390
- const {showXhImpl, instancesGridModel, selectedSyncRun} = this,
430
+ const {showXhImpl, favoritesOnly, favorites, instancesGridModel, selectedSyncRun} =
431
+ this,
391
432
  data = [];
392
433
 
393
434
  XH.inspectorService.activeInstances.forEach(inst => {
394
- if (!showXhImpl && inst.isXhImpl) return;
435
+ const isFavorite = favorites.includes(favoriteKey(inst));
436
+
437
+ // A star is an explicit opt-in, so favorites show regardless of the xhImpl filter.
438
+ if (favoritesOnly ? !isFavorite : !showXhImpl && inst.isXhImpl) return;
395
439
  if (selectedSyncRun && inst.syncRun !== selectedSyncRun) return;
396
440
 
397
441
  const displayGroup = inst.isHoistService
@@ -403,9 +447,29 @@ export class InstancesModel extends HoistModel {
403
447
  : inst.isView
404
448
  ? 'Views'
405
449
  : 'Models';
406
- data.push({...inst, displayGroup});
450
+ data.push({...inst, displayGroup, isFavorite, alive: true});
407
451
  });
408
452
 
453
+ // Favorites with no live instance - shown so they can be seen and un-starred.
454
+ if (favoritesOnly && !selectedSyncRun) {
455
+ const aliveKeys = new Set(data.map(favoriteKey));
456
+ favorites
457
+ .filter(key => !aliveKeys.has(key))
458
+ .forEach(key => {
459
+ const sep = key.indexOf(':'),
460
+ className = key.slice(0, sep),
461
+ xhName = key.slice(sep + 1);
462
+ data.push({
463
+ id: key,
464
+ className,
465
+ xhName,
466
+ displayGroup: 'Not Alive',
467
+ isFavorite: true,
468
+ alive: false
469
+ });
470
+ });
471
+ }
472
+
409
473
  instancesGridModel.loadData(data);
410
474
  }
411
475
  });
@@ -482,8 +546,7 @@ export class InstancesModel extends HoistModel {
482
546
  return null;
483
547
 
484
548
  const {xhId} = instance,
485
- ctorName = instance.constructor.name,
486
- instanceDisplayName = `${ctorName} [${xhId}]`,
549
+ instanceDisplayName = instanceLabel(instance),
487
550
  isLoadedGetter = isGetter && this.shouldLoadGetter(xhId, property),
488
551
  v = !isGetter || isLoadedGetter ? instance[property] : null,
489
552
  // Detect FormModel.values Proxy object - throws otherwise on attempt to render in grid.
@@ -554,7 +617,11 @@ export class InstancesModel extends HoistModel {
554
617
  }
555
618
  }
556
619
 
557
- const GROUP_SORT_ORDER = ['Models', 'Services', 'Cubes', 'Views', 'Stores'];
620
+ const GROUP_SORT_ORDER = ['Models', 'Services', 'Cubes', 'Views', 'Stores', 'Not Alive'];
621
+
622
+ /** Persistent identity for Favorites - null when the instance has no `xhName` to pin by. */
623
+ const favoriteKey = (inst: {className?: string; xhName?: string}): string =>
624
+ inst?.xhName ? `${inst.className}:${inst.xhName}` : null;
558
625
 
559
626
  const timestampRenderer = v => fmtDate(v, {fmt: 'HH:mm:ss.SSS'});
560
627
 
@@ -132,6 +132,12 @@ const instanceGridBar = hoistCmp.factory<InstancesModel>(({model}) => {
132
132
  value: 'showXhImpl',
133
133
  tooltip:
134
134
  'Show instances created as part of internal Hoist model/component implementations'
135
+ }),
136
+ button({
137
+ text: 'Favorites',
138
+ value: 'favoritesOnly',
139
+ tooltip:
140
+ 'Show only favorited instances, including any not currently alive. Star any instance with an xhName to favorite it - favorites persist across reloads.'
135
141
  })
136
142
  ]
137
143
  }),
@@ -6,11 +6,11 @@
6
6
  */
7
7
  import {elementFactory} from '@xh/hoist/core';
8
8
  import {ComponentType} from 'react';
9
- import {DayPicker} from 'react-day-picker';
10
- import 'react-day-picker/style.css';
9
+ import {DayPicker} from '@daypicker/react';
10
+ import '@daypicker/react/style.css';
11
11
 
12
12
  export {DayPicker};
13
- export type {DayPickerProps, Matcher} from 'react-day-picker';
13
+ export type {DayPickerProps, Matcher} from '@daypicker/react';
14
14
 
15
15
  // DayPicker's props are a discriminated union over its selection modes. Cast to ComponentType to
16
16
  // keep our element factory ergonomic and produce portable type declarations.
@@ -52,6 +52,7 @@ export const loginPanel = hoistCmp.factory({
52
52
  bind: 'username',
53
53
  leftIcon: Icon.user(),
54
54
  autoComplete: 'username',
55
+ enablePasswordManagers: true,
55
56
  autoCapitalize: 'none',
56
57
  commitOnChange: true,
57
58
  testId: 'xh-login-username'
@@ -64,6 +65,7 @@ export const loginPanel = hoistCmp.factory({
64
65
  leftIcon: Icon.lock(),
65
66
  type: 'password',
66
67
  autoComplete: 'current-password',
68
+ enablePasswordManagers: true,
67
69
  commitOnChange: true,
68
70
  testId: 'xh-login-password'
69
71
  })
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import {hspacer} from '@xh/hoist/cmp/layout';
8
8
  import {
9
+ DomAttrsProps,
9
10
  LayoutProps,
10
11
  StyleProps,
11
12
  hoistCmp,
@@ -23,7 +24,7 @@ import {ReactNode, ReactElement, MouseEvent} from 'react';
23
24
  import './Button.scss';
24
25
 
25
26
  export interface ButtonProps<M extends HoistModel = HoistModel>
26
- extends HoistProps<M>, TestSupportProps, LayoutProps, StyleProps {
27
+ extends HoistProps<M>, TestSupportProps, DomAttrsProps, LayoutProps, StyleProps {
27
28
  active?: boolean;
28
29
  disabled?: boolean;
29
30
  icon?: ReactElement;
@@ -51,8 +52,19 @@ export const [Button, button] = hoistCmp.withFactory<ButtonProps>({
51
52
  classes = [],
52
53
  items = [];
53
54
 
54
- const {active, className, disabled, icon, intent, onClick, style, testId, text, ...rest} =
55
- nonLayoutProps;
55
+ const {
56
+ active,
57
+ className,
58
+ disabled,
59
+ domAttrs,
60
+ icon,
61
+ intent,
62
+ onClick,
63
+ style,
64
+ testId,
65
+ text,
66
+ ...rest
67
+ } = nonLayoutProps;
56
68
 
57
69
  let {outlined, minimal} = nonLayoutProps;
58
70
  if (disabled) {
@@ -91,6 +103,7 @@ export const [Button, button] = hoistCmp.withFactory<ButtonProps>({
91
103
  ...layoutProps
92
104
  },
93
105
  [TEST_ID]: testId,
106
+ ...domAttrs,
94
107
  ...rest
95
108
  });
96
109
  }
@@ -160,6 +160,7 @@ export const [FormField, formField] = hoistCmp.withFactory<FormFieldProps>({
160
160
  return box({
161
161
  ref,
162
162
  testId,
163
+ domAttrs: props.domAttrs,
163
164
  className: classNames(className, classes),
164
165
  ...layoutProps,
165
166
  items: [
@@ -46,6 +46,7 @@ const cmp = hoistCmp.factory<CheckboxInputModel>(({model, className, ...props},
46
46
 
47
47
  style: props.style,
48
48
  [TEST_ID]: props.testId,
49
+ ...props.domAttrs,
49
50
 
50
51
  onBlur: model.onBlur,
51
52
  onFocus: model.onFocus,
@@ -163,6 +163,7 @@ const cmp = hoistCmp.factory<DateInputModel>(({model, className, ...props}, ref)
163
163
  style: {textAlign},
164
164
  tabIndex: props.tabIndex,
165
165
  [TEST_ID]: props.testId,
166
+ ...props.domAttrs,
166
167
  item: model.formattedRenderValue,
167
168
  onClick: () => model.openPicker()
168
169
  }),
@@ -8,6 +8,7 @@ import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cm
8
8
  import {div} from '@xh/hoist/cmp/layout';
9
9
  import {hoistCmp, HoistProps, StyleProps} from '@xh/hoist/core';
10
10
  import '@xh/hoist/mobile/register';
11
+ import {TEST_ID} from '@xh/hoist/utils/js';
11
12
  import './Label.scss';
12
13
 
13
14
  export interface LabelProps extends HoistProps, HoistInputProps, StyleProps {}
@@ -30,11 +31,15 @@ class LabelInputModel extends HoistInputModel {
30
31
  //-----------------------
31
32
  // Implementation
32
33
  //-----------------------
33
- const cmp = hoistCmp.factory(({model, className, style, width, children}, ref) => {
34
- return div({
35
- className,
36
- style: {...style, whiteSpace: 'nowrap', width},
37
- items: children,
38
- ref
39
- });
40
- });
34
+ const cmp = hoistCmp.factory(
35
+ ({model, className, style, width, children, testId, domAttrs}, ref) => {
36
+ return div({
37
+ className,
38
+ [TEST_ID]: testId,
39
+ ...domAttrs,
40
+ style: {...style, whiteSpace: 'nowrap', width},
41
+ items: children,
42
+ ref
43
+ });
44
+ }
45
+ );
@@ -4,7 +4,13 @@
4
4
  *
5
5
  * Copyright © 2026 Extremely Heavy Industries Inc.
6
6
  */
7
- import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
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 {fmtNumber, NumericPrecision, Precision, ZeroPad} from '@xh/hoist/format';
10
16
  import {input} from '@xh/hoist/kit/onsen';
@@ -16,7 +22,8 @@ import type {Property} from 'csstype';
16
22
  import {debounce, isNaN, isNil, isNumber, round} from 'lodash';
17
23
  import './NumberInput.scss';
18
24
 
19
- export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProps, LayoutProps {
25
+ export interface NumberInputProps
26
+ extends HoistProps, HoistInputProps, PasswordManagerSupportProps, StyleProps, LayoutProps {
20
27
  value?: number;
21
28
 
22
29
  /** True to commit on every change/keystroke, default false. */
@@ -257,6 +264,8 @@ const cmp = hoistCmp.factory<NumberInputModel>(
257
264
  },
258
265
  spellCheck: false,
259
266
  [TEST_ID]: props.testId,
267
+ ...getPasswordManagerAttrs(props.enablePasswordManagers),
268
+ ...props.domAttrs,
260
269
 
261
270
  onInput: model.onValueChange,
262
271
  onKeyDown: model.onKeyDown,
@@ -94,6 +94,7 @@ const cmp = hoistCmp.factory<SearchInputModel>(({model, className, ...props}, re
94
94
  textAlign: withDefault(props.textAlign, 'left')
95
95
  },
96
96
  [TEST_ID]: props.testId,
97
+ ...props.domAttrs,
97
98
 
98
99
  onInput: model.onChange,
99
100
  onKeyDown: model.onKeyDown,
@@ -86,21 +86,51 @@
86
86
  flex-basis: 0;
87
87
  }
88
88
 
89
- // Outlined mode - border around the tray with no inner background fill.
89
+ // Outlined mode (default) - a border around the tray. Independent of the tray fill, which is
90
+ // governed by --no-tray-bg below.
90
91
  &.xh-segmented-control--outlined {
91
- background-color: transparent;
92
92
  border: 1px solid var(--xh-border-color);
93
+ }
94
+
95
+ // Tray background suppressed - a lighter presentation, with no fill behind the options.
96
+ &.xh-segmented-control--no-tray-bg {
97
+ background-color: transparent;
93
98
 
94
- // Reserve border space on every option so selecting one does not shift the row. The
95
- // transparent tray offers no recessed backdrop to set a filled tile against, so indicate
96
- // selection with a ring instead - currentColor keeps it in step with the option's
97
- // text / intent color.
99
+ // Reserve border space on every option so selecting one does not shift the row. With no
100
+ // tray there is no recessed backdrop to set a filled tile against, so indicate selection
101
+ // with a ring instead - currentColor keeps it in step with the option's text / intent color.
98
102
  .xh-button.xh-segmented-control-option {
99
103
  border: 1px solid transparent;
100
104
  }
101
105
 
102
106
  .xh-button.xh-segmented-control-option--selected {
103
107
  border-color: currentColor;
108
+ background-color: transparent;
109
+ }
110
+ }
111
+
112
+ // Option dividers - internal structure for a tray whose options are otherwise undifferentiated,
113
+ // most notably in the empty state where no selected option breaks up the row. Drawn in the gap
114
+ // to the left of each option after the first, and suppressed on either side of the selected
115
+ // option so they never crowd it.
116
+ &.xh-segmented-control--dividers {
117
+ .xh-button.xh-segmented-control-option {
118
+ position: relative;
119
+ }
120
+
121
+ .xh-segmented-control-option + .xh-segmented-control-option::before {
122
+ content: '';
123
+ position: absolute;
124
+ left: calc(var(--xh-segmented-control-pad-px) / -2);
125
+ top: 20%;
126
+ bottom: 20%;
127
+ width: 1px;
128
+ background-color: var(--xh-border-color);
129
+ }
130
+
131
+ .xh-segmented-control-option--selected::before,
132
+ .xh-segmented-control-option--selected + .xh-segmented-control-option::before {
133
+ display: none;
104
134
  }
105
135
  }
106
136
 
@@ -17,7 +17,6 @@ import {hoistCmp, HoistProps, Intent} from '@xh/hoist/core';
17
17
  import {button} from '@xh/hoist/mobile/cmp/button';
18
18
  import '@xh/hoist/mobile/register';
19
19
  import {computed, makeObservable} from '@xh/hoist/mobx';
20
- import {TEST_ID} from '@xh/hoist/utils/js';
21
20
  import {getLayoutProps, getNonLayoutProps} from '@xh/hoist/utils/react';
22
21
  import classNames from 'classnames';
23
22
  import {filter, isObject} from 'lodash';
@@ -34,8 +33,8 @@ export interface SegmentedControlProps extends HoistProps, HoistInputProps {
34
33
  equalSegmentWidths?: boolean;
35
34
 
36
35
  /**
37
- * True (default) to stretch the control to fill available width,
38
- * distributing space equally among options.
36
+ * True (default) to stretch the control to fill available width. Set false to size the control
37
+ * to its options. See `equalSegmentWidths` for how the filled width is divided up.
39
38
  */
40
39
  fill?: boolean;
41
40
 
@@ -54,10 +53,26 @@ export interface SegmentedControlProps extends HoistProps, HoistInputProps {
54
53
  options: Array<SegmentedControlOption | SegmentedControlNullOption | OptionPrimitive>;
55
54
 
56
55
  /**
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.
56
+ * True (default) to render a border around the control tray. Border color follows the current
57
+ * intent. Note this controls the tray border only - it does not remove the tray background,
58
+ * which is governed independently by `showTrayBackground`.
59
59
  */
60
60
  outlined?: boolean;
61
+
62
+ /**
63
+ * True (default) to fill the control tray with a background, setting it off from the surface
64
+ * behind it. Set false to omit that fill for a lighter presentation. With no tray to set a
65
+ * filled tile against, the selected option is indicated with a ring instead.
66
+ */
67
+ showTrayBackground?: boolean;
68
+
69
+ /**
70
+ * True to render dividers between options, adding internal structure to the tray. Dividers
71
+ * are suppressed on either side of the selected option so they do not compete with it.
72
+ * Defaults to 'auto', showing dividers only while no option is selected - the state in which
73
+ * the tray offers no other cue that its options are separate and selectable.
74
+ */
75
+ showOptionDividers?: boolean | 'auto';
61
76
  }
62
77
 
63
78
  /**
@@ -114,11 +129,14 @@ class SegmentedControlModel extends HoistInputModel {
114
129
  });
115
130
  }
116
131
 
117
- /** Map the current render value to the string key used to identify the selected option. */
132
+ /**
133
+ * Key of the option matching the current render value, or null if no option matches -
134
+ * including whenever the bound value is itself null.
135
+ */
118
136
  @computed
119
- get selectedKey(): string {
137
+ get selectedKey(): string | null {
120
138
  const {renderValue, normalizedOptions} = this;
121
- return normalizedOptions.find(o => o.value === renderValue)?._key;
139
+ return normalizedOptions.find(o => o.value === renderValue)?._key ?? null;
122
140
  }
123
141
 
124
142
  get enabledButtons(): HTMLButtonElement[] {
@@ -160,13 +178,18 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
160
178
  equalSegmentWidths = true,
161
179
  fill = true,
162
180
  intent,
163
- outlined,
181
+ outlined = true,
182
+ showTrayBackground = true,
183
+ showOptionDividers = 'auto',
164
184
  testId,
185
+ domAttrs,
165
186
  ...rest
166
187
  } = getNonLayoutProps(props);
167
188
 
168
189
  const {selectedKey} = model,
169
- defaultIntent = intent && intent !== 'none' ? intent : null;
190
+ defaultIntent = intent && intent !== 'none' ? intent : null,
191
+ // 'auto' dividers track the empty state - shown only while nothing is selected.
192
+ dividers = showOptionDividers === 'auto' ? selectedKey == null : showOptionDividers;
170
193
 
171
194
  const buttons = model.normalizedOptions.map(opt => {
172
195
  const optIntent = opt.intent ?? defaultIntent,
@@ -200,6 +223,8 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
200
223
  className,
201
224
  defaultIntent && `xh-segmented-control--${defaultIntent}`,
202
225
  outlined && 'xh-segmented-control--outlined',
226
+ !showTrayBackground && 'xh-segmented-control--no-tray-bg',
227
+ dividers && 'xh-segmented-control--dividers',
203
228
  fill && 'xh-segmented-control--fill',
204
229
  fill && equalSegmentWidths && 'xh-segmented-control--equal-widths'
205
230
  ),
@@ -207,7 +232,8 @@ const cmp = hoistCmp.factory<SegmentedControlModel>(({model, className, ...props
207
232
  onFocus: model.onFocus,
208
233
  onBlur: model.onBlur,
209
234
  ...getLayoutProps(props),
210
- [TEST_ID]: testId,
235
+ testId,
236
+ domAttrs,
211
237
  items: buttons,
212
238
  ...rest
213
239
  });
@@ -696,6 +696,7 @@ const cmp = hoistCmp.factory<SelectInputModel>(({model, className, ...props}, re
696
696
  item: factory(rsProps),
697
697
  className,
698
698
  testId: props.testId,
699
+ domAttrs: props.domAttrs,
699
700
  ref
700
701
  })
701
702
  }),
@@ -708,6 +709,7 @@ const cmp = hoistCmp.factory<SelectInputModel>(({model, className, ...props}, re
708
709
  ...layoutProps,
709
710
  width: withDefault(width, null),
710
711
  testId: props.testId,
712
+ domAttrs: props.domAttrs,
711
713
  ref
712
714
  });
713
715
  }
@@ -47,6 +47,7 @@ const cmp = hoistCmp.factory<SwitchInputModel>(({model, className, ...props}, re
47
47
  className,
48
48
  style: props.style,
49
49
  [TEST_ID]: props.testId,
50
+ ...props.domAttrs,
50
51
 
51
52
  onBlur: model.onBlur,
52
53
  onFocus: model.onFocus,
@@ -4,7 +4,13 @@
4
4
  *
5
5
  * Copyright © 2026 Extremely Heavy Industries Inc.
6
6
  */
7
- import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
7
+ import {
8
+ getPasswordManagerAttrs,
9
+ HoistInputModel,
10
+ HoistInputProps,
11
+ PasswordManagerSupportProps,
12
+ useHoistInputModel
13
+ } from '@xh/hoist/cmp/input';
8
14
  import {div, textarea as textareaTag} from '@xh/hoist/cmp/layout';
9
15
  import {hoistCmp, HoistProps, LayoutProps, StyleProps} from '@xh/hoist/core';
10
16
  import '@xh/hoist/mobile/register';
@@ -12,7 +18,8 @@ import {TEST_ID, withDefault} from '@xh/hoist/utils/js';
12
18
  import {getLayoutProps} from '@xh/hoist/utils/react';
13
19
  import './TextArea.scss';
14
20
 
15
- export interface TextAreaProps extends HoistProps, HoistInputProps, StyleProps, LayoutProps {
21
+ export interface TextAreaProps
22
+ extends HoistProps, HoistInputProps, PasswordManagerSupportProps, StyleProps, LayoutProps {
16
23
  value?: string;
17
24
 
18
25
  /** True to commit on every change/keystroke, default false. */
@@ -85,6 +92,8 @@ const cmp = hoistCmp.factory<TextAreaInputModel>(({model, className, ...props},
85
92
  spellCheck: withDefault(props.spellCheck, false),
86
93
  tabIndex: props.tabIndex,
87
94
  [TEST_ID]: props.testId,
95
+ ...getPasswordManagerAttrs(props.enablePasswordManagers),
96
+ ...props.domAttrs,
88
97
 
89
98
  onChange: model.onChange,
90
99
  onKeyDown: model.onKeyDown,
@@ -4,7 +4,13 @@
4
4
  *
5
5
  * Copyright © 2026 Extremely Heavy Industries Inc.
6
6
  */
7
- import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
7
+ import {
8
+ getPasswordManagerAttrs,
9
+ HoistInputModel,
10
+ HoistInputProps,
11
+ PasswordManagerSupportProps,
12
+ useHoistInputModel
13
+ } from '@xh/hoist/cmp/input';
8
14
  import {box, hbox} from '@xh/hoist/cmp/layout';
9
15
  import {hoistCmp, HoistProps, LayoutProps, StyleProps} from '@xh/hoist/core';
10
16
  import {Icon} from '@xh/hoist/icon';
@@ -18,7 +24,8 @@ import {isEmpty} from 'lodash';
18
24
  import {ReactElement} from 'react';
19
25
  import './TextInput.scss';
20
26
 
21
- export interface TextInputProps extends HoistProps, HoistInputProps, StyleProps, LayoutProps {
27
+ export interface TextInputProps
28
+ extends HoistProps, HoistInputProps, PasswordManagerSupportProps, StyleProps, LayoutProps {
22
29
  value?: string;
23
30
 
24
31
  /**
@@ -145,6 +152,8 @@ const cmp = hoistCmp.factory<TextInputModel>(({model, className, ...props}, ref)
145
152
  className: 'xh-text-input__input',
146
153
  style: {textAlign: withDefault(props.textAlign, 'left')},
147
154
  [TEST_ID]: props.testId,
155
+ ...getPasswordManagerAttrs(props.enablePasswordManagers),
156
+ ...props.domAttrs,
148
157
 
149
158
  onInput: model.onChange,
150
159
  onKeyDown: model.onKeyDown,