All files / spells asTweener.js

0% Statements 0/27
0% Branches 0/13
0% Functions 0/8
0% Lines 0/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                                                                                                                                                                                         
/*
 * Copyright (c) 2022-2023 Braun Nathanael
 *
 * This project is dual licensed under one of the following licenses:
 * - Creative Commons Attribution-NoDerivatives 4.0 International License.
 * - GNU AFFERO GENERAL PUBLIC LICENSE Version 3
 *
 * You should have received a copy of theses licenses along with this work.
 * If not, see <http://creativecommons.org/licenses/by-nd/4.0/> or <http://www.gnu.org/licenses/agpl-3.0.txt>.
 */
 
import React                from "react";
import Tweener              from '../comps/Tweener';
import TweenerContext       from '../comps/TweenerContext';
import { isReactComponent } from '../utils/react';
 
const SimpleObjectProto = ( {} ).constructor;
 
/**
 * TweenerHost — thin function component hosting a pure Tweener engine.
 *
 * Replaces the legacy Tweener.render() (Tweener no longer extends React.Component):
 *  - maps the parent engine from TweenerContext into engine._parentTweener — a
 *    ref-like idempotent write, read at event time only (drag tree traversal &
 *    named lookup), exactly like the legacy Consumer did
 *  - provides this engine to descendants
 *  - drives the engine lifecycle (componentDidMount/DidUpdate/WillUnmount)
 */
const TweenerHost = ( { oProps, forwardedRef, BaseComponent, tweenerOptions } ) => {
    const parentTweener = React.useContext(TweenerContext),
          engine        = React.useMemo(
              () => new Tweener({ oProps, forwardedRef, BaseComponent, tweenerOptions }),
              []
          ),
          didMount      = React.useRef(false);
 
    // same ref-like parent mapping the legacy class render() did
    engine._parentTweener = parentTweener;
 
    React.useEffect(
        () => {
            engine.componentDidMount();
            return () => engine.componentWillUnmount();
        }, []
    );
    // class-lifecycle parity: componentDidUpdate on every update render, not on mount
    React.useEffect(
        () => {
            if ( !didMount.current ) {
                didMount.current = true;
                return;
            }
            engine.componentDidUpdate();
        }
    );
 
    return <TweenerContext.Provider value={ engine }>
        <BaseComponent { ...oProps } ref={ engine._.rootRef } tweener={ engine }/>
    </TweenerContext.Provider>;
};
 
/**
 * asTweener decorator
 * @param argz
 * @returns {*}
 */
export default function asTweener( ...argz ) {
    
    let BaseComponent = ( !argz[ 0 ] || isReactComponent(argz[ 0 ]) ) && argz.shift(),
        options       = ( !argz[ 0 ] || argz[ 0 ] instanceof SimpleObjectProto ) && argz.shift() || {};
    if ( !BaseComponent ) {
        return function ( BaseComponent ) {
            return asTweener(BaseComponent, options)
        }
    }
    
    options = {
        wheelRatio    : 5,
        maxClickTm    : 200,
        maxClickOffset: 20,
        ...options,
    };
    
    
    let withRef         = React.forwardRef(( props, ref ) => {
        return <TweenerHost oProps={ props } forwardedRef={ ref }
                            BaseComponent={ BaseComponent }
                            tweenerOptions={ options }/>;
    });
    withRef.displayName = String.fromCharCode(0xD83E, 0xDDD9) + ( BaseComponent.displayName || BaseComponent.name );
    return withRef;
}