@xh/hoist 80.0.0-SNAPSHOT.1768251023007 → 80.0.0-SNAPSHOT.1768251400948

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 (43) hide show
  1. package/CHANGELOG.md +14 -9
  2. package/build/types/cmp/grid/GridModel.d.ts +2 -2
  3. package/build/types/cmp/layout/CollapsibleSet.d.ts +14 -0
  4. package/build/types/cmp/layout/Tags.d.ts +2 -0
  5. package/build/types/cmp/tab/TabContainerModel.d.ts +1 -1
  6. package/build/types/core/XH.d.ts +1 -3
  7. package/build/types/core/enums/RenderMode.d.ts +1 -1
  8. package/build/types/desktop/cmp/button/AppMenuButton.d.ts +1 -10
  9. package/build/types/desktop/cmp/button/CollapsibleSetButton.d.ts +12 -0
  10. package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +46 -6
  11. package/build/types/desktop/cmp/dash/canvas/widgetwell/DashCanvasWidgetWell.d.ts +19 -0
  12. package/build/types/desktop/cmp/dash/canvas/widgetwell/DashCanvasWidgetWellModel.d.ts +11 -0
  13. package/build/types/dynamics/desktop.d.ts +1 -0
  14. package/build/types/dynamics/mobile.d.ts +1 -0
  15. package/build/types/mobile/cmp/button/CollapsibleSetButton.d.ts +12 -0
  16. package/build/types/mobile/cmp/header/AppMenuButton.d.ts +1 -10
  17. package/build/types/svc/IdentityService.d.ts +2 -4
  18. package/cmp/grid/GridModel.ts +2 -2
  19. package/cmp/layout/CollapsibleSet.scss +49 -0
  20. package/cmp/layout/CollapsibleSet.ts +135 -0
  21. package/cmp/layout/Tags.ts +2 -0
  22. package/cmp/tab/TabContainerModel.ts +1 -1
  23. package/core/XH.ts +4 -9
  24. package/core/enums/RenderMode.ts +1 -1
  25. package/desktop/appcontainer/AppContainer.ts +2 -0
  26. package/desktop/cmp/appbar/AppBar.scss +5 -24
  27. package/desktop/cmp/button/AppMenuButton.ts +2 -30
  28. package/desktop/cmp/button/CollapsibleSetButton.ts +57 -0
  29. package/desktop/cmp/dash/canvas/DashCanvas.ts +21 -4
  30. package/desktop/cmp/dash/canvas/DashCanvasModel.ts +140 -24
  31. package/desktop/cmp/dash/canvas/widgetwell/DashCanvasWidgetWell.scss +34 -0
  32. package/desktop/cmp/dash/canvas/widgetwell/DashCanvasWidgetWell.ts +135 -0
  33. package/desktop/cmp/dash/canvas/widgetwell/DashCanvasWidgetWellModel.ts +65 -0
  34. package/dynamics/desktop.ts +2 -0
  35. package/dynamics/mobile.ts +2 -0
  36. package/mobile/appcontainer/AppContainer.ts +2 -0
  37. package/mobile/cmp/button/CollapsibleSetButton.ts +57 -0
  38. package/mobile/cmp/header/AppBar.scss +0 -11
  39. package/mobile/cmp/header/AppMenuButton.ts +1 -29
  40. package/package.json +3 -3
  41. package/styles/vars.scss +1 -2
  42. package/svc/IdentityService.ts +2 -14
  43. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,65 @@
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 © 2025 Extremely Heavy Industries Inc.
6
+ */
7
+ import {DragEvent} from 'react';
8
+ import {DashCanvasModel} from '@xh/hoist/desktop/cmp/dash';
9
+ import {HoistModel, managed} from '@xh/hoist/core';
10
+ import '@xh/hoist/desktop/register';
11
+ import {makeObservable, observable} from '@xh/hoist/mobx';
12
+ import {runInAction} from 'mobx';
13
+
14
+ export class DashCanvasWidgetWellModel extends HoistModel {
15
+ @managed
16
+ @observable.ref
17
+ dashCanvasModel: DashCanvasModel;
18
+
19
+ constructor() {
20
+ super();
21
+ makeObservable(this);
22
+ }
23
+
24
+ override onLinked() {
25
+ this.addReaction({
26
+ track: () => this.componentProps,
27
+ run: () =>
28
+ runInAction(() => (this.dashCanvasModel = this.componentProps.dashCanvasModel)),
29
+ fireImmediately: true
30
+ });
31
+ }
32
+
33
+ onDragStart(evt: DragEvent<HTMLDivElement>) {
34
+ const target = evt.target as HTMLElement;
35
+ if (!target) return;
36
+
37
+ this.dashCanvasModel.showAddViewButtonWhenEmpty = false;
38
+ evt.dataTransfer.effectAllowed = 'move';
39
+ target.classList.add('is-dragging');
40
+
41
+ const viewSpecId: string = target.getAttribute('id').split('draggableFor-')[1],
42
+ viewSpec = this.dashCanvasModel.viewSpecs.find(it => it.id === viewSpecId),
43
+ {width, height} = viewSpec,
44
+ widget = {
45
+ viewSpecId,
46
+ layout: {
47
+ x: 0,
48
+ y: 0,
49
+ w: width,
50
+ h: height
51
+ }
52
+ };
53
+
54
+ this.dashCanvasModel.setDraggedInView(widget);
55
+ }
56
+
57
+ onDragEnd(evt: DragEvent<HTMLDivElement>) {
58
+ this.dashCanvasModel.showAddViewButtonWhenEmpty = true;
59
+
60
+ const target = evt.target as HTMLElement;
61
+ if (!target) return;
62
+
63
+ target.classList.remove('is-dragging');
64
+ }
65
+ }
@@ -15,6 +15,7 @@
15
15
  *
