1
0

index.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import DragMoveFoundation, { DragMoveAdapter } from '@douyinfe/semi-foundation/dragMove/foundation';
  2. import BaseComponent from '../_base/baseComponent';
  3. import { ReactNode } from 'react';
  4. import PropTypes from 'prop-types';
  5. import React from 'react';
  6. import { isHTMLElement } from '../_base/reactUtils';
  7. import ReactDOM from 'react-dom';
  8. export interface DragMoveProps {
  9. // The element that triggers the drag event,default is element
  10. handler?: () => ReactNode;
  11. // The element that constrains the movement range, This element requires relative positioning
  12. constrainer?: () => ReactNode | 'parent';
  13. children?: ReactNode | undefined | any;
  14. onMouseDown?: (e: MouseEvent) => void;
  15. onMouseMove?: (e: MouseEvent) => void;
  16. onMouseUp?: (e: MouseEvent) => void;
  17. onTouchStart?: (e: TouchEvent) => void;
  18. onTouchMove?: (e: TouchEvent) => void;
  19. onTouchEnd?: (e: TouchEvent) => void;
  20. onTouchCancel?: (e: TouchEvent) => void;
  21. // Determine whether dragging is triggered when the mouse is pressed.
  22. // Return true to trigger dragging. Return false to not trigger dragging.
  23. allowMove?: (e: MouseEvent | TouchEvent, element: HTMLElement) => boolean;
  24. // customize move behavior
  25. customMove?: (e: HTMLElement, top: number, left: number) => void
  26. }
  27. export default class DragMove extends BaseComponent<DragMoveProps, null> {
  28. static propTypes = {
  29. children: PropTypes.node,
  30. handler: PropTypes.func,
  31. allowInputDrag: PropTypes.bool,
  32. constrainNode: PropTypes.func,
  33. onMouseDown: PropTypes.func,
  34. onMouseMove: PropTypes.func,
  35. onMouseUp: PropTypes.func,
  36. onTouchStart: PropTypes.func,
  37. onTouchMove: PropTypes.func,
  38. onTouchEnd: PropTypes.func,
  39. onTouchCancel: PropTypes.func,
  40. }
  41. static __SemiComponentName__ = "DragMove";
  42. static defaultProps = {
  43. allowInputDrag: false,
  44. };
  45. constructor(props: DragMoveProps) {
  46. super(props);
  47. this.elementRef = React.createRef();
  48. this.foundation = new DragMoveFoundation(this.adapter);
  49. }
  50. elementRef: React.RefObject<unknown>;
  51. foundation: DragMoveFoundation;
  52. get adapter(): DragMoveAdapter<DragMoveProps, null> {
  53. return {
  54. ...super.adapter,
  55. getDragElement: () => {
  56. let elementDom = this.elementRef.current;
  57. if (!isHTMLElement(elementDom)) {
  58. /* REACT_18_START */
  59. elementDom = ReactDOM.findDOMNode(elementDom as React.ReactInstance);
  60. /* REACT_18_END */
  61. /* REACT_19_START */
  62. // console.warn(`[Semi DragMove] elementDom should be a valid DOM element. The element's ref is not returning a DOM node. This may cause dragMove positioning issues. Please ensure the element has a proper ref that returns a DOM node.`);
  63. /* REACT_19_END */
  64. }
  65. return elementDom as HTMLElement;
  66. },
  67. getConstrainer: () => {
  68. const { constrainer } = this.props;
  69. if (typeof constrainer === 'string' && constrainer === 'parent') {
  70. return (this.elementRef.current as HTMLElement)?.parentNode as HTMLElement;
  71. } else if (typeof constrainer === 'function') {
  72. return constrainer() as any;
  73. } else {
  74. return null;
  75. }
  76. },
  77. getHandler: () => {
  78. const { handler } = this.props;
  79. if (typeof handler === 'function') {
  80. return handler() as any;
  81. } else {
  82. return this.adapter.getDragElement() as HTMLElement;
  83. }
  84. },
  85. notifyMouseDown: (e: MouseEvent) => {
  86. this.props.onMouseDown && this.props.onMouseDown(e);
  87. },
  88. notifyMouseMove: (e: MouseEvent) => {
  89. this.props.onMouseMove && this.props.onMouseMove(e);
  90. },
  91. notifyMouseUp: (e: MouseEvent) => {
  92. this.props.onMouseUp && this.props.onMouseUp(e);
  93. },
  94. notifyTouchStart: (e: TouchEvent) => {
  95. this.props.onTouchStart && this.props.onTouchStart(e);
  96. },
  97. notifyTouchMove: (e: TouchEvent) => {
  98. this.props.onTouchMove && this.props.onTouchMove(e);
  99. },
  100. notifyTouchEnd: (e: TouchEvent) => {
  101. this.props.onTouchEnd && this.props.onTouchEnd(e);
  102. },
  103. notifyTouchCancel: (e: TouchEvent) => {
  104. this.props.onTouchCancel && this.props.onTouchCancel(e);
  105. },
  106. };
  107. }
  108. componentDidMount(): void {
  109. this.foundation.init();
  110. }
  111. componentWillUnmount(): void {
  112. this.foundation.destroy();
  113. }
  114. render() {
  115. const { children } = this.props;
  116. const newChildren = React.cloneElement(children, {
  117. ref: (node: React.ReactNode) => {
  118. (this.elementRef as any).current = node;
  119. // Call the original ref, if any
  120. /* REACT_18_START */
  121. const { ref } = children as any;
  122. /* REACT_18_END */
  123. /* REACT_19_START */
  124. // const { ref } = (children as any).props;
  125. /* REACT_19_END */
  126. if (typeof ref === 'function') {
  127. ref(node);
  128. } else if (ref && typeof ref === 'object') {
  129. ref.current = node;
  130. }
  131. },
  132. });
  133. return newChildren;
  134. }
  135. }