This library is in BETA
Every minor update could contain a lot of breaking changes to the API. While upgrading to stay up to date is probably helpful, only do so if you have the time to fix things.
- At the scope you'll define the
DragDropContext
(see next step), add aDragAndDropModel
to your model, and aDragAndDropMsg
to your messages.
open Elmish.DragAndDrop
type Model = {
DragAndDrop : DragAndDropModel
ContentMap : Map<ContentKey, ContentValue>
}
type Msg =
| DndMsg of DragAndDropMsg
| InputChange of elementId : string * newValue : string
You will need a way to determine which content to render based on which element Id is given. It is recommended to use a map for this, storing your content in an easily accessible (and easily modifiable) way.
- Create a
DragDropContext
at the scope you want the user to be able to drag things. This does not have to be the scope they can drop things; for usability, consider putting this context towards the root of your application.
let mappedMsg msg = DndMsg msg
DragDropContext.context
model.DragAndDrop
(mappedMsg >> dispatch)
div [] [ dropAreaContent ]
- Create your
Draggables
. These consist of 2 parts: (1) theDraggable
itself, the element that will move around the screen and be moved to different locations in your collection(s); (2) theDragHandle
, the part of the element your user will click on to do these actions. In many cases, the entireDraggable
will be a drag handle, but if your elements contain things the user may want to interact with, such as an input box or a checkbox, then you'll want to use define a specificDragHandle
.
If the whole element is a handle for itself, then Draggable.SelfHandle
takes care of all your needs. If you want a specific part of the element to be the handle by which the element is dragged, then you want to create a DragHandle.Handle
element, and put that element inside a Draggable.InnerHandle
. The DragHandle
will need to reference the (unique) id of the Draggable
it drags.
Additionally, you'll want to configure your DragAndDropConfig
at the scope of any specific drag & drop, to define additional styles or properties to put on your various drag & drop elements.
Also note that when creating a draggable, you'll need to specify the Category
it belongs to. This allows you to create mutually exclusive categories in your drag & drop model that items cannot move between.
let dragAndDropConfig = {
DragAndDropConfig.Empty() with
DraggedElementStyles = Some [
MarginLeft -130.
MarginTop -50.
Opacity 0.8
Position PositionOptions.Fixed
Cursor "grabbing"
Background "#00ffff"
]
HoverPreviewElementStyles = Some [
Opacity 0.2
]
}
let dragAndDropCategoryKey = "default-category"
let createDragHandle dndModel draggableId dispatch handleContent : ReactElement =
let handleId = draggableId + "-handle"
DragHandle.Handle
dndModel
dragAndDropCategoryKey
draggableId
(mappedMsg >> dispatch)
div
[ Id handleId ]
handleContent
let createDraggable dndModel draggableId dispatch handleContent content =
let handle = createDragHandle dndModel draggableId dispatch handleContent
Draggable.InnerHandle
dndModel
dragAndDropCategoryKey
dragAndDropConfig
(mappedMsg >> dispatch)
draggableId
div
[]
[ Id draggableId ] //note that you must set the Id.
( handle :: content )
- Define one or more
DropArea
elements for yourDraggables
to live.
let draggables
let handleContent = p [] [ "Click here to drag!" ]
model.DragAndDrop.ElementIds()
|> List.concat // collect into a single list; this is just for example, you may need to keep distinct lists
|> List.map (fun rootElementId ->
// find the content based on the Id given by drag and drop. In this example, this returns pure content
// In most scenarios, you'll store some object, and map that to content at this step.
let content = model.ContentMap |> Map.find rootElementId
createDraggable dndModel rootelementId dispatch handleContent content
)
let dropArea =
DropArea.DropArea
model.DragAndDrop
dragAndDropCategoryKey
dragAndDropConfig
(MouseEventHandlers.Empty())
(mappedMsg >> dispatch)
"drop-area"
div
[]
draggables
With all that, you're good to go! There's a fair bit of setup, which arises from the amount of configurability this library aims to achieve.
See the Examples folder for complete, working examples of different types of setups.
Sometimes you want a drop area that accepts an item and invokes a function, but doesn't keep the item around. An example of this might be dragging an item to delete it, or dragging an item over some element to create some event (perhaps creating a new element in the process). To do this, create a drop area as normal, but supply custom MouseEventHandlers
to handle some of the events:
let onHover = (fun _ id _ -> printfn "on hover for drop area bucket %A triggered" id; SomeMsg id |> dispatch)
let onDrop = (fun _ id -> printfn "on drop for drop area bucket %A triggered" id; SomeOtherMsg id |> dispatch )
let mouseEventFuncs =
{ MouseEventHandlers.Empty() with
OnHoverEnter = Some onHover
OnDrop = Some onDrop
}
This sets handlers to listen for those events on the DropArea
you configure, whereas normally those listeners only exist on draggables. Note that you can use MouseEventHandlers
on any DropArea
, even if it has Draggables
inside it.
See the "Drag To Delete" example to see this in action.
Contributions Welcome
I don't currently have the time to fully implement all the features that may be needed. See the TODO.md
file for what currently needs to be done!
GitHub Actions |
---|
Package | Stable | Prerelease |
---|---|---|
Elmish.DragAndDrop |
Make sure the following requirements are installed on your system:
- dotnet SDK 3.0 or higher
- Mono if you're on Linux or macOS.
or
CONFIGURATION
will set the configuration of the dotnet commands. If not set, it will default to Release.CONFIGURATION=Debug ./build.sh
will result in-c
additions to commands such as indotnet build -c Debug
GITHUB_TOKEN
will be used to upload release notes and Nuget packages to GitHub.- Be sure to set this before releasing
DISABLE_COVERAGE
Will disable running code coverage metrics. AltCover can have severe performance degradation so it's worth disabling when looking to do a quicker feedback loop.DISABLE_COVERAGE=1 ./build.sh
> build.cmd <optional buildtarget> // on windows
$ ./build.sh <optional buildtarget>// on unix
The bin of your library should look similar to:
$ tree src/MyCoolNewLib/bin/
src/MyCoolNewLib/bin/
└── Debug
└── net50
├── MyCoolNewLib.deps.json
├── MyCoolNewLib.dll
├── MyCoolNewLib.pdb
└── MyCoolNewLib.xml
Clean
- Cleans artifact and temp directories.DotnetRestore
- Runs dotnet restore on the solution file.DotnetBuild
- Runs dotnet build on the solution file.DotnetTest
- Runs dotnet test on the solution file.GenerateCoverageReport
- Code coverage is run duringDotnetTest
and this generates a report via ReportGenerator.WatchTests
- Runs dotnet watch with the test projects. Useful for rapid feedback loops.GenerateAssemblyInfo
- Generates AssemblyInfo for libraries.DotnetPack
- Runs dotnet pack. This includes running Source Link.SourceLinkTest
- Runs a Source Link test tool to verify Source Links were properly generated.PublishToNuGet
- Publishes the NuGet packages generated inDotnetPack
to NuGet via paket push.GitRelease
- Creates a commit message with the Release Notes and a git tag via the version in theRelease Notes
.GitHubRelease
- Publishes a GitHub Release with the Release Notes and any NuGet packages.FormatCode
- Runs Fantomas on the solution file.BuildDocs
- Generates Documentation fromdocsSrc
and the XML Documentation Comments from your libraries insrc
.WatchDocs
- Generates documentation and starts a webserver locally. It will rebuild and hot reload if it detects any changes made todocsSrc
files, libraries insrc
, or thedocsTool
itself.ReleaseDocs
- Will stage, commit, and push docs generated in theBuildDocs
target.Release
- Task that runs all release type tasks such asPublishToNuGet
,GitRelease
,ReleaseDocs
, andGitHubRelease
. Make sure to read Releasing to setup your environment correctly for releases.
git add .
git commit -m "Scaffold"
git remote add origin https://github.com/user/MyCoolNewLib.git
git push -u origin master
-
paket config add-token "https://www.nuget.org" 4003d786-cc37-4004-bfdf-c4f3e8ef9b3a
- or set the environment variable
NUGET_TOKEN
to your key
- or set the environment variable
-
- You can then set the environment variable
GITHUB_TOKEN
to upload release notes and artifacts to github - Otherwise it will fallback to username/password
- You can then set the environment variable
-
Then update the
CHANGELOG.md
with an "Unreleased" section containing release notes for this version, in KeepAChangelog format.
NOTE: Its highly recommend to add a link to the Pull Request next to the release note that it affects. The reason for this is when the RELEASE
target is run, it will add these new notes into the body of git commit. GitHub will notice the links and will update the Pull Request with what commit referenced it saying "added a commit that referenced this pull request". Since the build script automates the commit message, it will say "Bump Version to x.y.z". The benefit of this is when users goto a Pull Request, it will be clear when and which version those code changes released. Also when reading the CHANGELOG
, if someone is curious about how or why those changes were made, they can easily discover the work and discussions.
Here's an example of adding an "Unreleased" section to a CHANGELOG.md
with a 0.1.0
section already released.
## [Unreleased]
### Added
- Does cool stuff!
### Fixed
- Fixes that silly oversight
## [0.1.0] - 2017-03-17
First release
### Added
- This release already has lots of features
[Unreleased]: https://github.com/user/MyCoolNewLib.git/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/user/MyCoolNewLib.git/releases/tag/v0.1.0
- You can then use the
Release
target, specifying the version number either in theRELEASE_VERSION
environment variable, or else as a parameter after the target name. This will:- update
CHANGELOG.md
, moving changes from theUnreleased
section into a new0.2.0
section- if there were any prerelease versions of 0.2.0 in the changelog, it will also collect their changes into the final 0.2.0 entry
- make a commit bumping the version:
Bump version to 0.2.0
and adds the new changelog section to the commit's body - publish the package to NuGet
- push a git tag
- create a GitHub release for that git tag
- update
macOS/Linux Parameter:
./build.sh Release 0.2.0
macOS/Linux Environment Variable:
RELEASE_VERSION=0.2.0 ./build.sh Release