@xh/hoist 75.0.0-SNAPSHOT.1753376908830 → 75.0.0-SNAPSHOT.1753458616250

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.
@@ -437,6 +437,10 @@ export declare class GridModel extends HoistModel {
437
437
  collapseAll(): void;
438
438
  /** Expand all parent rows in grouped or tree grid to the specified level. */
439
439
  expandToLevel(level: number): void;
440
+ /**
441
+ * Get the resolved level labels for the current state of the grid.
442
+ */
443
+ get resolvedLevelLabels(): string[];
440
444
  /**
441
445
  * Sort this grid.
442
446
  * This method is a no-op if provided any sorters without a corresponding column.
@@ -13,7 +13,7 @@ export declare class DraggerModel extends HoistModel {
13
13
  maxSize: any;
14
14
  throttledSetSize: any;
15
15
  onLinked(): void;
16
- private addListeners;
16
+ private toggleListeners;
17
17
  private onDragStart;
18
18
  private onDrag;
19
19
  private onDragEnd;
@@ -24,9 +24,11 @@ export declare class DraggerModel extends HoistModel {
24
24
  private parseEventPositions;
25
25
  private isValidMouseEvent;
26
26
  private isValidTouchEvent;
27
+ private get isFirefox();
27
28
  /**
28
29
  * @param v - Workaround to allow dragging over iframe, which is its own
29
30
  * separate document and cannot listen for events from main document.
30
31
  */
31
32
  setIframePointerEvents(v: 'none' | 'auto'): void;
33
+ destroy(): void;
32
34
  }
@@ -92,7 +92,8 @@ import {
92
92
  min,
93
93
  omit,
94
94
  pick,
95
- pull
95
+ pull,
96
+ take
96
97
  } from 'lodash';
97
98
  import {ReactNode} from 'react';
98
99
  import {GridAutosizeOptions} from './GridAutosizeOptions';
