higlass 2.2.2 → 2.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 (38) hide show
  1. package/app/scripts/Autocomplete.jsx +12 -3
  2. package/app/scripts/Button.jsx +20 -24
  3. package/app/scripts/ContextMenuContainer.jsx +1 -3
  4. package/app/scripts/ContextMenuItem.jsx +15 -16
  5. package/app/scripts/Dialog.jsx +30 -29
  6. package/app/scripts/HiGlassComponent.jsx +54 -36
  7. package/app/scripts/HorizontalItem.jsx +50 -21
  8. package/app/scripts/HorizontalTrack.jsx +1 -0
  9. package/app/scripts/ListWrapper.jsx +6 -17
  10. package/app/scripts/Modal.jsx +14 -13
  11. package/app/scripts/PopupMenu.jsx +6 -12
  12. package/app/scripts/SortableList.jsx +119 -64
  13. package/app/scripts/TrackArea.jsx +19 -24
  14. package/app/scripts/TrackControl.jsx +16 -27
  15. package/app/scripts/VerticalItem.jsx +53 -22
  16. package/app/scripts/VerticalTrack.jsx +1 -0
  17. package/app/scripts/api.js +5 -4
  18. package/app/scripts/hglib.jsx +38 -5
  19. package/app/scripts/test-helpers/test-helpers.jsx +2 -2
  20. package/app/scripts/utils/get-higlass-components.js +2 -2
  21. package/app/scripts/utils/react-dom-compat.js +100 -0
  22. package/app/styles/HiGlass.scss +1 -0
  23. package/dist/app/scripts/Autocomplete.d.ts +3 -1
  24. package/dist/app/scripts/ContextMenuContainer.d.ts +0 -1
  25. package/dist/app/scripts/ContextMenuItem.d.ts +9 -15
  26. package/dist/app/scripts/HiGlassComponent.d.ts +0 -3
  27. package/dist/app/scripts/HorizontalItem.d.ts +1 -1
  28. package/dist/app/scripts/ListWrapper.d.ts +2 -7
  29. package/dist/app/scripts/PopupMenu.d.ts +2 -4
  30. package/dist/app/scripts/SortableList.d.ts +6 -2
  31. package/dist/app/scripts/VerticalItem.d.ts +1 -1
  32. package/dist/app/scripts/hglib.d.ts +2 -2
  33. package/dist/app/scripts/utils/react-dom-compat.d.ts +27 -0
  34. package/dist/hglib.js +13136 -11155
  35. package/dist/hglib.min.js +89 -89
  36. package/dist/higlass.mjs +13144 -11163
  37. package/dist/package.json +7 -5
  38. package/package.json +7 -5
@@ -17,6 +17,9 @@ class Autocomplete extends React.Component {
17
17
  isOpen: false,
18
18
  };
19
19
 
20
+ this.itemRefs = {};
21
+ this.menuRef = null;
22
+
20
23
  this.keyDownHandlers = {
21
24
  ArrowDown(event) {
22
25
  event.preventDefault();
@@ -139,7 +142,7 @@ class Autocomplete extends React.Component {
139
142
 
140
143
  maybeScrollItemIntoView() {
141
144
  if (this.state.isOpen === true && this.state.highlightedIndex !== null) {
142
- const itemNode = this.refs[`item-${this.state.highlightedIndex}`];
145
+ const itemNode = this.itemRefs[this.state.highlightedIndex];
143
146
  itemNode?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
144
147
  }
145
148
  }
@@ -250,7 +253,9 @@ class Autocomplete extends React.Component {
250
253
  // Ignore blur to prevent menu from de-rendering before we can process click
251
254
  onMouseEnter: () => this.highlightItemFromMouse(index),
252
255
  onClick: () => this.selectItemFromMouse(item),
253
- ref: `item-${index}`,
256
+ ref: (el) => {
257
+ this.itemRefs[index] = el;
258
+ },
254
259
  });
255
260
  });
