Last active
October 11, 2024 06:00
-
-
Save JordanMarr/2d478dfecf9e91bba953c649fe2ff458 to your computer and use it in GitHub Desktop.
Fable bindings for "react-dnd" using HTML5 provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module DragDropPage | |
open Feliz | |
open Fable.React | |
open Fable.React.Props | |
open ReactDND | |
type Language = { | |
Name: string | |
} | |
[<ReactComponent>] | |
let DraggableLanguage (lang: Language) = | |
let dragState, drag, preview = ReactDND.useDrag [ | |
DragSpec.Item { ``type`` = "Language"; dragSrc = lang } | |
DragSpec.Collect (fun mon -> { isDragging = mon.isDragging() }) | |
//DragSpec.End (fun dragItem mon -> printf "DragEnd: %A; Mon.DropTarget: %A" dragItem mon) | |
] | |
div [ | |
Ref drag | |
Style[Border "1px solid gray"; Padding "4px"; BackgroundColor (if dragState.isDragging then "yellow" else "white")] | |
Key lang.Name | |
] [ | |
str lang.Name | |
] | |
[<ReactComponent>] | |
let DropTarget () = | |
let selectedLang, setSelectedLang = React.useState<Language option>(None) | |
let dropState, drop = ReactDND.useDrop [ | |
DropSpec.Accept "Language" | |
DropSpec.Collect (fun mon -> { canDrop = mon.canDrop(); isOver = mon.isOver() }) | |
DropSpec.Drop (fun (dragItem: DragItem<Language>) -> | |
setSelectedLang(Some dragItem.dragSrc) | |
) | |
] | |
div [ | |
Ref drop | |
Style[ | |
BackgroundColor "whitesmoke" | |
Padding "20px" | |
BackgroundColor (if dropState.isOver then "lightgreen" else "white") | |
] | |
] [ | |
selectedLang | |
|> Option.map (fun l -> sprintf "You selected: %s" l.Name) | |
|> Option.defaultValue "Drag your .net language of choice here" | |
|> str | |
] | |
[<ReactComponent>] | |
let Page () = | |
let languages, setLanguages = React.useState<Language list>([]) | |
React.useEffectOnce(fun () -> | |
[ "C#"; "F#"; "VB" ] | |
|> List.map (fun l -> { Name = l }) | |
|> setLanguages | |
) | |
div [Style[Width "300px"; MarginLeft "auto"; MarginRight "auto"]] [ | |
ReactDND.dndProvider [ | |
// DndProviderProps.Backend html5Backend // optional | |
// DndProviderProps.Backend mouseBackend // optional (for svg) | |
] [ | |
dropTarget() | |
[ "F#"; "C#"; "VB" ] | |
|> List.map (fun l -> { Name = l }) | |
|> List.map (fun lang -> draggableLanguage {| lang = lang |}) | |
|> ofList | |
] | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Custom bindings for "react-dnd"; authored by jmarr on Feb 2020. | |
/// This expects that "react-dnd" and "react-dnd-html5-backend" are both installed. | |
module ReactDND | |
open Fable.Core | |
open Fable.Core.JsInterop | |
open Fable.React | |
open Fable.React.Props | |
open System.Text.RegularExpressions | |
let private kvl xs = JsInterop.keyValueList CaseRules.LowerFirst xs | |
type CollectedDragProps = { | |
isDragging: bool | |
} | |
type CollectedDropProps = { | |
isOver: bool | |
canDrop: bool | |
} | |
type [<AllowNullLiteral>] IDragMonitor = | |
abstract member isDragging: unit -> bool | |
type [<AllowNullLiteral>] IDropMonitor = | |
abstract member isOver: unit -> bool | |
abstract member canDrop: unit -> bool | |
abstract member getDropResult: unit -> obj | |
// Provider / Context / Backends | |
// Uncomment optional backends below if needed. | |
// HTML5 backend | |
// npm package: "react-dnd-html5-backend" | |
//[<Import("default", from="react-dnd-html5-backend")>] | |
//let html5Backend: obj = jsNative | |
// This backend supports SVG drag & drop. | |
// npm package: "react-dnd-mouse-backend" | |
//[<Import("default", from="react-dnd-mouse-backend")>] | |
//let mouseBackend: obj = jsNative | |
type DndProviderProps = | |
| Backend of obj | |
let dndProvider (props: DndProviderProps list) = ofImport "DndProvider" "react-dnd" (kvl props) | |
/// Provides a Drag and Drop context using the "react-dnd-html-backend" provider. | |
//let dndProviderHtml5 (props: DndProviderProps list) = dndProvider [DndProviderProps.Backend html5Backend] | |
// | |
// UseDrag | |
// | |
[<RequireQualifiedAccess>] | |
type DragSpec<'DragSrc> = | |
/// Specifies info about the drag source object and type (required). | |
| Item of DragItem<'DragSrc> | |
/// An optional handler to gather state. | |
| Collect of (IDragMonitor -> CollectedDragProps) | |
/// Provides an optional handler if actions are necessary at the end of a drag operation. | |
| End of (DragItem<'DragSrc> -> IDragMonitor -> unit) | |
and DragItem<'DragSrc> = { | |
``type``: string | |
dragSrc: 'DragSrc | |
} | |
let private useDragImpl: spec: obj -> collectedProps: CollectedDragProps * drag: (Browser.Types.Element -> unit) * preview: obj = import "useDrag" "react-dnd" | |
let useDrag<'TItem>(spec: DragSpec<'TItem> list) = useDragImpl (kvl spec) | |
// | |
// UseDrop | |
// | |
[<RequireQualifiedAccess>] | |
type DropSpec<'DragSrc, 'DropTgt> = | |
/// Specifies which kinds of drag item types are accepted (required). | |
| Accept of string | |
/// An optional handler to gather state. | |
| Collect of (IDropMonitor -> CollectedDropProps) | |
/// Provides an optional handler for the drop event. | |
| Drop of (DragItem<'DragSrc> -> 'DropTgt) | |
let private useDropImpl: spec: obj -> collectedProps: CollectedDropProps * drop: (Browser.Types.Element -> unit) = import "useDrop" "react-dnd" | |
let useDrop (spec: DropSpec<'DragSrc, 'DragTgt> list) = useDropImpl (kvl spec) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment