@xh/hoist 77.0.0-SNAPSHOT.1759849276739 → 77.0.0-SNAPSHOT.1760139387105

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## 77.0.0-SNAPSHOT - unreleased
4
4
 
5
+ ### 🎁 New Features
6
+
7
+ * `DashCanvasView` and `DashContainerView` components now have a public `@bindable` `titleDetails`
8
+ property on their models to support displaying additional information in the title bar of these
9
+ components. `titleDetails` is not persisted, and is expected to be set programmatically by the
10
+ application as needed.
11
+
5
12
  ## 76.0.0 - 2025-09-26
6
13
 
7
14
  ### 💥 Breaking Changes (upgrade difficulty: 🟠 MEDIUM - AG Grid update, Hoist React upgrade)
@@ -1,6 +1,6 @@
1
+ import { ReactElement } from 'react';
1
2
  import { HoistModel, MenuItemLike, PlainObject, RefreshMode, RenderMode } from '@xh/hoist/core';
2
3
  import '@xh/hoist/desktop/register';
3
- import { ReactElement } from 'react';
4
4
  import { DashViewSpec } from './DashViewSpec';
5
5
  export type DashViewState = PlainObject;
6
6
  /**
@@ -23,8 +23,15 @@ export declare class DashViewModel<T extends DashViewSpec = DashViewSpec> extend
23
23
  * constructing these models - no need to specify manually.
24
24
  */
25
25
  containerModel: any;
26
- /** Title with which to initialize the view. */
26
+ /** Title with which to initialize the view. Value is persisted. */
27
27
  title: string;
28
+ /**
29
+ * Additional info that will be displayed after the title.
30
+ * Applications can bind to this property to provide dynamic title details.
31
+ * Value is not persisted.
32
+ **/
33
+ titleDetails: string;
34
+ get fullTitle(): string;
28
35
  /** Icon with which to initialize the view. */
29
36
  icon: ReactElement;
30
37
  /** State with which to initialize the view. */
@@ -150,6 +150,7 @@ export declare class DashContainerModel extends DashModel<DashContainerViewSpec,
150
150
  private showTitleForm;
151
151
  private hideTitleForm;
152
152
  private createGoldenLayout;
153
+ private getTitleElement;
153
154
  private destroyGoldenLayout;
154
155
  destroy(): void;
155
156
  }
