1- // @flow
21import * as React from 'react' ;
32import PropTypes from 'prop-types' ;
43import ReactDOM from 'react-dom' ;
@@ -10,19 +9,18 @@ import DraggableCore from './DraggableCore';
109import type { ControlPosition , PositionOffsetControlPosition , DraggableCoreProps , DraggableCoreDefaultProps } from './DraggableCore' ;
1110import log from './utils/log' ;
1211import type { Bounds , DraggableEventHandler } from './utils/types' ;
13- import type { Element as ReactElement } from 'react' ;
12+ import type { ReactElement } from 'react' ;
1413
1514type DraggableState = {
1615 dragging : boolean ,
1716 dragged : boolean ,
1817 x : number , y : number ,
1918 slackX : number , slackY : number ,
2019 isElementSVG : boolean ,
21- prevPropsPosition : ? ControlPosition ,
20+ prevPropsPosition : ControlPosition | null ,
2221} ;
2322
24- export type DraggableDefaultProps = {
25- ...DraggableCoreDefaultProps ,
23+ export type DraggableDefaultProps = DraggableCoreDefaultProps & {
2624 axis : 'both' | 'x' | 'y' | 'none' ,
2725 bounds : Bounds | string | false ,
2826 defaultClassName : string ,
@@ -32,9 +30,7 @@ export type DraggableDefaultProps = {
3230 scale : number ,
3331} ;
3432
35- export type DraggableProps = {
36- ...DraggableCoreProps ,
37- ...DraggableDefaultProps ,
33+ export type DraggableProps = DraggableCoreProps & DraggableDefaultProps & {
3834 positionOffset : PositionOffsetControlPosition ,
3935 position : ControlPosition ,
4036} ;
@@ -43,11 +39,19 @@ export type DraggableProps = {
4339// Define <Draggable>
4440//
4541
46- class Draggable extends React . Component < DraggableProps , DraggableState > {
42+ // Public-facing prop shape: every prop is optional for consumers because the
43+ // required ones are supplied by `defaultProps`. This reproduces the historical
44+ // hand-written declaration `React.Component<Partial<DraggableProps>, {}>` so the
45+ // auto-generated .d.ts stays API-compatible with the old typings.
46+ class Draggable extends React . Component < Partial < DraggableProps > , DraggableState > {
4747
48- static displayName : ?string = 'Draggable' ;
48+ // Internally, defaultProps guarantees every prop is present at runtime, so we
49+ // narrow `this.props` back to the fully-resolved type for type-safe access.
50+ declare props : DraggableProps ;
4951
50- static propTypes : DraggableProps = {
52+ static displayName ?: string = 'Draggable' ;
53+
54+ static propTypes = {
5155 // Accepts all props <DraggableCore> accepts.
5256 ...DraggableCore . propTypes ,
5357
@@ -166,7 +170,11 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
166170 transform : dontSetMe
167171 } ;
168172
169- static defaultProps : DraggableDefaultProps = {
173+ // Typed as the full `DraggableProps` (not just the default-provided subset) so
174+ // React's JSX LibraryManagedAttributes treats EVERY prop as optional for
175+ // consumers, matching the historical hand-written typings. At runtime only the
176+ // default-able props are actually populated.
177+ static defaultProps : DraggableProps = {
170178 ...DraggableCore . defaultProps ,
171179 axis : 'both' ,
172180 bounds : false ,
@@ -175,11 +183,11 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
175183 defaultClassNameDragged : 'react-draggable-dragged' ,
176184 defaultPosition : { x : 0 , y : 0 } ,
177185 scale : 1
178- } ;
186+ } as unknown as DraggableProps ;
179187
180188 // React 16.3+
181189 // Arity (props, state)
182- static getDerivedStateFromProps ( { position} : DraggableProps , { prevPropsPosition} : DraggableState ) : ? Partial < DraggableState > {
190+ static getDerivedStateFromProps ( { position} : DraggableProps , { prevPropsPosition} : DraggableState ) : Partial < DraggableState > | null {
183191 // Set x/y if a new position is provided in props that is different than the previous.
184192 if (
185193 position &&
@@ -243,13 +251,17 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
243251
244252 // React 19 removed ReactDOM.findDOMNode, so nodeRef is now required.
245253 // For backward compatibility with React 18 and earlier, we still support findDOMNode if available.
246- findDOMNode ( ) : ? HTMLElement {
254+ findDOMNode ( ) : HTMLElement | null {
247255 if ( this . props ?. nodeRef ) {
248256 return this . props . nodeRef . current ;
249257 }
250- // ReactDOM.findDOMNode was removed in React 19
251- if ( typeof ReactDOM . findDOMNode === 'function ') {
252- return ReactDOM . findDOMNode ( this ) ;
258+ // ReactDOM.findDOMNode was removed from React 19's type defs (and runtime),
259+ // so access it dynamically to stay compatible with React 18 and earlier.
260+ const legacyReactDOM = ReactDOM as unknown as {
261+ findDOMNode ?: ( instance : unknown ) => HTMLElement | null ;
262+ } ;
263+ if ( typeof legacyReactDOM . findDOMNode === 'function' ) {
264+ return legacyReactDOM . findDOMNode ( this ) as HTMLElement | null ;
253265 }
254266 return null ;
255267 }
@@ -336,10 +348,10 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
336348 newState . y = y ;
337349 }
338350
339- this . setState ( newState ) ;
351+ this . setState ( newState as Pick < DraggableState , keyof DraggableState > ) ;
340352 } ;
341353
342- render ( ) : ReactElement < any > {
354+ render ( ) : ReactElement {
343355 const {
344356 axis,
345357 bounds,
@@ -385,8 +397,16 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
385397 style = createCSSTransform ( transformOpts , positionOffset ) ;
386398 }
387399
400+ // React.Children.only types its return as ReactElement<unknown>; narrow the
401+ // single child to an element carrying optional DOM style/className props so
402+ // we can read and merge them.
403+ const onlyChild = React . Children . only ( children ) as ReactElement < {
404+ className ?: string ,
405+ style ?: React . CSSProperties ,
406+ } > ;
407+
388408 // Mark with class while dragging
389- const className = clsx ( ( children . props . className || '' ) , defaultClassName , {
409+ const className = clsx ( ( onlyChild . props . className || '' ) , defaultClassName , {
390410 [ defaultClassNameDragging ] : this . state . dragging ,
391411 [ defaultClassNameDragged ] : this . state . dragged
392412 } ) ;
@@ -395,11 +415,11 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
395415 // This makes it flexible to use whatever element is wanted (div, ul, etc)
396416 return (
397417 < DraggableCore { ...draggableCoreProps } onStart = { this . onDragStart } onDrag = { this . onDrag } onStop = { this . onDragStop } >
398- { React . cloneElement ( React . Children . only ( children ) , {
418+ { React . cloneElement ( onlyChild , {
399419 className : className ,
400- style : { ...children . props . style , ...style } ,
420+ style : { ...onlyChild . props . style , ...style } ,
401421 transform : svgTransform
402- } ) }
422+ } as Partial < { className : string , style : React . CSSProperties , transform : string | null } > ) }
403423 </ DraggableCore >
404424 ) ;
405425 }
0 commit comments