Skip to content

Commit 23de94c

Browse files
committed
Upgrade reactflow
- Minor style improvements - More accurate drag & drop example - Added information about including styles to the usage section
1 parent aab9d98 commit 23de94c

4 files changed

Lines changed: 112 additions & 51 deletions

File tree

.github/workflows/CI.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ jobs:
1212
- id: CI
1313
uses: ./.github/actions/tangle
1414

15+
- name: Setup Chrome
16+
uses: browser-actions/setup-chrome@v1
17+
1518
- name: Setup Node
1619
uses: actions/setup-node@v3
1720
with:
@@ -34,4 +37,5 @@ jobs:
3437
shell: bash
3538
run: |
3639
cd babel
37-
clojure -M:test
40+
npx shadow-cljs compile ci
41+
npx karma start --single-run

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (C) 2024 DNV GL
3+
Copyright (C) 2025 DNV
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Setup.org

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ should be published, what license to use, etc.
8585
#+begin_src text :tangle LICENSE :noweb yes
8686
MIT License
8787

88-
Copyright (C) <<year()>> DNV GL
88+
Copyright (C) <<year()>> DNV
8989

9090
Permission is hereby granted, free of charge, to any person obtaining a copy
9191
of this software and associated documentation files (the "Software"), to deal
@@ -935,11 +935,12 @@ svg.react-flow__connectionline {
935935

936936
/* handle styles */
937937
.react-flow__resize-control.handle {
938-
width: 4px;
939-
height: 4px;
938+
width: 6px;
939+
height: 6px;
940940
border: 1px solid #fff;
941-
border-radius: 1px;
941+
border-radius: 3px;
942942
background-color: var(--xy-resize-background-color, var(--xy-resize-background-color-default));
943+
box-shadow: 0 1px 4px 1px var(--xy-controls-box-shadow-default);
943944
transform: translate(-50%, -50%);
944945
}
945946

@@ -1448,6 +1449,9 @@ a {
14481449
background: transparent;
14491450
border: none;
14501451
}
1452+
.react-flow__node-preview-node {
1453+
mix-blend-mode: difference;
1454+
}
14511455
.color-picker {
14521456
--radius: 0.5em;
14531457
--size: 2.5em;

index.org

Lines changed: 98 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88

99
* Usage
1010

11-
You can mostly follow the [[https://reactflow.dev/docs/][ReactFlow documentation]] and be sure to replace
11+
You can mostly follow the [[https://reactflow.dev/docs/][ReactFlow documentation]], but make sure to replace
1212
"react" with "reagent" and use ~kebab-casing~ instead of ~camelCasing~.
13-
There are some exceptions.
13+
14+
There are a few important exceptions:
1415

1516
- Types are prefixed with ~Flow~ and uses the original ~camelCasing~.
1617
We recommend using the keyword equivalent instead though, so you
1718
could ignore types altogether.
1819

1920
- The parameters received in ~node-types~ & ~edge-types~ are unchanged, so
2021
if you want to use them you should apply
21-
~(js->clj props :keywordize-keys true)~.
22+
~(flowjs->clj props)~.
2223
A nice pattern, is to only rely on the ~id~ from the parameters and do
2324
look-ups in your state manually.
2425
#+begin_example clojurescript
@@ -32,6 +33,9 @@ There are some exceptions.
3233
and there are events to listen for viewport-changes on the main
3334
component; ~reagent-flow~.
3435

36+
- CSS is not automatically included, so you'll have to download the
37+
CSS files you'd like from xyflow on github.
38+
3539
You can read more about the API at [[https://cljdoc.org/d/net.clojars.simtech/reagent-flow/][cljdocs]].
3640

3741
/Please examine the examples below to get a better grasp of the aforementioned differences./
@@ -229,41 +233,63 @@ map with ~x~, ~y~ & ~zoom~ values, just as the hook equivalent.
229233
viewport (r/atom {:x 0 :y 0 :zoom 1})
230234
data-type "application/reagentflow"]
231235
(letfn [(handle-drag [event]
232-
(let [data-transfer (-> event .-dataTransfer)]
233-
(.setData data-transfer data-type "default")
236+
(let [data-transfer (-> event .-dataTransfer)
237+
target (.-target event)
238+
rect (.getBoundingClientRect target)
239+
offset-x (- (.-clientX event) (.-left rect))
240+
offset-y (- (.-clientY event) (.-top rect))
241+
payload (js/JSON.stringify
242+
#js {:type "default"
243+
:offsetX offset-x
244+
:offsetY offset-y})]
245+
(.setData data-transfer data-type payload)
234246
(set! (-> data-transfer .-effectAllowed) "move")))
247+
235248
(handle-node-changes [changes]
236249
(reset! nodes (apply-node-changes changes @nodes)))
250+
237251
(handle-edge-changes [changes]
238252
(reset! edges (apply-edge-changes changes @edges)))
253+
239254
(handle-connect [connection]
240255
(reset! edges (add-edge connection @edges)))
256+
241257
(handle-drop [event]
242258
(.preventDefault event)
243-
(when-let [node-type (.getData (-> event .-dataTransfer) data-type)]
244-
(let [{:keys [screen-to-flow-position]} @provider
245-
flow-el (-> flow .-state .-firstChild)
246-
rect (.getBoundingClientRect flow-el)
247-
position (screen-to-flow-position {:x (.-clientX event)
248-
:y (.-clientY event)})]
249-
(swap! node-id inc)
250-
(swap! nodes conj {:id (str "node-" @node-id)
251-
:type node-type
252-
:position position
253-
:data {:label (str "Node #" @node-id)}}))))
259+
(let [data-json (.getData (-> event .-dataTransfer) data-type)
260+
data (js/JSON.parse data-json)]
261+
(when data
262+
(let [{:keys [screen-to-flow-position]} @provider
263+
flow-el (-> flow .-state .-firstChild)
264+
rect (.getBoundingClientRect flow-el)
265+
mouse-x (.-clientX event)
266+
mouse-y (.-clientY event)
267+
drop-pos (screen-to-flow-position {:x mouse-x
268+
:y mouse-y})
269+
node-pos (screen-to-flow-position {:x (- mouse-x (.-offsetX data))
270+
:y (- mouse-y (.-offsetY data))})]
271+
(swap! node-id inc)
272+
(swap! nodes conj {:id (str "node-" @node-id)
273+
:type (.-type data)
274+
:position node-pos
275+
:data {:label (str "Node #" @node-id)}})))))
276+
254277
(handle-drag-over [event]
255278
(.preventDefault event)
256279
(set! (-> event .-dataTransfer .-dropEffect) "move"))]
280+
257281
(fn []
258282
[:<>
259283
[:menu.node-palette
260284
[:div.node {:draggable true
261285
:on-drag-start handle-drag} "Node"]]
286+
262287
[reagent-flow {:ref #(reset! flow %)
263288
:id :drop-it-like-its-hot
264289
:nodes @nodes
265290
:edges @edges
266291
:fit-view true
292+
:viewport {:x 200 :y 200 :zoom 1.5}
267293
:on-init #(reset! provider %)
268294
:on-nodes-change handle-node-changes
269295
:on-edges-change handle-edge-changes
@@ -540,20 +566,21 @@ other third party libraries. The library we're using here is [[https://leva.ment
540566

541567
*** Deps.edn
542568
#+begin_src clojurescript :tangle babel/examples/deps.edn
543-
{:deps {org.clojure/clojure {:mvn/version "1.11.3"}
544-
org.clojure/clojurescript {:mvn/version "1.11.132"}
545-
com.google.javascript/closure-compiler-unshaded {:mvn/version "v20230411"}
569+
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
570+
org.clojure/clojurescript {:mvn/version "1.12.42"
571+
:exclusions [com.google.javascript/closure-compiler-unshaded]}
572+
com.google.javascript/closure-compiler-unshaded {:mvn/version "v20250528"}
546573
reagent/reagent {:mvn/version "1.2.0"}
547574
re-frame/re-frame {:mvn/version "1.4.3"}
548575
;; io.github.mentat-collective/leva.cljs {:git/sha "bb24493c8b4a0fcd862d69b4960fa297561fa5bb"}
549576
net.clojars.simtech/reagent-flow {:local/root "../"}}
550577
:paths ["src"]
551578
:aliases
552-
{:watch {:extra-deps {thheller/shadow-cljs {:mvn/version "2.28.14"}
553-
binaryage/devtools {:mvn/version "1.0.7"}}
579+
{:watch {:extra-deps {thheller/shadow-cljs {:mvn/version "3.1.7"}
580+
binaryage/devtools {:mvn/version "1.0.7"}}
554581
:main-opts ["-m" "shadow.cljs.devtools.cli" "watch" "examples"]}
555-
:build {:extra-deps {thheller/shadow-cljs {:mvn/version "2.28.14"}
556-
binaryage/devtools {:mvn/version "1.0.7"}}
582+
:build {:extra-deps {thheller/shadow-cljs {:mvn/version "3.1.7"}
583+
binaryage/devtools {:mvn/version "1.0.7"}}
557584
:main-opts ["-m" "shadow.cljs.devtools.cli" "compile" "examples"]}}}
558585
#+end_src
559586

@@ -586,7 +613,7 @@ We use the same version-scheme as ReactFlow. You're currently viewing
586613
version:
587614
#+name: version
588615
#+begin_src text
589-
12.2.0
616+
12.7.1
590617
#+end_src
591618

592619
Here you'll find all the names of the classes, functions, hooks, and types of
@@ -665,6 +692,7 @@ this version of ReactFlow listed.
665692
- XYZPosition
666693
- Dimensions
667694
- Rect
695+
- AriaLabelConfig
668696
- Box
669697
- Transform
670698
- CoordinateExtent
@@ -714,6 +742,9 @@ this version of ReactFlow listed.
714742
- OnConnectStartParams
715743
- OnConnectStart
716744
- OnConnectEnd
745+
- OnReconnect
746+
- OnReconnectConnectStart
747+
- OnReconnectConnectEnd
717748
- Viewport
718749
- KeyCode
719750
- SnapGrid
@@ -816,7 +847,8 @@ these processed lists.
816847
[clojure.string :as str]
817848
[clojure.walk :refer [postwalk]]
818849
[cljs.core :refer [IDeref IEditableCollection]]
819-
[medley.core :refer [map-keys map-vals]]
850+
[medley.core :refer [map-vals]]
851+
["react" :as react]
820852
[reagent.core :as r]
821853
["@xyflow/react$default" :as ReactFlow]
822854
["@xyflow/react" :as rf
@@ -851,8 +883,9 @@ helper-functions:
851883
(let [f (fn [[k v]] (if (or (string? k) (keyword? k)) [(f k) v] [k v]))]
852884
(postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))
853885

854-
(defn- flowjs->clj [o]
886+
(defn flowjs->clj
855887
"Convert a JavaScript object to a Clojure map with kebab-cased keys."
888+
[o]
856889
(let [obj (js->clj o :keywordize-keys true)]
857890
(if (map? obj)
858891
(change-keys (dissoc obj "") -->kebab-case)
@@ -868,7 +901,7 @@ helper-functions:
868901

869902
(defn- apply-changes [f delta src]
870903
(-> (f (clj->flowjs delta) (clj->flowjs src))
871-
(flowjs->clj)))
904+
(flowjs->clj)))
872905

873906
(defn- react-flowify [types]
874907
(clj->js ((partial map-vals r/reactify-component) types)))
@@ -1014,7 +1047,6 @@ client-code.
10141047
params] children)]]))
10151048
#+end_src
10161049

1017-
10181050
** Tests :noexport:
10191051

10201052
#+begin_src clojurescript :tangle babel/test/reagent_flow/core_test.cljs :noweb yes
@@ -1039,7 +1071,6 @@ client-code.
10391071
(let [transformed (#'reagent-flow.core/change-keys sample-map keyword)]
10401072
(is (every? (comp keyword? first) transformed)))))
10411073

1042-
;; TODO Could use a generative test for `test-flowjs->clj` as well
10431074
(deftest test-flowjs->clj
10441075
(testing "Ensure flowjs->clj handles JS objects correctly"
10451076
(let [js-obj {:aKey "value" :nestedObj {:anotherKey 42} :aList [1 2 3]}]
@@ -1077,35 +1108,36 @@ client-code.
10771108

10781109
**** Deps.edn
10791110
#+begin_src clojurescript :tangle babel/deps.edn
1080-
{:deps {org.clojure/clojure {:mvn/version "1.11.3"}
1081-
org.clojure/clojurescript {:mvn/version "1.11.132"}
1082-
camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.3"}
1083-
dev.weavejester/medley {:mvn/version "1.8.1"}
1084-
reagent/reagent {:mvn/version "1.2.0"}}
1111+
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
1112+
org.clojure/clojurescript {:mvn/version "1.12.42"
1113+
:exclusions [com.google.javascript/closure-compiler-unshaded]}
1114+
com.google.javascript/closure-compiler-unshaded {:mvn/version "v20250528"}}
10851115
:paths ["src"]
10861116
:aliases
1087-
{:build {:extra-deps {thheller/shadow-cljs {:mvn/version "2.28.14"}}
1117+
{:build {:extra-deps {thheller/shadow-cljs {:mvn/version "3.1.7"}}
10881118
:main-opts ["-m" "shadow.cljs.devtools.cli" "release" "reagent-flow"]}
1089-
:test {:extra-deps {olical/cljs-test-runner {:mvn/version "3.8.1"}
1090-
org.clojure/test.check {:mvn/version "1.1.1"}}
1091-
:extra-paths ["test" "cljs-test-runner-out/gen"]
1092-
:main-opts ["-m" "cljs-test-runner.main" "-d" "test"]}
1093-
:package {:deps {io.github.clojure/tools.build {:git/url "https://github.com/clojure/tools.build"
1094-
:git/sha "143611fcf965919f1d9c18a10eeeed319305e034"}
1119+
:package {:deps {io.github.clojure/tools.build {:mvn/version "0.10.9"}
10951120
slipset/deps-deploy {:mvn/version "0.2.2"}}
10961121
:ns-default package}}}
10971122
#+end_src
10981123

10991124
**** Shadow-cljs.edn
11001125
#+begin_src clojurescript :tangle babel/shadow-cljs.edn
1101-
{:deps true
1126+
{:dependencies [[camel-snake-kebab/camel-snake-kebab "0.4.3"]
1127+
[dev.weavejester/medley "1.8.1"]
1128+
[reagent/reagent "1.3.0"]]
11021129
:nrepl {:port 9000}
11031130
:builds
11041131
{:reagent-flow
11051132
{:modules {:reagent-flow {:entries [reagent-flow.core]}}
11061133
:target :browser
11071134
:asset-path "js"
1108-
:output-dir "target/classes/public/js"}}}
1135+
:output-dir "target/classes/public/js"}
1136+
:ci
1137+
{:target :karma
1138+
:output-to "out/ci.js"
1139+
:ns-regexp "-test$"
1140+
:autorun true}}}
11091141
#+end_src
11101142

11111143
#+begin_src clojurescript :tangle babel/src/deps.cljs :noweb yes
@@ -1114,6 +1146,24 @@ client-code.
11141146
"react-dom" "^18.3.1"}}
11151147
#+end_src
11161148

1149+
#+begin_src javascript :tangle babel/karma.confg.js
1150+
module.exports = function (config) {
1151+
config.set({
1152+
browsers: ['ChromeHeadless'],
1153+
basePath: 'target',
1154+
files: ['ci.js'],
1155+
frameworks: ['cljs-test'],
1156+
plugins: ['karma-cljs-test', 'karma-chrome-launcher'],
1157+
colors: true,
1158+
logLevel: config.LOG_INFO,
1159+
client: {
1160+
args: ["shadow.test.karma.init"],
1161+
singleRun: true
1162+
}
1163+
})
1164+
};
1165+
#+end_src
1166+
11171167
**** Package.json
11181168
#+begin_src javascript :tangle babel/package.json :noweb yes
11191169
{
@@ -1125,8 +1175,11 @@ client-code.
11251175
"@xyflow/react": "<<version>>"
11261176
},
11271177
"devDependencies": {
1128-
"react": "^18.3.1",
1129-
"react-dom": "^18.3.1"
1178+
"karma": "^6.4.4",
1179+
"karma-chrome-launcher": "^3.2.0",
1180+
"karma-cljs-test": "^0.1.0",
1181+
"react": "^18.3.1",
1182+
"react-dom": "^18.3.1"
11301183
}
11311184
}
11321185
#+end_src

0 commit comments

Comments
 (0)