How to specify the functions of external React components in Kotlin/JS?

I’m trying to use the react-signature-pad-wrapper npm package, in which the SignaturePad class has some functions which I need to use.

The problems I’m having are:

  1. I don’t know how to define those functions in the Kotlin wrapper I’m creating.

  2. I don’t know how to call these functions once I define them.

This is the wrapper I’ve created:

@JsModule("react-signature-pad-wrapper")
@JsNonModule
private external val module: dynamic

@Suppress("UnsafeCastFromDynamic")
val RSignaturePad: RClass<RSignaturePadProps> = module.default

external interface RSignaturePadProps : RProps {

    var width: Int
    var height: Int

    var options: RSignaturePadOptions

    var redrawOnResize: Boolean // default: false

    var debounceInterval: Number // default: 150

    var canvasProps: CanvasProps

}

external interface RSignaturePadOptions {

    /** Radius of a single dot. */
    var dotSize: Float

    /** Minimum width of a line. Defaults to 0.5. */
    var minWidth: Float

    /** Maximum width of a line. Defaults to 2.5. */
    var maxWidth: Float

    /** Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16. */
    var throttle: Int

    /** Add the next point only if the previous one is farther than x pixels. Defaults to 5.*/
    var minDistance: Int

    /**
     * Color used to clear the background. Can be any color format accepted by context.fillStyle.
     * Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)"
     * (opaque white) if you'd like to save signatures as JPEG images.
     */
    var backgroundColor: String

    /** Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black". */
    var penColor: String

    /** Weight used to modify new velocity based on the previous velocity. Defaults to 0.7. */
    var velocityFilterWeight: Float

    /**
     * Callback when stroke begin.
     *
     * The event is a [MouseEvent] or [Touch].
     */
    var onBegin: (event: Any) -> Unit

    /**
     * Callback when stroke end.
     *
     * The event is a [MouseEvent] or [Touch].
     */
    var onEnd: (event: Any) -> Unit

}

@Suppress("UnsafeCastFromDynamic", "FunctionName")
fun RSignaturePadOptions(): RSignaturePadOptions = js("{}")

external interface CanvasProps {

}

@Suppress("UnsafeCastFromDynamic", "FunctionName")
fun CanvasProps(): CanvasProps = js("{}")

After some trial and error I found a way to do this. I’ll document it below in case it helps someone in the future.

I did this to define the functions in the Kotlin wrapper:

@JsName("default")
external class RSignaturePad : Component<RSignaturePadProps, RState> {

    /** Get the original signature_pad instance. */
    val instance: dynamic

    val canvas: CANVAS


    override fun render(): dynamic

    /**
     * Returns `true` if canvas is empty, otherwise returns `false`.
     */
    fun isEmpty(): Boolean

    /**
     * Clears the canvas.
     */
    fun clear()

    /**
     * Returns signature image as data URL (see https://mdn.io/todataurl for the list of possible parameters).
     *
     * @param mimeType Type of image to generate (e.g., "image/jpeg"). Default is "image/png".
     */
    fun toDataURL(mimeType: String = definedExternally): String

    /**
     * Draws signature image from data URL.
     *
     * NOTE: This method does not populate internal data structure that represents drawn signature.
     * Thus, after using [fromDataURL], [toData] won't work properly.
     */
    fun fromDataURL(base64String: String)

    /**
     * Returns signature image as an array of point groups.
     */
    fun toData(): Array<PointGroup>

    /**
     * Draws signature image from an array of point groups
     */
    fun fromData(data: Array<PointGroup>)

    /**
     * Rebinds all event handlers.
     */
    fun on()

    /**
     * Unbinds all event handlers.
     */
    fun off()

    fun scaleCanvas()

}

There was a way to define this also using RClass instead of Component but using the latter seemed cleaner to me.

Once I had this, I got the RSignaturePad instance when using the component by calling the ref { } method.