16
16
  * See the platform specific AppContainer where these implementations are actually provided.
17
17
  */
18
+ export let collapsibleSetButton = null;
18
19
  export let ColChooserModel = null;
19
20
  export let ColumnHeaderFilterModel = null;
20
21
  export let ModalSupportModel = null;
@@ -37,6 +38,7 @@ export let DynamicTabSwitcherModel = null;
37
38
  * Not for Application use.
38
39
  */
39
40
  export function installDesktopImpls(impls) {
41
+ collapsibleSetButton = impls.collapsibleSetButton;
40
42
  ColChooserModel = impls.ColChooserModel;
41
43
  ColumnHeaderFilterModel = impls.ColumnHeaderFilterModel;
42
44
  ModalSupportModel = impls.ModalSupportModel;
@@ -15,6 +15,7 @@
15
15
  *
16
16
  * See the platform specific AppContainer where these implementations are actually provided.
17
17
  */
18
+ export let collapsibleSetButton = null;
18
19
  export let ColChooserModel = null;
19
20
  export let colChooser = null;
20
21
  export let zoneMapper = null;
@@ -30,6 +31,7 @@ export let maskImpl = null;
30
31
  * Not for Application use.
31
32
  */
32
33
  export function installMobileImpls(impls) {
34
+ collapsibleSetButton = impls.collapsibleSetButton;
33
35
  ColChooserModel = impls.ColChooserModel;
34
36
  colChooser = impls.colChooser;
35
37
  zoneMapper = impls.zoneMapper;
@@ -18,6 +18,7 @@ import {pinPadImpl} from '@xh/hoist/mobile/cmp/pinpad/impl/PinPad';
18
18
  import {storeFilterFieldImpl} from '@xh/hoist/mobile/cmp/store/impl/StoreFilterField';
19
19
  import {tabContainerImpl} from '@xh/hoist/mobile/cmp/tab/impl/TabContainer';
20
20
  import {zoneMapper} from '@xh/hoist/mobile/cmp/zoneGrid/impl/ZoneMapper';
21
+ import {collapsibleSetButton} from '@xh/hoist/mobile/cmp/button/CollapsibleSetButton';
21
22
  import {elementFromContent, useOnMount} from '@xh/hoist/utils/react';
22
23
  import {isEmpty} from 'lodash';
23
24
  import {aboutDialog} from './AboutDialog';
@@ -34,6 +35,7 @@ import {toastSource} from './ToastSource';
34
35
  import {versionBar} from './VersionBar';
35
36
 
36
37
  installMobileImpls({
38
+ collapsibleSetButton,
37
39
  tabContainerImpl,
38
40
  storeFilterFieldImpl,
39
41
  pinPadImpl,
@@ -0,0 +1,57 @@
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
+ import {type ReactElement, type ReactNode, type JSX, useState} from 'react';
9
+ import {tooltip as bpTooltip} from '@xh/hoist/kit/blueprint';
10
+ import {fragment} from '@xh/hoist/cmp/layout';
11
+ import {hoistCmp} from '@xh/hoist/core';
12
+ import type {Intent, HoistProps} from '@xh/hoist/core';
13
+ import {button} from '@xh/hoist/mobile/cmp/button';
14
+ import {legend} from '@xh/hoist/cmp/layout';
15
+ import {Icon} from '@xh/hoist/icon/Icon';
16
+
17
+ export interface CollapsibleSetButtonProps extends HoistProps {
18
+ icon?: ReactElement;
19
+ text: ReactNode;
20
+ tooltip?: JSX.Element | string;
21
+ clickHandler?: (boolean) => void;
22
+ intent?: Intent;
23
+ collapsed?: boolean;
24
+ disabled?: boolean;
25
+ }
26
+
27
+ export const [CollapsibleSetButton, collapsibleSetButton] =
28
+ hoistCmp.withFactory<CollapsibleSetButtonProps>({
29
+ displayName: 'CollapsibleSetButton',
30
+ model: false,
31
+ render({icon, text, tooltip, intent, clickHandler, collapsed, disabled}) {
32
+ const [isCollapsed, setIsCollapsed] = useState<boolean>(collapsed === true),
33
+ btn = button({
34
+ text: fragment(text, isCollapsed ? Icon.angleDown() : Icon.angleUp()),
35
+ icon,
36
+ outlined: isCollapsed && !intent,
37
+ minimal: !intent || (intent && !isCollapsed),
38
+ intent,
39
+ disabled,
40
+ onClick: () => {
41
+ const val = !isCollapsed;
42
+ setIsCollapsed(val);
43
+ clickHandler?.(val);
44
+ }
45
+ });
46
+
47
+ return legend(
48
+ tooltip
49
+ ? bpTooltip({
50
+ item: btn,
51
+ content: tooltip,
52
+ intent
53
+ })
54
+ : btn
55
+ );
56
+ }
57
+ });
@@ -18,17 +18,6 @@
18
18
  display: flex;
19
19
  }
20
20
 
21
- .xh-app-menu-button__user-profile {
22
- border-radius: 50%;
23
- border: 1px solid var(--xh-appbar-title-color);
24
- cursor: pointer;
25
- height: 28px;
26
- font-size: var(--xh-font-size-small-em);
27
- line-height: 27px;
28
- text-align: center;
29
- width: 28px;
30
- }
31
-
32
21
  .xh-button {
33
22
  background: transparent !important;
34
23
  color: var(--xh-appbar-title-color) !important;
@@ -4,14 +4,11 @@
4
4
  *
5
5
  * Copyright © 2026 Extremely Heavy Industries Inc.
6
6
  */
7
- import {div} from '@xh/hoist/cmp/layout';
8
- import {hoistCmp, HoistProps, HoistUser, MenuItemLike, XH} from '@xh/hoist/core';
7
+ import {hoistCmp, MenuItemLike, XH} from '@xh/hoist/core';
9
8
  import {Icon} from '@xh/hoist/icon';
10
9
  import {menuButton, MenuButtonProps} from '@xh/hoist/mobile/cmp/menu';
11
10
  import '@xh/hoist/mobile/register';
12
11
  import {withDefault} from '@xh/hoist/utils/js';
13
- import {isFunction} from 'lodash';
14
- import {ReactNode} from 'react';
15
12
 
16
13
  export interface AppMenuButtonProps extends MenuButtonProps {
17
14
  /** Array of app-specific MenuItems or configs to create them. */
@@ -40,17 +37,8 @@ export interface AppMenuButtonProps extends MenuButtonProps {
40
37
 
41
38
  /** True to hide the About button */
42
39
  hideAboutItem?: boolean;
43
-
44
- /**
45
- * Replace the default hamburger icon with a user profile representation. Set to true to render
46
- * the user's initials from their `HoistUser.displayName`. Alternately, provide a custom
47
- * function to render an alternate compact string or element for the current user.
48
- */
49
- renderWithUserProfile?: boolean | RenderWithUserProfileCustomFn;
50
40
  }
51
41
 
52
- type RenderWithUserProfileCustomFn = (user: HoistUser) => ReactNode;
53
-
54
42
  /**
55
43
  * A top-level application drop down menu, which installs a standard set of menu items for common
56
44
  * application actions. Application specific items can be displayed before these standard items.
@@ -74,13 +62,11 @@ export const [AppMenuButton, appMenuButton] = hoistCmp.withFactory<AppMenuButton
74
62
  hideOptionsItem,
75
63
  hideThemeItem,
76
64
  hideAboutItem,
77
- renderWithUserProfile,
78
65
  ...rest
79
66
  } = props;
80
67
 
81
68
  return menuButton({
82
69
  className,
83
- icon: renderWithUserProfile ? userProfile({renderWithUserProfile}) : Icon.menu(),
84
70
  menuItems: buildMenuItems(props),
85
71
  menuClassName: 'xh-app-menu',
86
72
  popoverProps: {popoverClassName: 'xh-app-menu-popover'},
@@ -92,20 +78,6 @@ export const [AppMenuButton, appMenuButton] = hoistCmp.withFactory<AppMenuButton
92
78
  //---------------------------
93
79
  // Implementation
94
80
  //---------------------------
95
- const userProfile = hoistCmp.factory<
96
- HoistProps & {renderWithUserProfile: true | RenderWithUserProfileCustomFn}
97
- >({
98
- model: false,
99
- render({renderWithUserProfile}) {
100
- return div({
101
- className: 'xh-app-menu-button__user-profile',
102
- item: isFunction(renderWithUserProfile)
103
- ? renderWithUserProfile(XH.getUser())
104
- : XH.getUserInitials()
105
- });
106
- }
107
- });
108
-
109
81
  function buildMenuItems({
110
82
  hideOptionsItem,
111
83
  hideFeedbackItem,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "80.0.0-SNAPSHOT.1768251023007",
3
+ "version": "80.0.0-SNAPSHOT.1768251400948",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",
@@ -62,11 +62,11 @@
62
62
  "moment": "~2.30.1",
63
63
  "numbro": "~2.5.0",
64
64
  "onsenui": "~2.12.8",
65
- "qs": "~6.14.0",
65
+ "qs": "~6.14.1",
66
66
  "react-beautiful-dnd": "~13.1.0",
67
67
  "react-dates": "~21.8.0",
68
68
  "react-dropzone": "~10.2.2",
69
- "react-grid-layout": "2.1.1",
69
+ "react-grid-layout": "2.2.2",
70
70
  "react-markdown": "~10.1.0",
71
71
  "react-onsenui": "~1.13.2",
72
72
  "react-popper": "~2.3.0",
package/styles/vars.scss CHANGED
@@ -205,14 +205,13 @@ body {
205
205
  //---------
206
206
  --xh-appbar-bg: var(--appbar-bg, var(--xh-bg-alt));
207
207
  --xh-appbar-border-color: var(--appbar-border-color, transparent);
208
- --xh-appbar-box-shadow: var(--appbar-box-shadow, #{0 0 0 1px rgb(17 20 24 / 10%), 0 1px 1px rgb(17 20 24 / 20%)});
209
208
  --xh-appbar-color: var(--appbar-color, var(--xh-text-color));
210
209
  --xh-appbar-height: var(--appbar-height, 42);
211
210
  --xh-appbar-height-px: calc(var(--xh-appbar-height) * 1px);
212
211
  --xh-appbar-title-color: var(--appbar-title-color, #{mc('blue-grey', '700')});
213
212
  --xh-appbar-title-font-size: var(--appbar-title-font-size, calc(var(--xh-font-size) * 1.9));
214
213
  --xh-appbar-title-font-size-px: calc(var(--xh-appbar-title-font-size) * 1px);
215
- --xh-appbar-user-profile-hover-color: var(--appbar-user-profile-hover-color, var(--xh-orange));
214
+ --xh-appbar-box-shadow: var(--appbar-box-shadow, #{0 0 0 1px rgb(17 20 24 / 10%), 0 1px 1px rgb(17 20 24 / 20%)});
216
215
 
217
216
  &.xh-dark {
218
217
  --xh-appbar-border-color: var(--appbar-border-color, #{mc('blue-grey', '700')});
@@ -30,28 +30,16 @@ export class IdentityService extends HoistService {
30
30
  }
31
31
  }
32
32
 
33
- /** @returns current acting user (see authUser for notes on impersonation) */
33
+ /** Current acting user (see authUser for notes on impersonation) */
34
34
  get user(): HoistUser {
35
35
  return this._apparentUser;
36
36
  }
37
37
 
38
- /** @returns current acting user's username. */
38
+ /** Current acting user's username. */
39
39
  get username(): string {
40
40
  return this.user?.username ?? null;
41
41
  }
42
42
 
43
- /** @returns current acting user's initials, based on displayName. */
44
- get userInitials(): string {
45
- // Handle common case of displayName being left as an email address.
46
- const [displayName] = this.user.displayName.split('@'),
47
- nameParts = displayName.split(/[\s.]+/);
48
-
49
- return nameParts
50
- .map(part => part.charAt(0).toUpperCase())
51
- .join('')
52
- .substring(0, XH.isMobileApp ? 2 : 3);
53
- }
54
-
55
43
  /**
56
44
  * Actual user who authenticated to the web application.
57
45
  * This will be the same as the user except when an administrator is impersonation another