@xh/hoist 87.1.1 → 87.2.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 (263) hide show
  1. package/.npmignore +39 -0
  2. package/CHANGELOG.md +92 -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/grouping/GroupingChooserModel.d.ts +3 -1
  86. package/build/types/cmp/input/HoistInputProps.d.ts +49 -2
  87. package/build/types/cmp/tab/TabContainerModel.d.ts +3 -1
  88. package/build/types/cmp/tab/TabModel.d.ts +3 -1
  89. package/build/types/cmp/treemap/TreeMapModel.d.ts +2 -0
  90. package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +2 -0
  91. package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +2 -0
  92. package/build/types/core/HoistBase.d.ts +7 -0
  93. package/build/types/core/HoistProps.d.ts +26 -1
  94. package/build/types/core/model/RootRefreshContextModel.d.ts +1 -0
  95. package/build/types/core/types/Telemetry.d.ts +1 -1
  96. package/build/types/data/Store.d.ts +3 -1
  97. package/build/types/data/StoreSelectionModel.d.ts +3 -1
  98. package/build/types/data/cube/Cube.d.ts +5 -2
  99. package/build/types/data/cube/View.d.ts +2 -0
  100. package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +3 -0
  101. package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +3 -0
  102. package/build/types/desktop/cmp/button/Button.d.ts +2 -2
  103. package/build/types/desktop/cmp/button/ButtonGroup.d.ts +2 -2
  104. package/build/types/desktop/cmp/dash/DashConfig.d.ts +2 -0
  105. package/build/types/desktop/cmp/dash/DashViewModel.d.ts +3 -1
  106. package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +1 -1
  107. package/build/types/desktop/cmp/dash/container/DashContainerModel.d.ts +1 -1
  108. package/build/types/desktop/cmp/daterange/DateRangePicker.d.ts +66 -0
  109. package/build/types/desktop/cmp/daterange/impl/CustomTab.d.ts +3 -0
  110. package/build/types/desktop/cmp/daterange/impl/DateRangePickerLocalModel.d.ts +121 -0
  111. package/build/types/desktop/cmp/daterange/impl/PeriodTab.d.ts +2 -0
  112. package/build/types/desktop/cmp/daterange/impl/PresetsTab.d.ts +3 -0
  113. package/build/types/desktop/cmp/daterange/impl/RelativeTab.d.ts +3 -0
  114. package/build/types/desktop/cmp/daterange/impl/TabUtils.d.ts +21 -0
  115. package/build/types/desktop/cmp/daterange/index.d.ts +2 -0
  116. package/build/types/desktop/cmp/dock/DockContainerModel.d.ts +3 -1
  117. package/build/types/desktop/cmp/dock/DockViewModel.d.ts +3 -1
  118. package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +4 -1
  119. package/build/types/desktop/cmp/input/IntentInput.d.ts +38 -0
  120. package/build/types/desktop/cmp/input/NumberInput.d.ts +2 -2
  121. package/build/types/desktop/cmp/input/SegmentedControl.d.ts +25 -4
  122. package/build/types/desktop/cmp/input/TextArea.d.ts +2 -2
  123. package/build/types/desktop/cmp/input/TextInput.d.ts +2 -2
  124. package/build/types/desktop/cmp/input/index.d.ts +1 -0
  125. package/build/types/desktop/cmp/leftrightchooser/LeftRightChooserModel.d.ts +3 -1
  126. package/build/types/desktop/cmp/panel/PanelModel.d.ts +3 -1
  127. package/build/types/desktop/cmp/rest/RestGridModel.d.ts +1 -1
  128. package/build/types/desktop/cmp/tab/impl/TabContainer.d.ts +1 -1
  129. package/build/types/icon/Icon.d.ts +3 -0
  130. package/build/types/inspector/impl/InspectorUtils.d.ts +3 -0
  131. package/build/types/inspector/instances/InstancesModel.d.ts +4 -0
  132. package/build/types/kit/react-day-picker/index.d.ts +3 -3
  133. package/build/types/mobile/cmp/button/Button.d.ts +2 -2
  134. package/build/types/mobile/cmp/input/NumberInput.d.ts +2 -2
  135. package/build/types/mobile/cmp/input/SegmentedControl.d.ts +18 -4
  136. package/build/types/mobile/cmp/input/TextArea.d.ts +2 -2
  137. package/build/types/mobile/cmp/input/TextInput.d.ts +2 -2
  138. package/build/types/mobile/cmp/navigator/NavigatorModel.d.ts +3 -1
  139. package/build/types/mobile/cmp/navigator/PageModel.d.ts +3 -1
  140. package/build/types/mobile/cmp/tab/impl/TabContainer.d.ts +1 -1
  141. package/build/types/svc/InspectorService.d.ts +1 -0
  142. package/build/types/utils/js/LangUtils.d.ts +24 -5
  143. package/cmp/README.md +1 -0
  144. package/cmp/ag-grid/AgGridModel.ts +5 -0
  145. package/cmp/badge/Badge.ts +5 -3
  146. package/cmp/card/Card.ts +4 -3
  147. package/cmp/chart/ChartModel.ts +11 -1
  148. package/cmp/dataview/DataViewModel.ts +7 -1
  149. package/cmp/daterange/DateRangePickerModel.ts +628 -0
  150. package/cmp/daterange/DateRangePresets.ts +274 -0
  151. package/cmp/daterange/DateRangeUtils.ts +557 -0
  152. package/cmp/daterange/README.md +434 -0
  153. package/cmp/daterange/Types.ts +203 -0
  154. package/cmp/daterange/index.ts +10 -0
  155. package/cmp/filter/FilterChooserModel.ts +7 -4
  156. package/cmp/form/FormModel.ts +6 -0
  157. package/cmp/form/README.md +1 -0
  158. package/cmp/grid/GridModel.ts +24 -4
  159. package/cmp/grid/Types.ts +3 -0
  160. package/cmp/grid/filter/GridFilterModel.ts +3 -1
  161. package/cmp/grouping/GroupingChooserModel.ts +6 -1
  162. package/cmp/input/HoistInputProps.ts +50 -2
  163. package/cmp/input/README.md +42 -0
  164. package/cmp/layout/Box.ts +3 -2
  165. package/cmp/tab/TabContainerModel.ts +9 -1
  166. package/cmp/tab/TabModel.ts +5 -0
  167. package/cmp/treemap/SplitTreeMapModel.ts +12 -2
  168. package/cmp/treemap/TreeMapModel.ts +6 -1
  169. package/cmp/viewmanager/ViewManagerModel.ts +6 -1
  170. package/cmp/zoneGrid/ZoneGridModel.ts +7 -0
  171. package/core/HoistBase.ts +11 -0
  172. package/core/HoistProps.ts +27 -0
  173. package/core/README.md +46 -0
  174. package/core/impl/InstallServices.ts +5 -1
  175. package/core/model/RootRefreshContextModel.ts +2 -0
  176. package/core/types/Telemetry.ts +1 -1
  177. package/data/README.md +3 -1
  178. package/data/Store.ts +7 -0
  179. package/data/StoreSelectionModel.ts +5 -1
  180. package/data/cube/Cube.ts +12 -3
  181. package/data/cube/View.ts +5 -1
  182. package/desktop/README.md +2 -0
  183. package/desktop/appcontainer/LoginPanel.ts +2 -0
  184. package/desktop/cmp/button/Button.scss +71 -42
  185. package/desktop/cmp/button/Button.ts +10 -1
  186. package/desktop/cmp/button/ButtonGroup.ts +4 -1
  187. package/desktop/cmp/button/grid/ColChooserButton.ts +9 -1
  188. package/desktop/cmp/button/zoneGrid/ZoneMapperButton.ts +1 -0
  189. package/desktop/cmp/dash/DashConfig.ts +3 -0
  190. package/desktop/cmp/dash/DashViewModel.ts +14 -1
  191. package/desktop/cmp/dash/canvas/DashCanvasModel.ts +3 -1
  192. package/desktop/cmp/dash/container/DashContainerModel.ts +3 -1
  193. package/desktop/cmp/daterange/DateRangePicker.scss +577 -0
  194. package/desktop/cmp/daterange/DateRangePicker.ts +357 -0
  195. package/desktop/cmp/daterange/impl/CustomTab.ts +165 -0
  196. package/desktop/cmp/daterange/impl/DateRangePickerLocalModel.ts +482 -0
  197. package/desktop/cmp/daterange/impl/PeriodTab.ts +138 -0
  198. package/desktop/cmp/daterange/impl/PresetsTab.ts +58 -0
  199. package/desktop/cmp/daterange/impl/RelativeTab.ts +108 -0
  200. package/desktop/cmp/daterange/impl/TabUtils.ts +34 -0
  201. package/desktop/cmp/daterange/index.ts +8 -0
  202. package/desktop/cmp/dock/DockContainerModel.ts +6 -1
  203. package/desktop/cmp/dock/DockViewModel.ts +6 -1
  204. package/desktop/cmp/filechooser/FileChooser.ts +4 -0
  205. package/desktop/cmp/form/FormField.ts +1 -0
  206. package/desktop/cmp/grouping/GroupingChooser.ts +8 -2
  207. package/desktop/cmp/input/Checkbox.ts +1 -0
  208. package/desktop/cmp/input/CodeInput.ts +1 -0
  209. package/desktop/cmp/input/DateInput.scss +1 -1
  210. package/desktop/cmp/input/DateInput.ts +1 -0
  211. package/desktop/cmp/input/IntentInput.scss +202 -0
  212. package/desktop/cmp/input/IntentInput.ts +213 -0
  213. package/desktop/cmp/input/NumberInput.ts +11 -2
  214. package/desktop/cmp/input/Picker.ts +1 -0
  215. package/desktop/cmp/input/RadioInput.ts +2 -1
  216. package/desktop/cmp/input/SegmentedControl.scss +45 -2
  217. package/desktop/cmp/input/SegmentedControl.ts +66 -11
  218. package/desktop/cmp/input/Select.ts +9 -7
  219. package/desktop/cmp/input/Slider.ts +2 -0
  220. package/desktop/cmp/input/SwitchInput.ts +1 -0
  221. package/desktop/cmp/input/TextArea.ts +11 -2
  222. package/desktop/cmp/input/TextInput.ts +11 -2
  223. package/desktop/cmp/input/index.ts +1 -0
  224. package/desktop/cmp/leftrightchooser/LeftRightChooserModel.ts +5 -0
  225. package/desktop/cmp/panel/Panel.ts +8 -5
  226. package/desktop/cmp/panel/PanelModel.ts +8 -0
  227. package/desktop/cmp/panel/impl/ResizeContainer.ts +2 -1
  228. package/desktop/cmp/rest/RestGridModel.ts +4 -0
  229. package/desktop/cmp/tab/impl/TabContainer.ts +2 -0
  230. package/docs/README.md +2 -0
  231. package/docs/build-and-publish.md +23 -7
  232. package/docs/doc-registry.json +8 -0
  233. package/docs/version-compatibility.md +2 -0
  234. package/icon/Icon.ts +9 -0
  235. package/icon/index.ts +24 -0
  236. package/inspector/README.md +8 -2
  237. package/inspector/impl/InspectorUtils.ts +12 -0
  238. package/inspector/instances/DiagnosticsModel.ts +3 -2
  239. package/inspector/instances/InstancesModel.ts +81 -14
  240. package/inspector/instances/InstancesPanel.ts +6 -0
  241. package/kit/react-day-picker/index.ts +3 -3
  242. package/mobile/appcontainer/LoginPanel.ts +2 -0
  243. package/mobile/cmp/button/Button.ts +16 -3
  244. package/mobile/cmp/form/FormField.ts +1 -0
  245. package/mobile/cmp/input/Checkbox.ts +1 -0
  246. package/mobile/cmp/input/DateInput.ts +1 -0
  247. package/mobile/cmp/input/Label.ts +13 -8
  248. package/mobile/cmp/input/NumberInput.ts +11 -2
  249. package/mobile/cmp/input/SearchInput.ts +1 -0
  250. package/mobile/cmp/input/SegmentedControl.scss +36 -6
  251. package/mobile/cmp/input/SegmentedControl.ts +37 -11
  252. package/mobile/cmp/input/Select.ts +2 -0
  253. package/mobile/cmp/input/SwitchInput.ts +1 -0
  254. package/mobile/cmp/input/TextArea.ts +11 -2
  255. package/mobile/cmp/input/TextInput.ts +11 -2
  256. package/mobile/cmp/navigator/NavigatorModel.ts +6 -1
  257. package/mobile/cmp/navigator/PageModel.ts +6 -1
  258. package/mobile/cmp/tab/impl/TabContainer.ts +9 -1
  259. package/package.json +19 -19
  260. package/styles/vars.scss +32 -2
  261. package/svc/InspectorService.ts +2 -0
  262. package/svc/TraceService.ts +5 -2
  263. package/utils/js/LangUtils.ts +37 -5
