{"id":32191459,"url":"https://github.com/g7s/skidder","last_synced_at":"2025-10-22T01:41:22.374Z","repository":{"id":62432751,"uuid":"257606793","full_name":"g7s/skidder","owner":"g7s","description":"Drag and drop for ClojureScript","archived":false,"fork":false,"pushed_at":"2021-02-05T10:23:49.000Z","size":37,"stargazers_count":18,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-19T05:30:16.299Z","etag":null,"topics":["clojurescript","drag-and-drop"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/g7s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-21T13:35:52.000Z","updated_at":"2023-01-28T16:47:01.000Z","dependencies_parsed_at":"2022-11-01T21:01:30.536Z","dependency_job_id":null,"html_url":"https://github.com/g7s/skidder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/g7s/skidder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g7s%2Fskidder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g7s%2Fskidder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g7s%2Fskidder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g7s%2Fskidder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/g7s","download_url":"https://codeload.github.com/g7s/skidder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g7s%2Fskidder/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280299520,"owners_count":26306928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojurescript","drag-and-drop"],"created_at":"2025-10-22T01:41:21.173Z","updated_at":"2025-10-22T01:41:22.361Z","avatar_url":"https://github.com/g7s.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# skidder\n\nClojureScript drag and drop library.\n\n\n## Installation\n\n[![Clojars Project](https://img.shields.io/clojars/v/g7s/skidder.svg)](https://clojars.org/g7s/skidder)\n\nTo install, add the following to your project `:dependencies`:\n\n    [g7s/skidder \"0.1.0\"]\n\n\n## Documentation\n\n`skidder` is a system that you can start, stop and listen for changes\n\n```clojure\n\n(ns test.app\n  (:require\n   [skidder.core :as skidder]))\n\n(skidder/start! :html5)\n\n```\n\nWhen skidder starts it adds some (required) event listeners. Those listeners will be\nremoved when the system stops\n\n```clojure\n\n(skidder/stop!)\n\n```\n\nskidder is built around [protocols](src/skidder/protocols.cljs). The most important\nprotocols are `DragSource`, `DropTarget`, `StateInfo` and `Connector`.\n\n\n### Drag Source\n\nA drag source is anything that can be dragged (usually a DOM node). Implementors of\nthe `DragSource` protocol will have to implement the following functions\n\n```clojure\n(ds-type [this] \"Return the type of this drag source.\")\n(can-drag? [this state-info ds-id] \"Return whether this drag source can be dragged.\")\n(on-begin-drag [this state-info ds-id] \"Called when the drag begins. The returned value will be available at the drop result.\")\n(on-end-drag [this state-info ds-id] \"Called when the drag ends.\")\n```\n\nNOTES:\n\n1. `ds-type` - Will be checked against a drop target's `accepts` set\n1. `can-drag?` - If this function returns `false` then the element will not be dragged.\n1. `on-begin-drag` - Because its return value will be available when its dropped on a target, you\n    should return drag source data that you might need to execute some action on drop.\n\n\n### Drop Target\n\nA drop target is anything that can accept a drag source element (usually a DOM node). Implementors\nof the `DropTarget` protocol will have to implement the following functions\n\n```clojure\n(exclusive? [this] \"Return whether this drop target should be treated as an exclusive one.\")\n(accepts [this] \"Return a set of drag source types that this drop target accepts.\")\n(can-drop? [this state-info ds-id dt-id] \"Return whether a drop can happen on this drop target.\")\n(on-hover [this state-info dt-id] \"Called when a hover happens on this drop target.\")\n(on-enter [this state-info dt-id] \"Called when an enter happens on this drop target.\")\n(on-leave [this state-info dt-id] \"Called when a leave happens on this drop target.\")\n(on-drop [this state-info ds-id dt-id] \"Called when a drop happens on this drop target.\")\n```\n\nNOTES:\n\n1. `exclusive` - If an exclusive drop target is contained inside another drop target then the drop will\nhappen only on this drop target\n1. `on-hover` - This will be called very often when above a target so don't do anything expensive in there\n\n\n### State Information\n\nWhen a skidder system starts it holds information about all kinds of things like which\ndrag sources are registered, which drop targets are registered, is a dragging operation in progress?,\nis it hovering over some drop target? etc. To see what queries are available for the state information\nyou can refer to the `StateInfo` protocol. One can get the state information from skidder\n(assuming that the system has started)\n\n```clojure\n(skidder/state-info)\n```\n\n### Connector\n\nThe connector is used to connect a drag source or a drop target to a DOM element. One can access the\nconnector of a started system from skidder\n\n```clojure\n(skidder/connector)\n```\n\nskidder provides a `:html5` connector that makes use of the HTML5 Drag and Drop API.\n\n\n## Example (React)\n\nSuppose that we have a React application and we want to incorporate the skidder library.\nFirst we need a way to create and register a drag source and a drop target:\n\n```clojure\n(ns react.example\n  (:require\n   [skidder.protocols :as p]\n   [skidder.core :as skidder]))\n\n(defn map-\u003eds\n  \"Given a map return a reified drag source.\"\n  [{:keys [type can-drag? begin-drag end-drag]}]\n  (reify\n    p/DragSource\n    (ds-type [this] type)\n    (can-drag? [this si ds-id]\n      (if can-drag?\n        (can-drag? si ds-id)\n        true))\n    (on-begin-drag [this si ds-id]\n      (when begin-drag\n        (begin-drag si ds-id)))\n    (on-end-drag [this si ds-id]\n      (when end-drag\n        (end-drag si ds-id)))))\n\n(defn register-ds\n  \"Register a drag source and return its \\\"unique\\\" id.\"\n  [ds]\n  (let [ds-id (str (gensym \"DS__\"))]\n    (p/add-ds! (p/ds-reg (skidder/state-info)) ds ds-id)\n    ds-id))\n\n\n(defn map-\u003edt\n  \"Given a map return a reified drop target.\"\n  [{:keys [accepts exclusive can-drop? hover enter leave drop]}]\n  (reify\n    p/DropTarget\n    (exclusive? [this] exclusive)\n    (accepts [this] accepts)\n    (can-drop? [this si ds-id dt-id]\n      (if can-drop?\n        (can-drop? si ds-id dt-id)\n        true))\n    (on-hover [this si dt-id]\n      (when hover\n        (hover si dt-id)))\n    (on-enter [this si dt-id]\n      (when enter\n        (enter si dt-id)))\n    (on-leave [this si dt-id]\n      (when leave\n        (leave si dt-id)))\n    (on-drop [this si ds-id dt-id]\n      (when drop\n        (drop si ds-id dt-id)))))\n\n(defn register-dt\n  \"Register a drop target and return its \\\"unique\\\" id.\"\n  [dt]\n  (let [dt-id (str (gensym \"DT__\"))]\n    (p/add-dt! (p/dt-reg (skidder/state-info)) dt dt-id)\n    dt-id))\n```\n\nNext we need a way to connect a drag source / drop target to a DOM element. This can be\naccomplished for example with the use of a React Hook that will use the connector and return\na React ref for the DOM element that we want to connect:\n\n```clojure\n(defn use-ds\n  \"React hook for a drag source.\"\n  [spec]\n  (let [ds-ref     (js/React.useRef nil)\n        dispose-fn (js/React.useRef nil)]\n    (js/React.useEffect\n     (fn []\n       (let [start  (fn []\n                      (when-some [node (.-current ds-ref)]\n                        (if (skidder/started?)\n                          (let [ds-id   (register-ds (map-\u003eds spec))\n                                disconn (p/connect-ds (skidder/connector) ds-id node)]\n                            (set! (.-current dispose-fn)\n                                  (fn []\n                                    (let [si (skidder/state-info)]\n                                      (when-not (p/dragging? si ds-id)\n                                        (p/remove-ds! (p/ds-reg si) ds-id)))\n                                    (disconn))))\n                          (set! (.-current dispose-fn) nil))))\n             stop   (fn []\n                      (when-let [dispose (.-current dispose-fn)]\n                        (dispose)))\n             remove (skidder/on-system-change {:start start\n                                               :stop  stop})]\n         (start)\n         (fn []\n           (stop)\n           (remove))))\n     #js [spec])\n    ds-ref))\n\n\n(defn use-dt\n  \"React hook for a drop target.\"\n  [spec]\n  (let [dt-ref     (js/React.useRef nil)\n        dispose-fn (js/React.useRef nil)]\n    (js/React.useEffect\n     (fn []\n       (let [start  (fn []\n                      (when-some [node (.-current dt-ref)]\n                        (if (skidder/started?)\n                          (let [dt-id   (register-dt (map-\u003edt spec))\n                                disconn (p/connect-dt (skidder/connector) dt-id node)]\n                            (set! (.-current dispose-fn)\n                                  (fn []\n                                    (disconn)\n                                    (p/remove-dt! (p/dt-reg (skidder/state-info)) dt-id))))\n                          (set! (.-current dispose-fn) nil))))\n             stop   (fn []\n                      (when-let [dispose (.-current dispose-fn)]\n                        (dispose)))\n             remove (skidder/on-system-change {:start start\n                                               :stop  stop})]\n         (start)\n         (fn []\n           (stop)\n           (remove))))\n     #js [spec])\n    dt-ref))\n```\n\nThen it is trivial to add drag and drop functionality to your React components\n\n```clojure\n(rum/defc cheese\n  [weight]\n  (let [cheese-ref (use-ds {:type       :cheese\n                            :begin-drag (constantly weight)})]\n    [:.cheese {:ref cheese-ref}\n     \"🧀\"]))\n\n\n(rum/defc mouse\n  []\n  (let [mouse-ref (use-dt {:accepts #{:cheese}\n                           :enter   (fn []\n                                      (js/console.log \"Gimme! Gimme!\"))\n                           :leave   (fn []\n                                      (js/console.log \"HEY! Right here!\"))\n                           :drop    (fn [state-info ds-id _]\n                                      (let [weight (p/drag-data state-info ds-id)]\n                                        (js/console.log (str \"Yum Yum! \" weight \" grams of cheese!\"))))})]\n    [:.mouse {:ref mouse-ref}\n     \"🐭\"]))\n```\n\n\n## License\n\nCopyright © 2020 Gerasimos\n\nThis program and the accompanying materials are made available under the\nterms of the Eclipse Public License 2.0 which is available at\nhttp://www.eclipse.org/legal/epl-2.0.\n\nThis Source Code may also be made available under the following Secondary\nLicenses when the conditions for such availability set forth in the Eclipse\nPublic License, v. 2.0 are satisfied: GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or (at your\noption) any later version, with the GNU Classpath Exception which is available\nat https://www.gnu.org/software/classpath/license.html.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fg7s%2Fskidder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fg7s%2Fskidder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fg7s%2Fskidder/lists"}