@@ -18,13 +18,16 @@ export const versionBar = hoistCmp.factory({
18
18
  const inspectorSvc = XH.inspectorService,
19
19
  envSvc = XH.environmentService,
20
20
  env = envSvc.get('appEnvironment'),
21
+ build = envSvc.get('clientBuild'),
21
22
  version = envSvc.get('clientVersion'),
22
- isAdminApp = window.location.pathname?.startsWith('/admin/');
23
+ isAdminApp = window.location.pathname?.startsWith('/admin/'),
24
+ versionAndBuild =
25
+ !build || build === 'UNKNOWN' ? version : `${version} (build ${build})`;
23
26
 
24
27
  return box({
25
28
  className: `xh-version-bar xh-version-bar--${env.toLowerCase()}`,
26
29
  items: [
27
- [XH.appName, env, version].join(' • '),
30
+ [XH.appName, env, versionAndBuild].join(' • '),
28
31
  span({
29
32
  className: 'xh-version-bar__spacer',
30
33
  items: '|'
@@ -4,6 +4,8 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {isNil} from 'lodash';
8
+ import {ReactElement} from 'react';
7
9
  import {
8
10
  HoistModel,
9
11
  managed,
@@ -16,7 +18,6 @@ import {
16
18
  import '@xh/hoist/desktop/register';
17
19
  import {makeObservable, bindable} from '@xh/hoist/mobx';
18
20
  import {throwIf} from '@xh/hoist/utils/js';
19
- import {ReactElement} from 'react';
20
21
  import {DashViewSpec} from './DashViewSpec';
21
22
 
22
23
  export type DashViewState = PlainObject;
@@ -44,9 +45,20 @@ export class DashViewModel<T extends DashViewSpec = DashViewSpec> extends HoistM
44
45
  */
45
46
  containerModel: any;
46
47
 
47
- /** Title with which to initialize the view. */
48
+ /** Title with which to initialize the view. Value is persisted. */
48
49
  @bindable title: string;
49
50
 
51
+ /**
52
+ * Additional info that will be displayed after the title.
53
+ * Applications can bind to this property to provide dynamic title details.
54
+ * Value is not persisted.
55
+ **/
56
+ @bindable titleDetails: string;
57
+
58
+ get fullTitle(): string {
59
+ return [this.title, this.titleDetails].filter(it => !isNil(it)).join(' ');
60
+ }
61
+
50
62
  /** Icon with which to initialize the view. */
51
63
  @bindable.ref icon: ReactElement;
52
64
 
@@ -34,13 +34,13 @@ export const dashCanvasView = hoistCmp.factory({
34
34
  model: uses(DashCanvasViewModel, {publishMode: 'limited'}),
35
35
 
36
36
  render({model, className}) {
37
- const {viewSpec, ref, hidePanelHeader, headerItems, autoHeight} = model,
37
+ const {viewSpec, ref, hidePanelHeader, headerItems, autoHeight, fullTitle, icon} = model,
38
38
  headerProps = hidePanelHeader
39
39
  ? {}
40
40
  : {
41
41
  compactHeader: true,
42
- title: model.title,
43
- icon: model.icon,
42
+ title: fullTitle,
43
+ icon,
44
44
  headerItems: [...headerItems, headerMenu({model})]
45
45
  };
46
46
  return panel({
@@ -338,7 +338,7 @@ export class DashContainerModel
338
338
  renameView(id: string) {
339
339
  const view = this.getItemByViewModel(id);
340
340
  if (!view) return;
341
- this.showTitleForm(view.tab.element);
341
+ this.showTitleForm(view.tab.element, this.getViewModel(id));
342
342
  }
343
343
 
344
344
  onResize() {
@@ -528,10 +528,10 @@ export class DashContainerModel
528
528
 
529
529
  const $el = item.tab.element, // Note: this is a jquery element
530
530
  stack = item.parent,
531
- $titleEl = $el.find('.lm_title').first(),
531
+ $titleEl = this.getTitleElement($el),
532
532
  iconSelector = 'svg.svg-inline--fa',
533
533
  viewSpec = this.getViewSpec(item.config.component),
534
- {icon, title} = viewModel;
534
+ {icon} = viewModel;
535
535
 
536
536
  $el.off('contextmenu').contextmenu(e => {
537
537
  const index = stack.contentItems.indexOf(item);
@@ -551,14 +551,9 @@ export class DashContainerModel
551
551
  }
552
552
  }
553
553
 
554
- if (title) {
555
- const currentTitle = $titleEl.text();
556
- if (currentTitle !== title) $titleEl.text(title);
557
- }
558
-
559
554
  if (viewSpec.allowRename) {
560
555
  this.insertTitleForm($el, viewModel);
561
- $titleEl.off('dblclick').dblclick(() => this.showTitleForm($el));
556
+ $titleEl.off('dblclick').dblclick(() => this.showTitleForm($el, viewModel));
562
557
  }
563
558
  });
564
559
  }
@@ -568,7 +563,7 @@ export class DashContainerModel
568
563
  if ($el.find(formSelector).length) return;
569
564
 
570
565
  // Create and insert form
571
- const $titleEl = $el.find('.lm_title').first();
566
+ const $titleEl = this.getTitleElement($el);
572
567
  $titleEl.after(`<form class="title-form"><input type="text"/></form>`);
573
568
 
574
569
  // Attach listeners
@@ -579,7 +574,6 @@ export class DashContainerModel
579
574
  $formEl.submit(() => {
580
575
  const title = $inputEl.val();
581
576
  if (title.length) {
582
- $titleEl.text(title);
583
577
  viewModel.title = title;
584
578
  }
585
579
 
@@ -588,12 +582,11 @@ export class DashContainerModel
588
582
  });
589
583
  }
590
584
 
591
- private showTitleForm($tabEl) {
585
+ private showTitleForm($tabEl, viewModel: DashViewModel) {
592
586
  if (this.renameLocked) return;
593
587
 
594
- const $titleEl = $tabEl.find('.lm_title').first(),
595
- $inputEl = $tabEl.find('.title-form input').first(),
596
- currentTitle = $titleEl.text();
588
+ const $inputEl = $tabEl.find('.title-form input').first(),
589
+ currentTitle = viewModel.title;
597
590
 
598
591
  $tabEl.addClass('show-title-form');
599
592
  $inputEl.val(currentTitle);
@@ -647,6 +640,16 @@ export class DashContainerModel
647
640
  containerModel: this
648
641
  });
649
642
 
643
+ model.addReaction({
644
+ track: () => model.fullTitle,
645
+ run: () => {
646
+ const item = this.getItemByViewModel(id),
647
+ $titleEl = this.getTitleElement(item.tab.element);
648
+
649
+ $titleEl.text(model.fullTitle);
650
+ }
651
+ });
652
+
650
653
  this.addViewModel(model);
651
654
  return modelLookupContextProvider({
652
655
  value: this.modelLookupContext,
@@ -662,6 +665,10 @@ export class DashContainerModel
662
665
  return ret;
663
666
  }
664
667
 
668
+ private getTitleElement($el) {
669
+ return $el.find('.lm_title').first();
670
+ }
671
+
665
672
  @action
666
673
  private destroyGoldenLayout() {
667
674
  XH.safeDestroy(this.goldenLayout);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "77.0.0-SNAPSHOT.1759849276739",
3
+ "version": "77.0.0-SNAPSHOT.1760139387105",
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",