@@ -0,0 +1,108 @@
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 {DATE_RANGE_UNITS, getDateRangeUnitLabel} from '@xh/hoist/cmp/daterange';
8
+ import type {DateRangePickerLocalModel} from './DateRangePickerLocalModel';
9
+ import {div, hbox, span, vbox} from '@xh/hoist/cmp/layout';
10
+ import {hoistCmp} from '@xh/hoist/core';
11
+ import {button} from '@xh/hoist/desktop/cmp/button';
12
+ import {numberInput, segmentedControl} from '@xh/hoist/desktop/cmp/input';
13
+ import {controlGroup} from '@xh/hoist/kit/blueprint';
14
+ import {getTestId, TEST_ID} from '@xh/hoist/utils/js';
15
+ import {sectionHeader} from './TabUtils';
16
+
17
+ /** Relative tab - a lookback of N units ending on the anchor date, drafted until applied. */
18
+ export const relativeTab = hoistCmp.factory<DateRangePickerLocalModel>(({model, testId}) => {
19
+ const {parentModel, relativeRange} = model;
20
+
21
+ return vbox({
22
+ className: 'xh-date-range-picker-popover__tab',
23
+ items: [
24
+ sectionHeader('Relative Lookback'),
25
+ hbox({
26
+ className: 'xh-date-range-picker-popover__rel-row',
27
+ items: [
28
+ span({className: 'xh-date-range-picker-popover__rel-prefix', item: 'Prev'}),
29
+ controlGroup({
30
+ items: [
31
+ button({
32
+ text: '−',
33
+ minWidth: 20,
34
+ testId: getTestId(testId, 'count-down'),
35
+ onClick: () => model.stepCount(-1)
36
+ }),
37
+ numberInput({
38
+ model,
39
+ bind: 'relativeCount',
40
+ min: 1,
41
+ max: 999,
42
+ width: 56,
43
+ textAlign: 'center',
44
+ commitOnChange: true,
45
+ testId: getTestId(testId, 'count')
46
+ }),
47
+ button({
48
+ text: '+',
49
+ minWidth: 20,
50
+ testId: getTestId(testId, 'count-up'),
51
+ onClick: () => model.stepCount(1)
52
+ })
53
+ ]
54
+ }),
55
+ segmentedControl({
56
+ model,
57
+ bind: 'relativeUnit',
58
+ testId: getTestId(testId, 'unit'),
59
+ options: DATE_RANGE_UNITS.map(unit => ({
60
+ value: unit,
61
+ label: getDateRangeUnitLabel(unit)
62
+ }))
63
+ })
64
+ ]
65
+ }),
66
+ div({
67
+ className: 'xh-date-range-picker-popover__snap-box',
68
+ items: [
69
+ // Two ways of counting, named as peers rather than as a checkbox modifier.
70
+ // Days have nothing to round out, so no choice at that grain.
71
+ segmentedControl({
72
+ omit: !model.snapApplies,
73
+ model,
74
+ bind: 'relativeSnap',
75
+ fill: false,
76
+ testId: getTestId(testId, 'snap'),
77
+ options: [
78
+ {value: false, label: 'Rolling'},
79
+ {value: true, label: 'Calendar'}
80
+ ]
81
+ }),
82
+ div({
83
+ className: 'xh-date-range-picker-popover__snap-help',
84
+ [TEST_ID]: getTestId(testId, 'snap-help'),
85
+ item: model.relativeModeHelpText
86
+ })
87
+ ]
88
+ }),
89
+ div({
90
+ className: 'xh-date-range-picker-popover__preview',
91
+ items: [
92
+ div({
93
+ className: 'xh-date-range-picker-popover__preview-label',
94
+ item: 'Resolves To'
95
+ }),
96
+ div({
97
+ className: 'xh-date-range-picker-popover__preview-range',
98
+ item: parentModel.fmtRange(relativeRange)
99
+ }),
100
+ div({
101
+ className: 'xh-date-range-picker-popover__preview-days',
102
+ item: model.relativePreviewCountText
103
+ })
104
+ ]
105
+ })
106
+ ]
107
+ });
108
+ });
@@ -0,0 +1,34 @@
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 {div} from '@xh/hoist/cmp/layout';
8
+ import type {KeyboardEvent} from 'react';
9
+
10
+ export const sectionHeader = (text: string) =>
11
+ div({className: 'xh-date-range-picker-popover__section-hdr', item: text});
12
+
13
+ /** Props making a styled div act as a keyboard-accessible button. Pass null to disable. */
14
+ export const clickable = (onClick: () => void) =>
15
+ onClick
16
+ ? {
17
+ role: 'button',
18
+ tabIndex: 0,
19
+ onClick,
20
+ onKeyDown: (e: KeyboardEvent) => {
21
+ if (e.key === 'Enter' || e.key === ' ') {
22
+ e.preventDefault();
23
+ e.stopPropagation();
24
+ onClick();
25
+ }
26
+ }
27
+ }
28
+ : {role: 'button', 'aria-disabled': true};
29
+
30
+ /**
31
+ * Spread arbitrary `data-*` attributes onto a plain element spec. Needed only for attributes
32
+ * `ElementSpec` does not declare - `data-testid` is declared, so pass that as `[TEST_ID]` directly.
33
+ */
34
+ export const dataAttrs = (attrs: Record<string, string>) => attrs as object;
@@ -0,0 +1,8 @@
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
+ export * from './DateRangePicker';
8
+ export * from '@xh/hoist/cmp/daterange';
@@ -20,6 +20,9 @@ interface DockContainerConfig {
20
20
  renderMode?: RenderMode;
21
21
  /** Strategy for refreshing DockViews. Can also be set per-view via `DockViewModelConfig.refreshMode` */
22
22
  refreshMode?: RefreshMode;
23
+
24
+ /** See {@link HoistBase.xhName}. */
25
+ xhName?: string;
23
26
  }
