diff --git a/LICENSE b/LICENSE index bed20ac..3c8a718 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (C) 2023 DNV GL +Copyright (C) 2024 DNV GL Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Setup.org b/Setup.org index 114d362..5583654 100644 --- a/Setup.org +++ b/Setup.org @@ -302,6 +302,7 @@ p > code { background: var(--dots-hex); border-radius: .2em; padding: .1em .5em; + display: inline-block; } pre, @@ -310,6 +311,711 @@ code { } #+end_src + + +*** xyflow + +#+begin_src css :tangle docs/css/docs.css +.react-flow { + direction: ltr; + + --xy-edge-stroke-default: #b1b1b7; + --xy-edge-stroke-width-default: 1; + --xy-edge-stroke-selected-default: #555; + + --xy-connectionline-stroke-default: #b1b1b7; + --xy-connectionline-stroke-width-default: 1; + + --xy-attribution-background-color-default: rgba(255, 255, 255, 0.5); + + --xy-minimap-background-color-default: #fff; + --xy-minimap-mask-background-color-default: rgb(240, 240, 240, 0.6); + --xy-minimap-mask-stroke-color-default: transparent; + --xy-minimap-mask-stroke-width-default: 1; + --xy-minimap-node-background-color-default: #e2e2e2; + --xy-minimap-node-stroke-color-default: transparent; + --xy-minimap-node-stroke-width-default: 2; + + --xy-background-color-default: transparent; + --xy-background-pattern-dots-color-default: #91919a; + --xy-background-pattern-lines-color-default: #eee; + --xy-background-pattern-cross-color-default: #e2e2e2; +} + +.react-flow.dark { + --xy-edge-stroke-default: #3e3e3e; + --xy-edge-stroke-width-default: 1; + --xy-edge-stroke-selected-default: #727272; + + --xy-connectionline-stroke-default: #b1b1b7; + --xy-connectionline-stroke-width-default: 1; + + --xy-attribution-background-color-default: rgba(150, 150, 150, 0.25); + + --xy-minimap-background-color-default: #141414; + --xy-minimap-mask-background-color-default: rgb(60, 60, 60, 0.6); + --xy-minimap-mask-stroke-color-default: transparent; + --xy-minimap-mask-stroke-width-default: 1; + --xy-minimap-node-background-color-default: #2b2b2b; + --xy-minimap-node-stroke-color-default: transparent; + --xy-minimap-node-stroke-width-default: 2; + + --xy-background-color-default: #141414; + --xy-background-pattern-dots-color-default: #777; + --xy-background-pattern-lines-color-default: #777; + --xy-background-pattern-cross-color-default: #777; +} + +.react-flow { + background-color: var(--xy-background-color, var(--xy-background-color-default)); +} + +.react-flow__background { + background-color: var(--xy-background-color, var(--xy-background-color-props, var(--xy-background-color-default))); +} + +.react-flow__container { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; +} + +.react-flow__pane { + z-index: 1; + + &.draggable { + cursor: grab; + } + + &.dragging { + cursor: grabbing; + } + + &.selection { + cursor: pointer; + } +} + +.react-flow__viewport { + transform-origin: 0 0; + z-index: 2; + pointer-events: none; +} + +.react-flow__renderer { + z-index: 4; +} + +.react-flow__selection { + z-index: 6; +} + +.react-flow__nodesselection-rect:focus, +.react-flow__nodesselection-rect:focus-visible { + outline: none; +} + +.react-flow__edge-path { + stroke: var(--xy-edge-stroke, var(--xy-edge-stroke-default)); + stroke-width: var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default)); + fill: none; +} + +.react-flow__connection-path { + stroke: var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default)); + stroke-width: var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default)); + fill: none; +} + +.react-flow .react-flow__edges { + position: absolute; + + svg { + overflow: visible; + position: absolute; + pointer-events: none; + } +} + +.react-flow__edge { + pointer-events: visibleStroke; + + &.selectable { + cursor: pointer; + } + + &.animated path { + stroke-dasharray: 5; + animation: dashdraw 0.5s linear infinite; + } + + &.animated path.react-flow__edge-interaction { + stroke-dasharray: none; + animation: none; + } + + &.inactive { + pointer-events: none; + } + + &.selected, + &:focus, + &:focus-visible { + outline: none; + } + + &.selected .react-flow__edge-path, + &.selectable:focus .react-flow__edge-path, + &.selectable:focus-visible .react-flow__edge-path { + stroke: var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default)); + } + + &-textwrapper { + pointer-events: all; + } + + .react-flow__edge-text { + pointer-events: none; + user-select: none; + } +} +.react-flow__connection { + pointer-events: none; + + .animated { + stroke-dasharray: 5; + animation: dashdraw 0.5s linear infinite; + } +} + +svg.react-flow__connectionline { + z-index: 1001; + overflow: visible; + position: absolute; +} + +.react-flow__nodes { + pointer-events: none; + transform-origin: 0 0; +} + +.react-flow__node { + position: absolute; + user-select: none; + pointer-events: all; + transform-origin: 0 0; + box-sizing: border-box; + cursor: default; + + &.selectable { + cursor: pointer; + } + + &.draggable { + cursor: grab; + pointer-events: all; + + &.dragging { + cursor: grabbing; + } + } +} + +.react-flow__nodesselection { + z-index: 3; + transform-origin: left top; + pointer-events: none; + + &-rect { + position: absolute; + pointer-events: all; + cursor: grab; + } +} + +.react-flow__handle { + position: absolute; + pointer-events: none; + min-width: 5px; + min-height: 5px; + + &.connectingfrom { + pointer-events: all; + } + + &.connectionindicator { + pointer-events: all; + cursor: crosshair; + } + + &-bottom { + top: auto; + left: 50%; + bottom: 0; + transform: translate(-50%, 50%); + } + + &-top { + top: 0; + left: 50%; + transform: translate(-50%, -50%); + } + + &-left { + top: 50%; + left: 0; + transform: translate(-50%, -50%); + } + + &-right { + top: 50%; + right: 0; + transform: translate(50%, -50%); + } +} + +.react-flow__edgeupdater { + cursor: move; + pointer-events: all; +} + +.react-flow__panel { + position: absolute; + z-index: 5; + margin: 15px; + + &.top { + top: 0; + } + + &.bottom { + bottom: 0; + } + + &.left { + left: 0; + } + + &.right { + right: 0; + } + + &.center { + left: 50%; + transform: translateX(-50%); + } +} + +.react-flow__attribution { + font-size: 10px; + background: var(--xy-attribution-background-color, var(--xy-attribution-background-color-default)); + padding: 2px 3px; + margin: 0; + + a { + text-decoration: none; + color: #999; + } +} + +@keyframes dashdraw { + from { + stroke-dashoffset: 10; + } +} + +.react-flow__edgelabel-renderer { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; + user-select: none; + left: 0; + top: 0; +} + +.react-flow__viewport-portal { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + user-select: none; +} + +.react-flow__minimap { + background: var( + --xy-minimap-background-color-props, + var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) + ); + + &-svg { + display: block; + } + + &-mask { + fill: var( + --xy-minimap-mask-background-color-props, + var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) + ); + stroke: var( + --xy-minimap-mask-stroke-color-props, + var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) + ); + stroke-width: var( + --xy-minimap-mask-stroke-width-props, + var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) + ); + } + + &-node { + fill: var( + --xy-minimap-node-background-color-props, + var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) + ); + stroke: var( + --xy-minimap-node-stroke-color-props, + var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) + ); + stroke-width: var( + --xy-minimap-node-stroke-width-props, + var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) + ); + } +} + +.react-flow__background { + pointer-events: none; + z-index: -1; +} + +.react-flow__background-pattern { + &.dots { + fill: var( + --xy-background-pattern-color-props, + var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) + ); + } + + &.lines { + stroke: var( + --xy-background-pattern-color-props, + var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) + ); + } + + &.cross { + stroke: var( + --xy-background-pattern-color-props, + var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) + ); + } +} + +.react-flow__controls { + display: flex; + flex-direction: column; + + &.horizontal { + flex-direction: row; + } + + &-button { + display: flex; + justify-content: center; + align-items: center; + height: 26px; + width: 26px; + padding: 4px; + + svg { + width: 100%; + max-width: 12px; + max-height: 12px; + fill: currentColor; + } + } +} + + + +.react-flow { + --xy-node-color-default: inherit; + --xy-node-border-default: 1px solid #1a192b; + --xy-node-background-color-default: #fff; + --xy-node-group-background-color-default: rgba(240, 240, 240, 0.25); + --xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, 0.08); + --xy-node-boxshadow-selected-default: 0 0 0 0.5px #1a192b; + --xy-node-border-radius-default: 3px; + + --xy-handle-background-color-default: #1a192b; + --xy-handle-border-color-default: #fff; + + --xy-selection-background-color-default: rgba(0, 89, 220, 0.08); + --xy-selection-border-default: 1px dotted rgba(0, 89, 220, 0.8); + + --xy-controls-button-background-color-default: #fefefe; + --xy-controls-button-background-color-hover-default: #f4f4f4; + --xy-controls-button-color-default: inherit; + --xy-controls-button-color-hover-default: inherit; + --xy-controls-button-border-color-default: #eee; + --xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08); + + --xy-edge-label-background-color-default: #ffffff; + --xy-edge-label-color-default: inherit; +} + +.react-flow.dark { + --xy-node-color-default: #f8f8f8; + --xy-node-border-default: 1px solid #3c3c3c; + --xy-node-background-color-default: #1e1e1e; + --xy-node-group-background-color-default: rgba(240, 240, 240, 0.25); + --xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, 0.08); + --xy-node-boxshadow-selected-default: 0 0 0 0.5px #999; + + --xy-handle-background-color-default: #bebebe; + --xy-handle-border-color-default: #1e1e1e; + + --xy-selection-background-color-default: rgba(200, 200, 220, 0.08); + --xy-selection-border-default: 1px dotted rgba(200, 200, 220, 0.8); + + --xy-controls-button-background-color-default: #2b2b2b; + --xy-controls-button-background-color-hover-default: #3e3e3e; + --xy-controls-button-color-default: #f8f8f8; + --xy-controls-button-color-hover-default: #fff; + --xy-controls-button-border-color-default: #5b5b5b; + --xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08); + + --xy-edge-label-background-color-default: #141414; + --xy-edge-label-color-default: #f8f8f8; +} + +.react-flow__edge { + &.updating { + .react-flow__edge-path { + stroke: #777; + } + } + + &-text { + font-size: 10px; + } +} + +.react-flow__node.selectable { + &:focus, + &:focus-visible { + outline: none; + } +} + +.react-flow__node-input, +.react-flow__node-default, +.react-flow__node-output, +.react-flow__node-group { + padding: 10px; + border-radius: var(--xy-node-border-radius, var(--xy-node-border-radius-default)); + width: 150px; + font-size: 12px; + color: var(--xy-node-color, var(--xy-node-color-default)); + text-align: center; + border: var(--xy-node-border, var(--xy-node-border-default)); + background-color: var(--xy-node-background-color, var(--xy-node-background-color-default)); + + &.selectable { + &:hover { + box-shadow: var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default)); + } + + &.selected, + &:focus, + &:focus-visible { + box-shadow: var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default)); + } + } +} + +.react-flow__node-group { + background-color: var(--xy-node-group-background-color, var(--xy-node-group-background-color-default)); +} + +.react-flow__nodesselection-rect, +.react-flow__selection { + background: var(--xy-selection-background-color, var(--xy-selection-background-color-default)); + border: var(--xy-selection-border, var(--xy-selection-border-default)); + + &:focus, + &:focus-visible { + outline: none; + } +} + +.react-flow__handle { + width: 6px; + height: 6px; + background-color: var(--xy-handle-background-color, var(--xy-handle-background-color-default)); + border: 1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default)); + border-radius: 100%; +} + +.react-flow__controls { + box-shadow: var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default)); + + &-button { + border: none; + background: var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default)); + border-bottom: 1px solid + var( + --xy-controls-button-border-color-props, + var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) + ); + color: var( + --xy-controls-button-color-props, + var(--xy-controls-button-color, var(--xy-controls-button-color-default)) + ); + cursor: pointer; + user-select: none; + + &:hover { + background: var( + --xy-controls-button-background-color-hover-props, + var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) + ); + color: var( + --xy-controls-button-color-hover-props, + var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) + ); + } + + &:disabled { + pointer-events: none; + + svg { + fill-opacity: 0.4; + } + } + } + + &-button:last-child { + border-bottom: none; + } +} + + + +.react-flow { + --xy-resize-background-color-default: #3367d9; +} + +.react-flow__resize-control { + position: absolute; +} + +.react-flow__resize-control.left, +.react-flow__resize-control.right { + cursor: ew-resize; +} + +.react-flow__resize-control.top, +.react-flow__resize-control.bottom { + cursor: ns-resize; +} + +.react-flow__resize-control.top.left, +.react-flow__resize-control.bottom.right { + cursor: nwse-resize; +} + +.react-flow__resize-control.bottom.left, +.react-flow__resize-control.top.right { + cursor: nesw-resize; +} + +/* handle styles */ +.react-flow__resize-control.handle { + width: 4px; + height: 4px; + border: 1px solid #fff; + border-radius: 1px; + background-color: var(--xy-resize-background-color, var(--xy-resize-background-color-default)); + transform: translate(-50%, -50%); +} + +.react-flow__resize-control.handle.left { + left: 0; + top: 50%; +} +.react-flow__resize-control.handle.right { + left: 100%; + top: 50%; +} +.react-flow__resize-control.handle.top { + left: 50%; + top: 0; +} +.react-flow__resize-control.handle.bottom { + left: 50%; + top: 100%; +} +.react-flow__resize-control.handle.top.left { + left: 0; +} +.react-flow__resize-control.handle.bottom.left { + left: 0; +} +.react-flow__resize-control.handle.top.right { + left: 100%; +} +.react-flow__resize-control.handle.bottom.right { + left: 100%; +} + +/* line styles */ +.react-flow__resize-control.line { + border-color: var(--xy-resize-background-color, var(--xy-resize-background-color-default)); + border-width: 0; + border-style: solid; +} + +.react-flow__resize-control.line.left, +.react-flow__resize-control.line.right { + width: 1px; + transform: translate(-50%, 0); + top: 0; + height: 100%; +} + +.react-flow__resize-control.line.left { + left: 0; + border-left-width: 1px; +} + +.react-flow__resize-control.line.right { + left: 100%; + border-right-width: 1px; +} + +.react-flow__resize-control.line.top, +.react-flow__resize-control.line.bottom { + height: 1px; + transform: translate(0, -50%); + left: 0; + width: 100%; +} + +.react-flow__resize-control.line.top { + top: 0; + border-top-width: 1px; +} + +.react-flow__resize-control.line.bottom { + border-bottom-width: 1px; + top: 100%; +} +#+end_src + *** ReactFlow #+begin_src css :tangle docs/css/docs.css @@ -320,6 +1026,11 @@ code { top: 0; left: 0; } +.react-flow__container svg { + overflow: visible; + position: absolute; + pointer-events: none; +} .react-flow__pane { z-index: 1; cursor: -webkit-grab; @@ -799,7 +1510,7 @@ const main = (w, d) => { } }); observer.observe(example); - }); + }); } main(window, document); diff --git a/index.org b/index.org index 704dd42..4a3c39d 100644 --- a/index.org +++ b/index.org @@ -9,7 +9,7 @@ * Usage You can mostly follow the [[https://reactflow.dev/docs/][ReactFlow documentation]] and be sure to replace -"react" with "reagent" and use ~snake-casing~ instead of ~kebabCasing~. +"react" with "reagent" and use ~kebab-casing~ instead of ~camelCasing~. There are some exceptions. - Types are prefixed with ~Flow~ and uses the original ~camelCasing~. @@ -47,7 +47,6 @@ You can read more about the API at [[https://cljdoc.org/d/net.clojars.simtech/re :header-args:clojurescript: :tangle babel/examples/src/custom_nodes/core.cljs :exports none :noweb yes :end: - Connect the nodes, pick a color and see the nodes change interactively. #+html: