I'm trying to write Fable binders for the react-beautiful-dnd library, and I'm stuck on how to bind the Droppable component because it requires a function to create the children rather than have the children supplied directly like most other components.
I'm referencing this react example, where the component looks like this (see line 70 on that link):
render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={getListStyle(snapshot.isDraggingOver)}
>
{this.state.items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
So instead of just providing children directly, I need to provide a function of (DroppableProvided, DroppableStateSnapshot) -> ReactElement. Here is what I have so far:
type DroppableStateSnapshot = {
isDraggingOver : bool
}
type DroppableProvided = {
innerRef : Browser.Types.Element -> unit
}
type IDroppableAttribute = interface end
type droppable =
static member inline droppableId (id : string) = unbox<IDroppableAttribute>("droppableId", id)
static member inline childrenFn (f : (DroppableProvided * DroppableStateSnapshot) -> ReactElement) =
unbox<IDroppableAttribute>("children", f)
[<Erase>]
type Droppable() =
static member inline droppable (id : string) props (childrenFn: (DroppableProvided * DroppableStateSnapshot) -> ReactElement) =
let _id = droppable.droppableId id
let f = droppable.childrenFn childrenFn
let props = keyValueList CaseRules.LowerFirst (_id :: (f :: props))
ofImport "Droppable" "react-beautiful-dnd" props []
I directly created the snapshot and provided types instead of trying to import them because I'm pretty sure I just need something with the right properties as the arguments to the childrenFn function.
Finally, here is a component using the droppable component I defined, providing a function to create the child element:
[<ReactComponent>]
let TestDroppable() =
Droppable.droppable "test-droppable" [] (fun (provided, snapshot) ->
div [ Ref provided.innerRef ] [
BuildDraggable 0 [p [] [ str "This is the first item"]]
BuildDraggable 1 [p [] [ str "This is the second item"]]
BuildDraggable 2 [p [] [ str "This is the third item"]]
BuildDraggable 3 [p [] [ str "This is the fourth item"]]
])
When I run this in the browser, I get an error of Uncaught TypeError: provided is undefined, pointing to the line where I set the Ref property. I'm doing this because the docs say you have to provide a ref value (see the "Children Function" section of the Droppable docs I linked). The stack trace shows this function being invoked from react-beautiful-dnd, so I can at least be reasonably sure I'm loading & executing the library.
So it seems like the function is being invoked by the framework correctly, but with null arguments. I don't know if this error is because of binding wrong, building the component wrong, not importing the right things, or some other combination of errors when working with the imported library.
The full source code is available here. I am putting this whole thing inside a DragDropContext, which seems to bind without issue.
I don't have any Fable/React experience, so I could be way off, but this F# code looks suspect to me:
In a normal runtime environment, this will throw an
InvalidCastException, because theTupletype doesn't implementIDroppableAttribute. I don't know what happens when it's translated to Javascript instead, but I suspect it won't work there either.