24
27
 
25
28
  /**
@@ -38,10 +41,12 @@ export class DockContainerModel extends HoistModel {
38
41
  views = [],
39
42
  direction = 'rtl',
40
43
  renderMode = 'lazy',
41
- refreshMode = 'onShowLazy'
44
+ refreshMode = 'onShowLazy',
45
+ xhName = null
42
46
  }: DockContainerConfig = {}) {
43
47
  super();
44
48
  makeObservable(this);
49
+ this.xhName = xhName;
45
50
  views = views.filter(v => !isOmitted(v));
46
51
 
47
52
  ensureUniqueBy(views as [], 'id', 'Multiple DockContainerModel views have the same id.');
@@ -66,6 +66,9 @@ export interface DockViewConfig {
66
66
  allowDialog?: boolean;
67
67
  /** Awaitable callback invoked on close. Return false to prevent close. */
68
68
  onClose?: () => Awaitable<boolean | void>;
69
+
70
+ /** See {@link HoistBase.xhName}. */
71
+ xhName?: string;
69
72
  }
70
73
 
71
74
  /**
@@ -128,7 +131,8 @@ export class DockViewModel extends HoistModel {
128
131
  collapsed = false,
129
132
  allowClose = true,
130
133
  allowDialog = true,
131
- onClose
134
+ onClose,
135
+ xhName = null
132
136
  }: DockViewConfig) {
133
137
  super();
134
138
  makeObservable(this);
@@ -136,6 +140,7 @@ export class DockViewModel extends HoistModel {
136
140
 
137
141
  this.id = id;
138
142
  this.containerModel = containerModel;
143
+ this.xhName = xhName ?? containerModel?.childXhName(id);
139
144
  this.title = title;
140
145
  this.icon = icon;
141
146
  this.content = content;
@@ -112,6 +112,8 @@ export const [FileChooser, fileChooser] = hoistCmp.withFactory<FileChooserProps>
112
112
  container = isTop ? vframe : hframe;
113
113
  return container({
114
114
  className,
115
+ testId: props.testId,
116
+ domAttrs: props.domAttrs,
115
117
  items: [
116
118
  // `box` (flex: none) so the rail keeps a fixed size beside the grid;
117
119
  // `frame` would grow and crowd it out.
@@ -149,6 +151,8 @@ export const [FileChooser, fileChooser] = hoistCmp.withFactory<FileChooserProps>
149
151
  !hasFiles ? 'xh-file-chooser__target--empty' : null,
150
152
  clickable ? 'xh-file-chooser__target--clickable' : null
151
153
  ),
154
+ testId: props.testId,
155
+ domAttrs: props.domAttrs,
152
156
  items: [dropzoneItem, targetMask, input({...getInputProps()})],
153
157
  ...getRootProps(),
154
158
  ...getLayoutProps(props)
@@ -273,6 +273,7 @@ export const [FormField, formField] = hoistCmp.withFactory<FormFieldProps>({
273
273
  className: classNames(className, classes),
274
274
  ...getLayoutProps(props),
275
275
  testId,
276
+ domAttrs: props.domAttrs,
276
277
  items: [
277
278
  labelEl({
278
279
  omit: !label,
@@ -62,7 +62,10 @@ export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel>
62
62
  */
