NeoDrag
Partas.Solid.NeoDrag binds @neodrag/solid, for drag and drop. See its docs for usage. The binding has not been tested much.
Vanilla NeoDrag on Solid 2
@neodrag/vanilla has no framework dependency, so it works on Solid 2 today. Its whole API is one class, Draggable. [<Import>] on a class type imports the named export, and calling the constructor emits new Draggable(...). The options are an anonymous record, and the interface types the data NeoDrag passes to its drag callbacks.
open Browser.Types
[<Import("Draggable", "@neodrag/vanilla")>]
type Draggable(node: HTMLElement, options: obj) =
member _.updateOptions(options: obj) : unit = jsNative
member _.destroy() : unit = jsNative
type DragEventData =
abstract offsetX: float
abstract offsetY: float
The component creates the Draggable in onSettled, once the ref is set, and returns destroy as the cleanup. onDrag writes the offset into two signals. bounds = "parent" keeps the card inside its box. Passing a position makes the position controlled, so Reset can move the card back with updateOptions.
[<SolidComponent>]
let DragDemo () =
let x, setX = createSignal 0
let y, setY = createSignal 0
let mutable card: HTMLDivElement = JS.undefined
let mutable drag: Draggable = JS.undefined
onSettled (fun () ->
drag <-
Draggable(
card,
{| bounds = "parent"
position = {| x = 0; y = 0 |}
onDrag =
fun (data: DragEventData) ->
setX (int data.offsetX)
setY (int data.offsetY) |}
)
fun () -> drag.destroy ())
let reset () =
drag.updateOptions {| position = {| x = 0; y = 0 |} |}
setX 0
setY 0
div (style = "display: grid; gap: .75rem; justify-items: start; width: 100%") {
div (style = dragArea) {
div(style = dragCard).ref (card) {
div (style = dragTitle) { "Drag me" }
div (style = dragReadout) { $"x {x ()} · y {y ()}" }
}
}
button (class' = "p-btn p-btn--secondary", onClick = fun _ -> reset ()) { "Reset" }
}
export function DragDemo() {
const patternInput = createSignal(0);
const setX = patternInput[1];
const patternInput_1 = createSignal(0);
const setY = patternInput_1[1];
let card = void 0;
let drag = void 0;
onSettled(() => {
drag = (new Draggable(card, {
bounds: "parent",
onDrag: (data) => {
setX(~~data.offsetX);
setY(~~data.offsetY);
},
position: {
x: 0,
y: 0,
},
}));
return () => {
drag.destroy();
};
});
return <div style="display: grid; gap: .75rem; justify-items: start; width: 100%">
<div style={dragArea}>
<div style={dragCard}
ref={card}>
<div style={dragTitle}>
Drag me
</div>
<div style={dragReadout}>
{`x ${patternInput[0]()} · y ${patternInput_1[0]()}`}
</div>
</div>
</div>
<button class="p-btn p-btn--secondary"
onClick={(_arg) => {
drag.updateOptions({
position: {
x: 0,
y: 0,
},
});
setX(0);
setY(0);
}}>
Reset
</button>
</div>;
}