@@ -552,7 +553,7 @@ export class GridModel extends HoistModel {
552
553
 
553
554
  this.xhImpl = xhImpl;
554
555
 
555
- this._defaultState = {columns, sortBy, groupBy};
556
+ this._defaultState = {columns, sortBy, groupBy, expandLevel};
556
557
 
557
558
  this.treeMode = treeMode;
558
559
  this.treeStyle = treeStyle;
@@ -679,10 +680,11 @@ export class GridModel extends HoistModel {
679
680
  if (!confirmed) return false;
680
681
  }
681
682
 
682
- const {columns, sortBy, groupBy, filter} = this._defaultState;
683
+ const {columns, sortBy, groupBy, filter, expandLevel} = this._defaultState;
683
684
  this.setColumns(columns);
684
685
  this.setSortBy(sortBy);
685
686
  this.setGroupBy(groupBy);
687
+ this.expandToLevel(expandLevel);
686
688
 
687
689
  this.filterModel?.setFilter(filter);
688
690
 
@@ -1043,6 +1045,19 @@ export class GridModel extends HoistModel {
1043
1045
  this.expandLevel = level;
1044
1046
  }
1045
1047
 
1048
+ /**
1049
+ * Get the resolved level labels for the current state of the grid.
1050
+ */
1051
+ get resolvedLevelLabels(): string[] {
1052
+ const {maxDepth, levelLabels} = this,
1053
+ ret = executeIfFunction(levelLabels);
1054
+ if (ret && ret.length < maxDepth + 1) {
1055
+ this.logError('Value produced by `GridModel.levelLabels` has insufficient length.');
1056
+ return null;
1057
+ }
1058
+ return ret ? take(ret, maxDepth + 1) : null;
1059
+ }
1060
+
1046
1061
  /**
1047
1062
  * Sort this grid.
1048
1063
  * This method is a no-op if provided any sorters without a corresponding column.
@@ -9,7 +9,6 @@ import {Column, GridModel} from '@xh/hoist/cmp/grid';
9
9
  import {RecordAction, Store, StoreRecord} from '@xh/hoist/data';
10
10
  import {convertIconToHtml, Icon} from '@xh/hoist/icon';
11
11
  import {filterConsecutiveMenuSeparators} from '@xh/hoist/utils/impl';
12
- import {executeIfFunction} from '@xh/hoist/utils/js';
13
12
  import copy from 'clipboard-copy';
14
13
  import {isEmpty, isFunction, isNil, isString, uniq} from 'lodash';
15
14
  import {isValidElement} from 'react';
@@ -282,26 +281,15 @@ function levelExpandAction(gridModel: GridModel): RecordAction {
282
281
  return new RecordAction({
283
282
  text: 'Expand to ...',
284
283
  displayFn: () => {
285
- // Don't show for degenerate shallow grid models, or if we don't have labels
286
- const {maxDepth, expandLevel} = gridModel;
287
- if (maxDepth <= 1) return {hidden: true};
284
+ const {maxDepth, expandLevel, resolvedLevelLabels} = gridModel;
288
285
 
289
- // Validate level labels.
290
- const levelLabels = executeIfFunction(gridModel.levelLabels);
291
- if (!levelLabels) {
292
- return {hidden: true};
293
- }
294
- if (levelLabels.length < maxDepth + 1) {
295
- gridModel.logDebug(
296
- 'Value produced by `GridModel.levelLabels` has insufficient length. No menu items shown.'
297
- );
298
- return {hidden: true};
299
- }
286
+ // Don't show for flat grid models or if we don't have labels
287
+ if (!maxDepth || !resolvedLevelLabels) return {hidden: true};
300
288
 
301
- const items = levelLabels.map((label, idx) => {
289
+ const items = resolvedLevelLabels.map((label, idx) => {
302
290
  const isCurrLevel =
303
291
  expandLevel === idx ||
304
- (expandLevel > maxDepth && idx === levelLabels.length - 1);
292
+ (expandLevel > maxDepth && idx === resolvedLevelLabels.length - 1);
305
293
 
306
294
  return {
307
295
  icon: isCurrLevel ? Icon.check() : null,
@@ -11,7 +11,7 @@ import '@xh/hoist/desktop/register';
11
11
  import {Icon} from '@xh/hoist/icon';
12
12
  import {menu, popover, Position} from '@xh/hoist/kit/blueprint';
13
13
  import {parseMenuItems} from '@xh/hoist/utils/impl';
14
- import {executeIfFunction, logDebug, logError, withDefault} from '@xh/hoist/utils/js';
14
+ import {logError, withDefault} from '@xh/hoist/utils/js';
15
15
  import {ReactNode} from 'react';
16
16
  import {button, ButtonProps} from '../Button';
17
17
 
@@ -52,25 +52,14 @@ export const [ExpandToLevelButton, expandToLevelButton] =
52
52
  return disabledButton();
53
53
  }
54
54
 
55
- // Render a disabled button if requested or if we have a flat grid.
56
- const {maxDepth, expandLevel} = gridModel;
57
- if (disabled || !maxDepth) return disabledButton();
55
+ // Render a disabled button if requested, if we have a flat grid, or no level labels
56
+ const {maxDepth, expandLevel, resolvedLevelLabels} = gridModel;
57
+ if (disabled || !maxDepth || !resolvedLevelLabels) return disabledButton();
58
58
 
59
- // Validate level labels - disable quietly if unspecified or w/log if mismatched to grid depth.
60
- const levelLabels = executeIfFunction(gridModel.levelLabels);
61
- if (!levelLabels) return disabledButton();
62
- if (levelLabels.length < maxDepth + 1) {
63
- logDebug(
64
- 'Value produced by `GridModel.levelLabels` has insufficient length - button will be disabled.',
65
- ExpandToLevelButton
66
- );
67
- return disabledButton();
68
- }
69
-
70
- const menuItems: MenuItemLike[] = levelLabels.map((label, idx) => {
59
+ const menuItems: MenuItemLike[] = resolvedLevelLabels.map((label, idx) => {
71
60
  const isCurrLevel =
72
61
  expandLevel === idx ||
73
- (expandLevel > maxDepth && idx === levelLabels.length - 1);
62
+ (expandLevel > maxDepth && idx === resolvedLevelLabels.length - 1);
74
63
 
75
64
  return {
76
65
  icon: isCurrLevel ? Icon.check() : Icon.placeholder(),
@@ -30,25 +30,34 @@ export class DraggerModel extends HoistModel {
30
30
  override onLinked() {
31
31
  this.throttledSetSize = throttle(size => (this.panelModel.size = size), 50);
32
32
 
33
- // Add listeners to el to ensure we can get non-passive handlers than can preventDefault()
33
+ // maintain listeners on el to ensure we can get non-passive handlers than can preventDefault()
34
34
  // React synthetic touch events on certain browsers (e.g. airwatch) don't yield that
35
35
  this.addReaction({
36
36
  track: () => this.ref.current,
37
- run: current => {
38
- if (current) this.addListeners(current);
37
+ run: (current, old) => {
38
+ this.toggleListeners(old, false);
39
+ this.toggleListeners(current, true);
39
40
  }
40
41
  });
41
42
  }
42
43
 
43
- private addListeners(el) {
44
+ private toggleListeners(el, add: boolean) {
45
+ if (!el) return;
46
+ const fn = (element, event, handler, options = undefined) => {
47
+ add
48
+ ? element.addEventListener(event, handler, options)
49
+ : element.removeEventListener(event, handler, options);
50
+ };
44
51
  if (XH.isDesktop) {
45
- el.addEventListener('dragstart', this.onDragStart);
46
- el.addEventListener('drag', this.onDrag);
47
- el.addEventListener('dragend', this.onDragEnd);
52
+ fn(el, 'dragstart', this.onDragStart);
53
+ fn(el, 'dragend', this.onDragEnd);
54
+ this.isFirefox // drag co-ordinates not impl in firefox -- use 'dragover' on doc
55
+ ? fn(document, `dragover`, this.onDrag)
56
+ : fn(el, 'drag', this.onDrag);
48
57
  } else {
49
- el.addEventListener('touchstart', this.onDragStart);
50
- el.addEventListener('touchmove', this.onDrag, {passive: false});
51
- el.addEventListener('touchend', this.onDragEnd);
58
+ fn(el, 'touchstart', this.onDragStart);
59
+ fn(el, 'touchmove', this.onDrag, {passive: false});
60
+ fn(el, 'touchend', this.onDragEnd);
52
61
  }
53
62
  }
54
63
 
@@ -263,6 +272,10 @@ export class DraggerModel extends HoistModel {
263
272
  return e.touches && e.touches.length > 0;
264
273
  }
265
274
 
275
+ private get isFirefox(): boolean {
276
+ return navigator.userAgent.toLowerCase().includes('firefox');
277
+ }
278
+
266
279
  /**
267
280
  * @param v - Workaround to allow dragging over iframe, which is its own
268
281
  * separate document and cannot listen for events from main document.
@@ -272,4 +285,9 @@ export class DraggerModel extends HoistModel {
272
285
  el.style['pointer-events'] = v;
273
286
  }
274
287
  }
288
+
289
+ override destroy() {
290
+ this.toggleListeners(this.ref.current, false);
291
+ super.destroy();
292
+ }
275
293
  }
@@ -8,7 +8,7 @@ import {GridModel} from '@xh/hoist/cmp/grid';
8
8
  import {hoistCmp, MenuItemLike, useContextModel} from '@xh/hoist/core';
9
9
  import {Icon} from '@xh/hoist/icon';
10
10
  import '@xh/hoist/mobile/register';
11
- import {executeIfFunction, logDebug, logError, withDefault} from '@xh/hoist/utils/js';
11
+ import {logError, withDefault} from '@xh/hoist/utils/js';
12
12
  import {menuButton, MenuButtonProps} from '../../menu';
13
13
 
14
14
  export interface ExpandToLevelButtonProps extends MenuButtonProps {
@@ -29,7 +29,7 @@ export const [ExpandToLevelButton, expandToLevelButton] =
29
29
  className: 'xh-expand-to-level-button',
30
30
  model: false,
31
31
 
32
- render({gridModel, className, icon, ...rest}) {
32
+ render({gridModel, className, icon, disabled, ...rest}) {
33
33
  gridModel = withDefault(gridModel, useContextModel(GridModel));
34
34
  icon = withDefault(icon, Icon.treeList());
35
35
 
@@ -50,28 +50,14 @@ export const [ExpandToLevelButton, expandToLevelButton] =
50
50
  return disabledButton();
51
51
  }
52
52
 
53
- // Disable for flat grids. Still show for grids with a single grouping level, although
54
- // in this case we're effectively a less efficient way to expand/collapse all.
55
- const {maxDepth, expandLevel} = gridModel;
56
- if (!maxDepth) return disabledButton();
53
+ // Render a disabled button if requested or if we have a flat grid, or no level labels
54
+ const {maxDepth, expandLevel, resolvedLevelLabels} = gridModel;
55
+ if (disabled || !maxDepth || !resolvedLevelLabels) return disabledButton();
57
56
 
58
- // Validate level labels - disable if unspecified or mismatched to grid depth.
59
- const levelLabels = executeIfFunction(gridModel.levelLabels);
60
- if (!levelLabels) {
61
- return disabledButton();
62
- }
63
- if (levelLabels.length < maxDepth + 1) {
64
- logDebug(
65
- 'Value produced by `GridModel.levelLabels` has insufficient length. No menu items shown.',
66
- ExpandToLevelButton
67
- );
68
- return disabledButton();
69
- }
70
-
71
- const menuItems: MenuItemLike[] = levelLabels.map((label, idx) => {
57
+ const menuItems: MenuItemLike[] = resolvedLevelLabels.map((label, idx) => {
72
58
  const isCurrLevel =
73
59
  expandLevel === idx ||
74
- (expandLevel > maxDepth && idx === levelLabels.length - 1);
60
+ (expandLevel > maxDepth && idx === resolvedLevelLabels.length - 1);
75
61
 
76
62
  return {
77
63
  icon: isCurrLevel ? Icon.check() : Icon.placeholder(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "75.0.0-SNAPSHOT.1753376908830",
3
+ "version": "75.0.0-SNAPSHOT.1753458616250",
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",