63
63
  popoverWidth?: number;
64
64
 
65
- /** True (default) to style trigger button background and borders to match inputs. */
65
+ /**
66
+ * True (default) to style trigger button background and borders to match inputs. Set to false
67
+ * to render an outlined button instead.
68
+ */
66
69
  styleButtonAsInput?: boolean;
67
70
  }
68
71
 
@@ -125,7 +128,10 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
125
128
  'xh-grouping-chooser-button',
126
129
  styleButtonAsInput ? 'xh-grouping-chooser-button--as-input' : null
127
130
  ),
128
- minimal: styleButtonAsInput,
131
+ // Input mode relies on the `--as-input` class for its chrome, so renders
132
+ // minimal. Button mode renders outlined, to match the ViewManager trigger.
133
+ ...(styleButtonAsInput ? {minimal: true} : {outlined: true}),
134
+ active: isOpen,
129
135
  ...buttonProps,
130
136
  onClick: () => impl.toggleEditor(),
131
137
  testId
@@ -75,6 +75,7 @@ const cmp = hoistCmp.factory<CheckboxInputModel>(({model, className, ...props},
75
75
  tabIndex: props.tabIndex,
76
76
  id: props.id,
77
77
  [TEST_ID]: props.testId,
78
+ ...props.domAttrs,
78
79
  className,
79
80
  style: props.style,
80
81
 
@@ -671,6 +671,7 @@ const cmp = hoistCmp.factory<CodeInputModel>(({model, className, ...props}, ref)
671
671
  model: model.modalSupportModel,
672
672
  item: inputCmp({
673
673
  testId: props.testId,
674
+ domAttrs: props.domAttrs,
674
675
  width: '100%',
675
676
  height: '100%',
676
677
  className,
@@ -10,7 +10,7 @@
10
10
  height: 100%;
11
11
  }
12
12
 
13
- .bp6-input-group .xh-icon {
13
+ .bp6-input-action .xh-icon {
14
14
  position: relative;
15
15
  margin: 0 !important;
16
16
  }
@@ -468,6 +468,7 @@ const cmp = hoistCmp.factory<DateInputProps & {model: DateInputModel}>(
468
468
  inputRef: model.inputRef,
469
469
  ref: model.textInputRef,
470
470
  testId: getTestId(props),
471
+ domAttrs: props.domAttrs,
471
472
  ...getLayoutProps(props)
472
473
  }),
473
474
  className: 'xh-date-input__click-target',
@@ -0,0 +1,202 @@
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
+
8
+ .xh-intent-input {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ gap: var(--xh-intent-input-gap-px);
12
+
13
+ // Selection ring geometry. Drawn as an outline on the swatch fill, so it takes no part in
14
+ // layout - the container reserves room for it with matching padding, derived from these two
15
+ // values so the two cannot drift apart.
16
+ --ring-offset: 2px;
17
+ --ring-width: 1px;
18
+
19
+ padding: calc(var(--ring-offset) + var(--ring-width));
20
+ // Pin the box against the line rather than letting its own baseline drive the line box. An
21
+ // inline-flex box takes its baseline from its first flex item, so the baseline would otherwise
22
+ // shift as the first swatch gains or loses the check - moving neighboring content (e.g. a
23
+ // FormField label) by a couple of pixels on every selection change.
24
+ vertical-align: middle;
25
+
26
+ &__swatch {
27
+ // Inset hairline color, shared by every fill state below. Overridden by the --none variant,
28
+ // whose transparent fill needs a visible outline rather than a darkening edge.
29
+ --swatch-hairline-color: rgba(0, 0, 0, 0.15);
30
+
31
+ // Reset the UA button styling - the swatch fill below is the entire control.
32
+ appearance: none;
33
+ background: none;
34
+ border: none;
35
+ padding: 0;
36
+ margin: 0;
37
+ display: inline-flex;
38
+ align-items: center;
39
+ gap: var(--xh-pad-half-px);
40
+ cursor: pointer;
41
+ color: var(--xh-text-color);
42
+ font-family: inherit;
43
+ font-size: var(--xh-font-size-px);
44
+ line-height: 1;
45
+
46
+ // Focus ring - drawn in the primary intent so it reads as focus rather than as selection.
47
+ // Higher specificity than the selected/hover rules below, so focus wins where they coincide.
48
+ &:focus-visible {
49
+ outline: none;
50
+
51
+ .xh-intent-input__swatch__fill {
52
+ outline: var(--ring-width) solid var(--xh-intent-primary);
53
+ outline-offset: var(--ring-offset);
54
+ }
55
+ }
56
+
57
+ // The color chip itself. Sized identically whether or not it holds the check, so selecting
58
+ // an option never shifts the row.
59
+ &__fill {
60
+ flex: none;
61
+ width: var(--xh-intent-input-swatch-size-px);
62
+ height: var(--xh-intent-input-swatch-size-px);
63
+ border-radius: var(--xh-intent-input-border-radius-px);
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ // Inner hairline keeps a swatch legible where its color sits close to the surface behind it.
68
+ box-shadow: inset 0 0 0 1px var(--swatch-hairline-color);
69
+ transition:
70
+ box-shadow 100ms,
71
+ outline-color 100ms;
72
+ }
73
+
74
+ // Check is rendered for every swatch and merely hidden when unselected. This keeps each
75
+ // swatch's box AND its baseline identical in both states: a flex container takes its baseline
76
+ // from its first flex item, so a fill that gained/lost the check would move the whole control's
77
+ // baseline, shifting baseline-aligned siblings - most visibly an inline FormField label, which
78
+ // aligns its row with `align-items: baseline`.
79
+ &__check {
80
+ color: var(--xh-white);
81
+ font-size: calc(var(--xh-intent-input-swatch-size-px) * 0.6);
82
+
83
+ &--hidden {
84
+ visibility: hidden;
85
+ }
86
+ }
87
+
88
+ &__name {
89
+ white-space: nowrap;
90
+ }
91
+
92
+ // Selected / hover - a ring offset from the swatch, echoing its own color. Drawn as an
93
+ // outline rather than a second box-shadow: the offset gap then shows the actual surface
94
+ // behind the control, instead of a band painted in --xh-bg that would be the wrong color on
95
+ // any other surface (a panel, a toolbar, a grid cell).
96
+ &--selected .xh-intent-input__swatch__fill,
97
+ &:hover:not(:disabled) .xh-intent-input__swatch__fill {
98
+ outline: var(--ring-width) solid currentColor;
99
+ outline-offset: var(--ring-offset);
100
+ }
101
+
102
+ &:disabled {
103
+ cursor: default;
104
+ color: var(--xh-input-disabled-text-color);
105
+
106
+ .xh-intent-input__swatch__fill {
107
+ opacity: 0.5;
108
+ }
109
+ }
110
+
111
+ // The null option, shown when `enableClear` is set - an empty outlined box standing in for
112
+ // "no intent". Keeps the shared geometry, so it selects and checks exactly like the others.
113
+ &--none {
114
+ // With no fill behind it, this outline IS the swatch, so it needs real contrast against the
115
+ // surface - the standard border color is too faint to read as a selectable target. Mixed
116
+ // from the text color so it adapts per theme; --xh-text-color-muted resolves to the same
117
+ // mid-gray in both, which lands too light against a white background.
118
+ --swatch-hairline-color: color-mix(in srgb, var(--xh-text-color) 60%, var(--xh-bg));
119
+
120
+ color: var(--xh-text-color-muted);
121
+
122
+ .xh-intent-input__swatch__fill {
123
+ background-color: transparent;
124
+ }
125
+
126
+ // No colored fill to sit on, so the check takes the normal text color instead of white.
127
+ .xh-intent-input__swatch__check {
128
+ color: var(--xh-text-color);
129
+ }
130
+ }
131
+
132
+ // Per-intent coloring. `color` carries the intent so the selected ring and the label can
133
+ // both pick it up via currentColor - the check reasserts white against the fill.
134
+ @each $intent in (primary, success, warning, danger) {
135
+ &--#{$intent} {
136
+ color: var(--xh-intent-#{$intent});
137
+
138
+ .xh-intent-input__swatch__fill {
139
+ background-color: var(--xh-intent-#{$intent});
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ // Names shown - label text returns to the standard text color, leaving the swatch to carry
146
+ // the intent's color. Selected names are emphasized instead.
147
+ &--with-names .xh-intent-input__swatch {
148
+ margin-right: var(--xh-pad-half-px);
149
+
150
+ &__name {
151
+ color: var(--xh-text-color);
152
+ }
153
+
154
+ &--selected .xh-intent-input__swatch__name {
155
+ font-weight: 600;
156
+ }
157
+
158
+ &:disabled .xh-intent-input__swatch__name {
159
+ color: var(--xh-input-disabled-text-color);
160
+ }
161
+ }
162
+
163
+ // Compact mode - reduced sizing for toolbars and space-constrained contexts. The ring offset
164
+ // tightens too, keeping the control inside the compact toolbar's item height.
165
+ &--compact {
166
+ --ring-offset: 1px;
167
+
168
+ gap: calc(var(--xh-intent-input-gap-px) / 2);
169
+
170
+ .xh-intent-input__swatch {
171
+ font-size: var(--xh-tbar-compact-font-size-px);
172
+
173
+ &__fill {
174
+ width: var(--xh-intent-input-compact-swatch-size-px);
175
+ height: var(--xh-intent-input-compact-swatch-size-px);
176
+ }
177
+
178
+ &__check {
179
+ font-size: calc(var(--xh-intent-input-compact-swatch-size-px) * 0.6);
180
+ }
181
+ }
182
+ }
183
+
184
+ // Validation borders - drawn on the container, as the swatches themselves are borderless.
185
+ &.xh-input--invalid,
186
+ &.xh-input--warning,
187
+ &.xh-input--info {
188
+ border-radius: var(--xh-intent-input-border-radius-px);
189
+ }
190
+
191
+ &.xh-input--invalid {
192
+ border: var(--xh-form-field-invalid-border);
193
+ }
194
+
195
+ &.xh-input--warning {
196
+ border: var(--xh-form-field-warning-border);
197
+ }
198
+
199
+ &.xh-input--info {
200
+ border: var(--xh-form-field-info-border);
201
+ }
202
+ }