256
261
  const style = {
@@ -260,7 +265,11 @@ class Autocomplete extends React.Component {
260
265
  };
261
266
  if (!items.length) return null;
262
267
  const menu = this.props.renderMenu(items, this.props.value, style);
263
- return React.cloneElement(menu, { ref: 'menu' });
268
+ return React.cloneElement(menu, {
269
+ ref: (el) => {
270
+ this.menuRef = el;
271
+ },
272
+ });
264
273
  }
265
274
 
266
275
  handleInputBlur() {
@@ -4,30 +4,26 @@ import React from 'react';
4
4
 
5
5
  import classes from '../styles/Button.module.scss';
6
6
 
7
- const Button = React.forwardRef((props, ref) => (
8
- <button
9
- ref={ref}
10
- className={classes[props.styleName] ?? classes.button}
11
- disabled={props.disable}
12
- onBlur={props.onBlur}
13
- onClick={props.onClick}
14
- onMouseDown={props.onMouseDown}
15
- onMouseOut={props.onMouseOut}
16
- onMouseUp={props.onMouseUp}
17
- type="button"
18
- >
19
- {props.children}
20
- {props.shortcut && (
21
- <span className={classes['button-shortcut']}>{props.shortcut}</span>
22
- )}
23
- </button>
24
- ));
25
-
26
- Button.defaultProps = {
27
- onClick: () => {},
28
- styleName: '',
29
- type: 'button',
30
- };
7
+ const Button = React.forwardRef(
8
+ ({ onClick = () => {}, styleName = '', children, ...props }, ref) => (
9
+ <button
10
+ ref={ref}
11
+ className={classes[styleName] ?? classes.button}
12
+ disabled={props.disable}
13
+ onBlur={props.onBlur}
14
+ onClick={onClick}
15
+ onMouseDown={props.onMouseDown}
16
+ onMouseOut={props.onMouseOut}
17
+ onMouseUp={props.onMouseUp}
18
+ type="button"
19
+ >
20
+ {children}
21
+ {props.shortcut && (
22
+ <span className={classes['button-shortcut']}>{props.shortcut}</span>
23
+ )}
24
+ </button>
25
+ ),
26
+ );
31
27
 
32
28
  Button.propTypes = {
33
29
  children: PropTypes.any,
@@ -2,7 +2,6 @@
2
2
  import clsx from 'clsx';
3
3
  import PropTypes from 'prop-types';
4
4
  import React from 'react';
5
- import ReactDOM from 'react-dom';
6
5
 
7
6
  import { THEME_DARK } from './configs';
8
7
 
@@ -101,8 +100,7 @@ class ContextMenuContainer extends React.Component {
101
100
  if (this.adjusted) return;
102
101
 
103
102
  this.adjusted = true;
104
- this.divDom = ReactDOM.findDOMNode(this.div);
105
- const bbox = this.divDom.getBoundingClientRect();
103
+ const bbox = this.div.getBoundingClientRect();
106
104
 
107
105
  const parentBbox = this.props.parentBbox
108
106
  ? this.props.parentBbox
@@ -9,37 +9,36 @@ import classes from '../styles/ContextMenu.module.scss';
9
9
  * @typedef ContextMenuItemProps
10
10
  * @prop {string} [className]
11
11
  * @prop {(evt: React.MouseEvent) => void} onClick
12
- * @prop {(evt: React.MouseEvent) => void} onMouseEnter
13
- * @prop {(evt: React.MouseEvent) => void} onMouseLeave
12
+ * @prop {(evt: React.MouseEvent) => void} [onMouseEnter]
13
+ * @prop {(evt: React.MouseEvent) => void} [onMouseLeave]
14
14
  */
15
15
 
16
16
  /**
17
17
  * @param {React.PropsWithChildren<ContextMenuItemProps>} props
18
18
  */
19
- function ContextMenuItem(props) {
19
+ function ContextMenuItem({
20
+ onMouseEnter = () => undefined,
21
+ onMouseLeave = () => undefined,
22
+ onClick,
23
+ className,
24
+ children,
25
+ }) {
20
26
  return (
21
27
  <div
22
- data-menu-item-for={
23
- typeof props.children === 'string' ? props.children : null
24
- }
25
- className={clsx(classes['context-menu-item'], props.className)}
26
- onClick={(e) => props.onClick(e)}
27
- onMouseEnter={(e) => props.onMouseEnter(e)}
28
- onMouseLeave={(e) => props.onMouseLeave(e)}
28
+ data-menu-item-for={typeof children === 'string' ? children : null}
29
+ className={clsx(classes['context-menu-item'], className)}
30
+ onClick={(e) => onClick(e)}
31
+ onMouseEnter={(e) => onMouseEnter(e)}
32
+ onMouseLeave={(e) => onMouseLeave(e)}
29
33
  // biome-ignore lint/a11y/useSemanticElements:
30
34
  role="button"
31
35
  tabIndex={0}
32
36
  >
33
- <span className={classes['context-menu-span']}>{props.children}</span>
37
+ <span className={classes['context-menu-span']}>{children}</span>
34
38
  </div>
35
39
  );
36
40
  }
37
41
 
38
- ContextMenuItem.defaultProps = {
39
- onMouseEnter: () => undefined,
40
- onMouseLeave: () => undefined,
41
- };
42
-
43
42
  ContextMenuItem.propTypes = {
44
43
  children: PropTypes.node.isRequired,
45
44
  onClick: PropTypes.func.isRequired,
@@ -10,53 +10,62 @@ import withModal from './hocs/with-modal';
10
10
 
11
11
  import classes from '../styles/Dialog.module.scss';
12
12
 
13
- function Dialog(props) {
13
+ function Dialog({
14
+ cancelTitle = 'Cancel',
15
+ hide = false,
16
+ maxHeight = false,
17
+ okayOnly = false,
18
+ okayTitle = 'Ok',
19
+ modal,
20
+ onCancel,
21
+ onOkay,
22
+ title,
23
+ cancelShortcut,
24
+ okayShortcut,
25
+ children,
26
+ }) {
14
27
  const handleCancel = () => {
15
- props.modal.close();
16
- if (props.onCancel) props.onCancel();
28
+ modal.close();
29
+ if (onCancel) onCancel();
17
30
  };
18
31
 
19
32
  const handleOkay = () => {
20
- props.modal.close();
21
- if (props.onOkay) props.onOkay();
33
+ modal.close();
34
+ if (onOkay) onOkay();
22
35
  };
23
36
 
24
37
  return (
25
- <Modal closeButton={false} hide={props.hide} maxHeight={props.maxHeight}>
38
+ <Modal closeButton={false} hide={hide} maxHeight={maxHeight}>
26
39
  <>
27
40
  <header className={classes['dialog-header']}>
28
- <h3>{props.title}</h3>
41
+ <h3>{title}</h3>
29
42
  <Button onClick={handleCancel}>
30
43
  <Cross />
31
44
  </Button>
32
45
  </header>
33
- {props.maxHeight ? (
46
+ {maxHeight ? (
34
47
  <main
35
- className={clsx(
36
- props.maxHeight && classes['dialog-main-max-height'],
37
- )}
48
+ className={clsx(maxHeight && classes['dialog-main-max-height'])}
38
49
  >
39
- {props.children}
50
+ {children}
40
51
  </main>
41
52
  ) : (
42
- <main>{props.children}</main>
53
+ <main>{children}</main>
43
54
  )}
44
55
  <footer
45
56
  className={
46
- classes[
47
- props.maxHeight ? 'dialog-footer-max-height' : 'dialog-footer'
48
- ]
57
+ classes[maxHeight ? 'dialog-footer-max-height' : 'dialog-footer']
49
58
  }
50
59
  >
51
- {props.okayOnly ? (
60
+ {okayOnly ? (
52
61
  <div />
53
62
  ) : (
54
- <Button onClick={handleCancel} shortcut={props.cancelShortcut}>
55
- {props.cancelTitle}
63
+ <Button onClick={handleCancel} shortcut={cancelShortcut}>
64
+ {cancelTitle}
56
65
  </Button>
57
66
  )}
58
- <Button onClick={handleOkay} shortcut={props.okayShortcut}>
59
- {props.okayTitle}
67
+ <Button onClick={handleOkay} shortcut={okayShortcut}>
68
+ {okayTitle}
60
69
  </Button>
61
70
  </footer>
62
71
  </>
@@ -64,14 +73,6 @@ function Dialog(props) {
64
73
  );
65
74
  }
66
75
 
67
- Dialog.defaultProps = {
68
- cancelTitle: 'Cancel',
69
- hide: false,
70
- maxHeight: false,
71
- okayOnly: false,
72
- okayTitle: 'Ok',
73
- };
74
-
75
76
  Dialog.propTypes = {
76
77
  cancelShortcut: PropTypes.string,
77
78
  cancelTitle: PropTypes.string,
@@ -7,9 +7,8 @@ import * as PIXI from 'pixi.js';
7
7
  import PropTypes from 'prop-types';
8
8
  import createPubSub, { globalPubSub } from 'pub-sub-es';
9
9
  import React from 'react';
10
- import ReactGridLayout from 'react-grid-layout';
10
+ import ReactGridLayout, { getCompactor } from 'react-grid-layout';
11
11
  import slugid from 'slugid';
12
- import parse from 'url-parse';
13
12
  import vkbeautify from 'vkbeautify';
14
13
 
15
14
  import ChromosomeInfo from './ChromosomeInfo';
@@ -290,7 +289,6 @@ class HiGlassComponent extends React.Component {
290
289
  this.pluginDataFetchers = pluginDataFetchers;
291
290
 
292
291
  this.state = {
293
- currentBreakpoint: 'lg',
294
292
  width: 0,
295
293
  height: 0,
296
294
  rowHeight,
@@ -555,10 +553,18 @@ class HiGlassComponent extends React.Component {
555
553
  this.fitPixiToParentContainer();
556
554
 
557
555
  // keep track of the width and height of this element, because it
558
- // needs to be reflected in the size of our drawing surface
556
+ // needs to be reflected in the size of our drawing surface.
557
+ // Also measure initial width so that ReactGridLayout renders grid items
558
+ // with valid dimensions on the first mounted render. Without this,
559
+ // width=0 causes react-grid-layout v2 to calculate negative column
560
+ // widths, and TiledPlot's ResizeSensor fails to detect the subsequent
561
+ // resize from zero to the actual size.
562
+ const [initialWidth, initialHeight] = getElementDim(this.element);
559
563
  this.setState({
560
564
  svgElement: this.svgElement,
561
565
  canvasElement: this.canvasElement,
566
+ ...(initialWidth > 0 ? { width: initialWidth } : {}),
567
+ ...(initialHeight > 0 ? { height: initialHeight } : {}),
562
568
  });
563
569
 
564
570
  this.waitForDOMAttachment(() => {
@@ -588,11 +594,11 @@ class HiGlassComponent extends React.Component {
588
594
  }
589
595
 
590
596
  getTrackObject(viewUid, trackUid) {
591
- return this.tiledPlots[viewUid].trackRenderer.getTrackObject(trackUid);
597
+ return this.tiledPlots[viewUid]?.trackRenderer?.getTrackObject(trackUid);
592
598
  }
593
599
 
594
600
  getTrackRenderer(viewUid) {
595
- return this.tiledPlots[viewUid].trackRenderer;
601
+ return this.tiledPlots[viewUid]?.trackRenderer;
596
602
  }
597
603
 
598
604
  UNSAFE_componentWillReceiveProps(newProps) {
@@ -985,12 +991,6 @@ class HiGlassComponent extends React.Component {
985
991
  this.resizeHandler();
986
992
  }
987
993
 
988
- onBreakpointChange(breakpoint) {
989
- this.setState({
990
- currentBreakpoint: breakpoint,
991
- });
992
- }
993
-
994
994
  handleOverlayMouseEnter(uid) {
995
995
  this.setState({
996
996
  mouseOverOverlayUid: uid,
@@ -1316,7 +1316,7 @@ class HiGlassComponent extends React.Component {
1316
1316
  ) > epsilon;
1317
1317
 
1318
1318
  const hasBrushMoved =
1319
- sourceTrack.options &&
1319
+ sourceTrack?.options &&
1320
1320
  lockedTrack.options &&
1321
1321
  typeof sourceTrack.options.scaleStartPercent !== 'undefined' &&
1322
1322
  typeof sourceTrack.options.scaleEndPercent !== 'undefined' &&
@@ -4196,6 +4196,7 @@ class HiGlassComponent extends React.Component {
4196
4196
  }
4197
4197
 
4198
4198
  triggerViewChange() {
4199
+ if (!this.mounted) return;
4199
4200
  const viewsString = this.getViewsAsString();
4200
4201
  this.viewChangeListener.forEach((callback) => callback(viewsString));
4201
4202
  }
@@ -5317,8 +5318,29 @@ class HiGlassComponent extends React.Component {
5317
5318
  />
5318
5319
  ) : null;
5319
5320
 
5321
+ // Constrain the grid item child to the expected pixel height
5322
+ // derived from view.layout.h. react-grid-layout v2 syncs its
5323
+ // internal layout state via useEffect (async), so after a layout
5324
+ // change (e.g. track removal) the grid item's DOM dimensions can
5325
+ // be stale (too large). maxHeight ensures TiledPlot.measureSize()
5326
+ // reads the correct (new) height even before the grid re-syncs.
5327
+ const marginY = this.isEditable() ? 10 : 0;
5328
+ const expectedPixelHeight = view.layout
5329
+ ? Math.round(
5330
+ this.state.rowHeight * view.layout.h +
5331
+ Math.max(0, view.layout.h - 1) * marginY,
5332
+ )
5333
+ : undefined;
5334
+
5320
5335
  return (
5321
- <div key={view.uid}>
5336
+ <div
5337
+ key={view.uid}
5338
+ style={
5339
+ expectedPixelHeight !== undefined
5340
+ ? { maxHeight: expectedPixelHeight, overflow: 'hidden' }
5341
+ : undefined
5342
+ }
5343
+ >
5322
5344
  <div
5323
5345
  ref={(c) => {
5324
5346
  this.tiledAreasDivs[view.uid] = c;
@@ -5360,34 +5382,30 @@ class HiGlassComponent extends React.Component {
5360
5382
 
5361
5383
  const gridLayout = (
5362
5384
  <ReactGridLayout
5363
- // Reserved props
5364
- ref={(c) => {
5365
- this.gridLayout = c;
5385
+ gridConfig={{
5386
+ cols: 12,
5387
+ rowHeight: this.state.rowHeight,
5388
+ // for some reason, this becomes 40 within the react-grid component
5389
+ // (try resizing the component to see how much the height changes)
5390
+ // Programming by coincidence FTW :-/
5391
+ margin: this.isEditable() ? [10, 10] : [0, 0],
5392
+ containerPadding: [containerPaddingX, containerPaddingY],
5393
+ }}
5394
+ dragConfig={{
5395
+ enabled: this.isEditable(),
5396
+ handle: `.${stylesMTHeader['multitrack-header-grabber']}`,
5397
+ }}
5398
+ resizeConfig={{
5399
+ enabled: this.isEditable(),
5366
5400
  }}
5367
- // Custom props
5368
- cols={12}
5369
- containerPadding={[containerPaddingX, containerPaddingY]}
5370
- draggableHandle={`.${stylesMTHeader['multitrack-header-grabber']}`}
5371
- isDraggable={this.isEditable()}
5372
- isResizable={this.isEditable()}
5401
+ compactor={getCompactor(
5402
+ this.state.viewConfig.compactLayout === false ? null : 'vertical',
5403
+ )}
5373
5404
  layout={layouts}
5374
- margin={this.isEditable() ? [10, 10] : [0, 0]}
5375
- measureBeforeMount={false}
5376
- onBreakpointChange={this.onBreakpointChange.bind(this)}
5377
5405
  onDragStart={this.handleDragStart.bind(this)}
5378
5406
  onDragStop={this.handleDragStop.bind(this)}
5379
5407
  onLayoutChange={this.handleLayoutChange.bind(this)}
5380
5408
  onResize={this.resizeHandler.bind(this)}
5381
- rowHeight={this.state.rowHeight}
5382
- // for some reason, this becomes 40 within the react-grid component
5383
- // (try resizing the component to see how much the height changes)
5384
- // Programming by coincidence FTW :-/
5385
- // WidthProvider option
5386
- // I like to have it animate on mount. If you don't, delete
5387
- // `useCSSTransforms` (it's default `true`)
5388
- // and set `measureBeforeMount={true}`.
5389
- useCSSTransforms={this.mounted}
5390
- verticalCompact={this.state.viewConfig.compactLayout}
5391
5409
  width={this.state.width}
5392
5410
  >
5393
5411
  {this.tiledAreas}
@@ -1,30 +1,59 @@
1
1
  // @ts-nocheck
2
+
3
+ import { useSortable } from '@dnd-kit/sortable';
4
+ import { CSS } from '@dnd-kit/utilities';
2
5
  import React from 'react';
3
- import { SortableElement } from 'react-sortable-hoc';
4
6
 
5
7
  import HorizontalTrack from './HorizontalTrack';
6
8
 
7
- const HorizontalItem = SortableElement((props) => {
9
+ function HorizontalItem(props) {
10
+ const {
11
+ attributes,
12
+ listeners,
13
+ setNodeRef,
14
+ transform,
15
+ transition,
16
+ isDragging,
17
+ } = useSortable({ id: props.uid });
18
+
19
+ const adjustedTransform = transform ? { ...transform, x: 0 } : transform;
20
+
21
+ const style = {
22
+ transform: CSS.Transform.toString(adjustedTransform),
23
+ transition,
24
+ ...(isDragging
25
+ ? {
26
+ zIndex: 10,
27
+ background: 'rgba(0, 0, 0, 0.1)',
28
+ border: '2px dashed rgba(0, 0, 0, 0.3)',
29
+ borderRadius: 4,
30
+ }
31
+ : {}),
32
+ };
33
+
8
34
  return (
9
- <HorizontalTrack
10
- className={props.className}
11
- editable={props.editable}
12
- handleConfigTrack={props.handleConfigTrack}
13
- handleResizeTrack={props.handleResizeTrack}
14
- height={props.height}
15
- isCollapsed={props.isCollapsed}
16
- item={props.item}
17
- onAddSeries={props.onAddSeries}
18
- onCollapseTrack={props.onCollapseTrack}
19
- onExpandTrack={props.onExpandTrack}
20
- onCloseTrack={props.onCloseTrack}
21
- onCloseTrackMenuOpened={props.onCloseTrackMenuOpened}
22
- onConfigTrackMenuOpened={props.onConfigTrackMenuOpened}
23
- resizeHandles={props.resizeHandles}
24
- uid={props.uid}
25
- width={props.width}
26
- />
35
+ <div ref={setNodeRef} style={style}>
36
+ <HorizontalTrack
37
+ className={props.className}
38
+ dragHandleProps={{ ...attributes, ...listeners }}
39
+ editable={props.editable}
40
+ handleConfigTrack={props.handleConfigTrack}
41
+ handleResizeTrack={props.handleResizeTrack}
42
+ height={props.height}
43
+ isCollapsed={props.isCollapsed}
44
+ item={props.item}
45
+ onAddSeries={props.onAddSeries}
46
+ onCollapseTrack={props.onCollapseTrack}
47
+ onExpandTrack={props.onExpandTrack}
48
+ onCloseTrack={props.onCloseTrack}
49
+ onCloseTrackMenuOpened={props.onCloseTrackMenuOpened}
50
+ onConfigTrackMenuOpened={props.onConfigTrackMenuOpened}
51
+ resizeHandles={props.resizeHandles}
52
+ uid={props.uid}
53
+ width={props.width}
54
+ />
55
+ </div>
27
56
  );
28
- });
57
+ }
29
58
 
30
59
  export default HorizontalItem;
@@ -17,6 +17,7 @@ class HorizontalTrack extends MoveableTrack {
17
17
  // center.
18
18
  expandCollapseAvailable={true}
19
19
  configMenuVisible={this.props.item.configMenuVisible}
20
+ dragHandleProps={this.props.dragHandleProps}
20
21
  imgStyleAdd={STYLES}
21
22
  imgStyleClose={STYLES}
22
23
  imgStyleMove={STYLES}
@@ -1,8 +1,8 @@
1
1
  // @ts-nocheck
2
2
 
3
+ import { arrayMove } from '@dnd-kit/sortable';
3
4
  import PropTypes from 'prop-types';
4
5
  import React from 'react';
5
- import { arrayMove } from 'react-sortable-hoc';
6
6
 
7
7
  class ListWrapper extends React.Component {
8
8
  constructor({ items }) {
@@ -19,8 +19,7 @@ class ListWrapper extends React.Component {
19
19
  });
20
20
  }
21
21
 
22
- onSortStart({ node, index, collection }, e) {
23
- e.stopImmediatePropagation();
22
+ onSortStart({ index }) {
24
23
  const { onSortStart } = this.props;
25
24
  this.setState({ isSorting: true });
26
25
 
@@ -29,9 +28,6 @@ class ListWrapper extends React.Component {
29
28
  }
30
29
 
31
30
  this.sortingIndex = index;
32
-
33
- this.sortStartTop = e.offsetTop;
34
- this.sortStartLeft = e.offsetLeft;
35
31
  }
36
32
 
37
33
  onSortMove() {}
@@ -39,14 +35,15 @@ class ListWrapper extends React.Component {
39
35
  onSortEnd({ oldIndex, newIndex }) {
40
36
  const { onSortEnd } = this.props;
41
37
  const { items } = this.state;
38
+ const sortedItems = arrayMove(items, oldIndex, newIndex);
42
39
 
43
40
  this.setState({
44
- items: arrayMove(items, oldIndex, newIndex),
41
+ items: sortedItems,
45
42
  isSorting: false,
46
43
  });
47
44
 
48
45
  if (onSortEnd) {
49
- onSortEnd(this.state.items);
46
+ onSortEnd(sortedItems);
50
47
  }
51
48
 
52
49
  this.sortingIndex = null;
@@ -63,15 +60,7 @@ class ListWrapper extends React.Component {
63
60
  onSortMove: this.onSortMove.bind(this),
64
61
  };
65
62
 
66
- return (
67
- <Component
68
- {...this.props}
69
- {...props}
70
- ref={(element) => {
71
- this.ref = element;
72
- }}
73
- />
74
- );
63
+ return <Component {...this.props} {...props} />;
75
64
  }
76
65
  }
77
66
 
@@ -9,42 +9,43 @@ import withModal from './hocs/with-modal';
9
9
 
10
10
  import classes from '../styles/Modal.module.scss';
11
11
 
12
- function Modal(props) {
12
+ function Modal({
13
+ closeButton = true,
14
+ hide = false,
15
+ maxHeight = false,
16
+ modal,
17
+ onClose,
18
+ children,
19
+ }) {
13
20
  const handleClose = () => {
14
- props.modal.close();
15
- if (props.onClose) props.onClose();
21
+ modal.close();
22
+ if (onClose) onClose();
16
23
  };
17
24
 
18
25
  return (
19
26
  <div
20
27
  className={clsx(classes['modal-background'], {
21
- [classes['modal-hide']]: props.hide,
28
+ [classes['modal-hide']]: hide,
22
29
  })}
23
30
  >
24
31
  <div className={classes['modal-wrap']}>
25
32
  <div
26
33
  className={clsx(classes['modal-window'], {
27
- [classes['modal-window-max-height']]: props.maxHeight,
34
+ [classes['modal-window-max-height']]: maxHeight,
28
35
  })}
29
36
  >
30
- {props.closeButton && (
37
+ {closeButton && (
31
38
  <Button onClick={handleClose}>
32
39
  <Cross />
33
40
  </Button>
34
41
  )}
35
- <div className={classes['modal-content']}>{props.children}</div>
42
+ <div className={classes['modal-content']}>{children}</div>
36
43
  </div>
37
44
  </div>
38
45
  </div>
39
46
  );
40
47
  }
41
48
 
42
- Modal.defaultProps = {
43
- closeButton: true,
44
- hide: false,
45
- maxHeight: false,
46
- };
47
-
48
49
  Modal.propTypes = {
49
50
  children: PropTypes.element.isRequired,
50
51
  closeButton: PropTypes.bool,