caixanegra 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -2
- data/app/assets/javascripts/caixanegra/designer.js +163 -17
- data/app/assets/stylesheets/caixanegra/designer.scss +49 -3
- data/app/views/caixanegra/designer/index.html.erb +26 -2
- data/app/views/layouts/caixanegra/application.html.erb +1 -1
- data/lib/caixanegra/manager.rb +1 -1
- data/lib/caixanegra/unit_helper.rb +13 -4
- data/lib/caixanegra/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 002efa6f5c1a220426d102e5f055b50eff9fb55e5742d6c2c33d6474f890fd8a
|
4
|
+
data.tar.gz: 03c265dbfa950af4d043bfc56711593b2cb1661f76fc2ca40327fbc7a7f5e309
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da48c854b8646515af968b7c685cece267a5bed236f1f58d5331de39a2d75bded3f0896b84d09de7cbe70c0fad7f388ef5891684bc04f45995e0b1afdc9d1784
|
7
|
+
data.tar.gz: 28b98d23640368db0f32475c4cd6209fb39648e723e63d70ae2fd814e88c05ca6c9033dccc1accfa3c2c331413569fb54e1b52f115cc26841703019dec4fa0a2
|
data/README.md
CHANGED
@@ -32,18 +32,21 @@ Caixanegra.setup do |config|
|
|
32
32
|
config.units = [
|
33
33
|
Caixanegra::Units::AwesomeUnit,
|
34
34
|
Some::Other::Namespace::AnotherAwesomeUnit,
|
35
|
-
Caixanegra::Units::SuperUnit,
|
35
|
+
[Caixanegra::Units::NamespaceOne::SuperUnit, :ns1_super_unit],
|
36
|
+
[Caixanegra::Units::NamespaceTwo::SuperUnit, :ns2_super_unit],
|
36
37
|
]
|
37
38
|
config.transient_store = GreatTransientStore.new
|
38
39
|
end
|
39
40
|
```
|
41
|
+
While configuring units from your codebase, if provided in the array form, **caixanegra** will use the second array item as the class name. Otherwise it will infer from class.
|
42
|
+
**NOTE:** You can't have two classes with the same name as **caixanegra** will use the last one
|
40
43
|
|
41
44
|
With the designer configured, you can use `Caixanegra::Manager` to handle the previously stored definition (or an empty one).
|
42
45
|
This will give you the UID that **caixanegra** designer will understand, using transient store.
|
43
46
|
|
44
47
|
```ruby
|
45
48
|
my_flow = somewhere.get_flow # get from your own persistence or transport solution
|
46
|
-
uid = Caixanegra::Manager.handler(my_flow || {})
|
49
|
+
uid = Caixanegra::Manager.handler(flow_definition: my_flow || {})
|
47
50
|
```
|
48
51
|
|
49
52
|
You can then safely navigate to the designer.
|
@@ -303,6 +303,10 @@ window.Caixanegra.Designer = {
|
|
303
303
|
document.querySelector("#console").addEventListener("click", this.toggleExecutionConsole.bind(this));
|
304
304
|
document.querySelector("#clearCarryOverObject").addEventListener("click", this.clearCarryOverObject.bind(this));
|
305
305
|
document.querySelector("#reset").addEventListener("click", this.resetExecution.bind(this));
|
306
|
+
document.querySelector("#duplicate").addEventListener("click", this.duplicateUnit.bind(this));
|
307
|
+
document.querySelector("#importFlow").addEventListener("click", this.#importFlow.bind(this));
|
308
|
+
document.querySelector("#exportFlow").addEventListener("click", this.#exportFlow.bind(this));
|
309
|
+
document.querySelector("#importFlowFile").addEventListener("change", this.#importFlowFileChanged.bind(this));
|
306
310
|
this.toggleBlocker(true, "loading your flow");
|
307
311
|
window.addEventListener("resize", this.#windowResized.bind(drawingSurface));
|
308
312
|
drawingSurface.addEventListener("update_start", this.#engineUpdate.bind(this));
|
@@ -385,6 +389,8 @@ window.Caixanegra.Designer = {
|
|
385
389
|
}
|
386
390
|
|
387
391
|
container.append(wrapper);
|
392
|
+
|
393
|
+
return wrapper;
|
388
394
|
}
|
389
395
|
|
390
396
|
deleteUnit() {
|
@@ -394,6 +400,12 @@ window.Caixanegra.Designer = {
|
|
394
400
|
this.toggleDeleteConfirmation();
|
395
401
|
}
|
396
402
|
|
403
|
+
duplicateUnit() {
|
404
|
+
this.unitDetailPane.classList.remove("-open");
|
405
|
+
this.unitDebugHitsPane.classList.remove("-open");
|
406
|
+
this.cloneUnit(this.selectedUnit.oid);
|
407
|
+
}
|
408
|
+
|
397
409
|
flushToConsole(entries) {
|
398
410
|
const container = document.querySelector("#executionConsole");
|
399
411
|
container.innerHTML = "";
|
@@ -468,7 +480,8 @@ window.Caixanegra.Designer = {
|
|
468
480
|
flowSnapshot() {
|
469
481
|
const snapshot = {
|
470
482
|
entrypoint: this.#units.find((unit) => {return unit.type === "starter"})?.oid,
|
471
|
-
units: []
|
483
|
+
units: [],
|
484
|
+
initialCarryover: this.digInitialCarryOver(document.querySelector("#carryOverObject"))
|
472
485
|
};
|
473
486
|
|
474
487
|
this.#units.forEach((unit) => {
|
@@ -587,33 +600,81 @@ window.Caixanegra.Designer = {
|
|
587
600
|
});
|
588
601
|
|
589
602
|
this.#loadedComponents.catalog = true;
|
590
|
-
this
|
603
|
+
this.reveal();
|
591
604
|
});
|
592
605
|
}
|
593
606
|
|
594
607
|
loadFlow() {
|
595
608
|
this.api.getFlow(this.flowId).then((response) => {
|
596
609
|
const flowData = response;
|
597
|
-
this.gre.clear();
|
598
|
-
this.#units = [];
|
599
610
|
|
600
|
-
(flowData
|
601
|
-
this.createUnit(unit);
|
602
|
-
});
|
603
|
-
|
604
|
-
this.#units.forEach((unit) => {
|
605
|
-
(unit?.exits || []).forEach((exit) => {
|
606
|
-
const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
|
607
|
-
if (object) { exit.target = object; }
|
608
|
-
});
|
609
|
-
});
|
611
|
+
this.#loadFlowFromJSON(flowData);
|
610
612
|
|
611
613
|
this.#loadedComponents.flow = true;
|
612
|
-
this
|
614
|
+
this.reveal();
|
615
|
+
});
|
616
|
+
}
|
617
|
+
|
618
|
+
#loadFlowFromJSON(flowData) {
|
619
|
+
this.gre.clear();
|
620
|
+
this.#units = [];
|
621
|
+
|
622
|
+
(flowData?.units || []).forEach((unit) => {
|
623
|
+
this.createUnit(unit);
|
624
|
+
});
|
625
|
+
|
626
|
+
this.#units.forEach((unit) => {
|
627
|
+
(unit?.exits || []).forEach((exit) => {
|
628
|
+
const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
|
629
|
+
if (object) { exit.target = object; }
|
630
|
+
});
|
613
631
|
});
|
632
|
+
|
633
|
+
this.buildInitialCarryover(flowData.initialCarryover);
|
634
|
+
}
|
635
|
+
|
636
|
+
digBuildInitialCarryover(container, key, objectPointer) {
|
637
|
+
const lastChild = container.childNodes[container.childNodes.length -1];
|
638
|
+
|
639
|
+
if (key !== null) {
|
640
|
+
container.childNodes[0].querySelector("input").value = key;
|
641
|
+
}
|
642
|
+
|
643
|
+
if (Array.isArray(objectPointer)) {
|
644
|
+
this.coeAssignType(lastChild, "array");
|
645
|
+
|
646
|
+
for (let idx = 0; idx < objectPointer.length; idx++) {
|
647
|
+
this.digBuildInitialCarryover(
|
648
|
+
this.coeAddRow(container.querySelector(".rows")),
|
649
|
+
null,
|
650
|
+
objectPointer[idx]
|
651
|
+
);
|
652
|
+
}
|
653
|
+
} else if (typeof objectPointer === "object") {
|
654
|
+
this.coeAssignType(lastChild, "object");
|
655
|
+
|
656
|
+
for (const key in objectPointer) {
|
657
|
+
this.digBuildInitialCarryover(this.coeAddRow(container.querySelector(".rows")), key, objectPointer[key]);
|
658
|
+
}
|
659
|
+
} else if (typeof objectPointer === "number") {
|
660
|
+
this.coeAssignType(lastChild, "number");
|
661
|
+
lastChild.querySelector("input").value = objectPointer;
|
662
|
+
} else {
|
663
|
+
this.coeAssignType(lastChild, "string");
|
664
|
+
lastChild.querySelector("input").value = objectPointer;
|
665
|
+
}
|
666
|
+
}
|
667
|
+
|
668
|
+
buildInitialCarryover(initialCarryover) {
|
669
|
+
this.clearCarryOverObject();
|
670
|
+
const base = document.querySelector("#carryOverObject > .rows");
|
671
|
+
|
672
|
+
for (const key in initialCarryover) {
|
673
|
+
this.digBuildInitialCarryover(this.coeAddRow(base), key, initialCarryover[key]);
|
674
|
+
}
|
614
675
|
}
|
615
676
|
|
616
|
-
|
677
|
+
reveal() {
|
617
678
|
if (this.#loadedComponents.flow && this.#loadedComponents.catalog) {
|
618
679
|
this.toggleBlocker(false);
|
619
680
|
}
|
@@ -652,7 +713,10 @@ window.Caixanegra.Designer = {
|
|
652
713
|
createUnit(params) {
|
653
714
|
this.#sequence++;
|
654
715
|
const newUnit = new Caixanegra.Designer.Unit({
|
655
|
-
position: new Sabertooth.Vector2(
|
716
|
+
position: new Sabertooth.Vector2(
|
717
|
+
-this.gre.worldCenter.x + 20,
|
718
|
+
-this.gre.worldCenter.y + 20
|
719
|
+
),
|
656
720
|
type: params.type,
|
657
721
|
title: params.title,
|
658
722
|
class: params.class,
|
@@ -710,6 +774,21 @@ window.Caixanegra.Designer = {
|
|
710
774
|
});
|
711
775
|
}
|
712
776
|
|
777
|
+
cloneUnit(oid) {
|
778
|
+
const unit = this.#units.find((unit) => unit.oid === oid);
|
779
|
+
const dup = this.#dupObject(unit);
|
780
|
+
|
781
|
+
dup.oid = Sabertooth.Utils.generateOId();
|
782
|
+
dup.position = { x: unit.position.x + 25, y: unit.position.y + 25 };
|
783
|
+
dup.title = `${dup.title} (copy)`;
|
784
|
+
|
785
|
+
dup.exits.forEach(exit => {
|
786
|
+
delete exit.target;
|
787
|
+
});
|
788
|
+
|
789
|
+
this.createUnit(dup);
|
790
|
+
}
|
791
|
+
|
713
792
|
showError(message) {
|
714
793
|
this.actionMessage(true, message, "error");
|
715
794
|
setTimeout(() => {this.actionMessage(false)}, 5000);
|
@@ -759,6 +838,73 @@ window.Caixanegra.Designer = {
|
|
759
838
|
this.unitDebugHitsPane.classList.remove("-open");
|
760
839
|
}
|
761
840
|
|
841
|
+
#dupObject(obj) {
|
842
|
+
if (typeof obj !== 'object' || obj === null) {
|
843
|
+
return obj;
|
844
|
+
}
|
845
|
+
|
846
|
+
const clonedObject = Array.isArray(obj) ? [] : {};
|
847
|
+
|
848
|
+
for (const key in obj) {
|
849
|
+
if (obj.hasOwnProperty(key)) {
|
850
|
+
clonedObject[key] = this.#dupObject(obj[key]);
|
851
|
+
}
|
852
|
+
}
|
853
|
+
|
854
|
+
return clonedObject;
|
855
|
+
}
|
856
|
+
|
857
|
+
#importFlow() {
|
858
|
+
document.querySelector("#importFlowFile").click();
|
859
|
+
}
|
860
|
+
|
861
|
+
#importFlowFileChanged() {
|
862
|
+
const fileInput = document.querySelector("#importFlowFile");
|
863
|
+
|
864
|
+
if (fileInput.files.length === 0) {
|
865
|
+
return;
|
866
|
+
}
|
867
|
+
|
868
|
+
const selectedFile = fileInput.files[0];
|
869
|
+
const reader = new FileReader();
|
870
|
+
|
871
|
+
reader.onload = (event) => {
|
872
|
+
this.toggleBlocker(true, "importing your flow");
|
873
|
+
this.gre.disable();
|
874
|
+
const flowBackup = this.flowSnapshot();
|
875
|
+
|
876
|
+
try {
|
877
|
+
this.#loadFlowFromJSON(JSON.parse(event.target.result));
|
878
|
+
|
879
|
+
this.actionMessage(true, "Flow imported", "ok");
|
880
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
881
|
+
} catch (error) {
|
882
|
+
this.#loadFlowFromJSON(flowBackup);
|
883
|
+
|
884
|
+
this.actionMessage(true, "Failed to import flow. Rolled back", "error");
|
885
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
886
|
+
}
|
887
|
+
|
888
|
+
this.gre.enable();
|
889
|
+
this.reveal();
|
890
|
+
};
|
891
|
+
|
892
|
+
reader.readAsText(selectedFile);
|
893
|
+
}
|
894
|
+
|
895
|
+
#exportFlow() {
|
896
|
+
const blob = new Blob([JSON.stringify(this.flowSnapshot())], { type: "application/json" });
|
897
|
+
const url = URL.createObjectURL(blob);
|
898
|
+
const link = document.createElement('a');
|
899
|
+
link.href = url;
|
900
|
+
link.download = "flow_definition.cxn";
|
901
|
+
link.click();
|
902
|
+
URL.revokeObjectURL(url);
|
903
|
+
|
904
|
+
this.actionMessage(true, "Flow exported", "ok");
|
905
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
906
|
+
}
|
907
|
+
|
762
908
|
#executeFlow() {
|
763
909
|
this.toggleBlocker(true, "saving your flow");
|
764
910
|
|
@@ -3,6 +3,10 @@ $accent-color: rgb(105, 105, 105);
|
|
3
3
|
$accent-highlight-color: lighten($accent-color, 20%);
|
4
4
|
$accent-contrast-color: darken($accent-color, 20%);
|
5
5
|
|
6
|
+
#importFlowFile {
|
7
|
+
display: none;
|
8
|
+
}
|
9
|
+
|
6
10
|
#execution {
|
7
11
|
position: fixed;
|
8
12
|
display: flex;
|
@@ -37,6 +41,33 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
37
41
|
}
|
38
42
|
}
|
39
43
|
|
44
|
+
#io {
|
45
|
+
position: fixed;
|
46
|
+
display: flex;
|
47
|
+
flex-direction: row;
|
48
|
+
bottom: 8px;
|
49
|
+
right: 8px;
|
50
|
+
|
51
|
+
div {
|
52
|
+
cursor: pointer;
|
53
|
+
transition: all 250ms ease-in-out;
|
54
|
+
height: 18px;
|
55
|
+
width: 18px;
|
56
|
+
margin-left: 8px;
|
57
|
+
|
58
|
+
&:hover {
|
59
|
+
transform: scale(1.1);
|
60
|
+
}
|
61
|
+
|
62
|
+
svg {
|
63
|
+
path, polygon, line, polyline {
|
64
|
+
fill: white;
|
65
|
+
stroke: white;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
40
71
|
#addUnit {
|
41
72
|
position: fixed;
|
42
73
|
border-radius: 50%;
|
@@ -649,10 +680,25 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
649
680
|
flex-direction: row;
|
650
681
|
justify-content: space-between;
|
651
682
|
|
652
|
-
.name {
|
653
|
-
font-size: 1em;
|
654
|
-
font-weight: bold;
|
683
|
+
.name-holder {
|
655
684
|
width: 100%;
|
685
|
+
display: flex;
|
686
|
+
flex-direction: row;
|
687
|
+
|
688
|
+
.name {
|
689
|
+
font-size: 1em;
|
690
|
+
font-weight: bold;
|
691
|
+
}
|
692
|
+
|
693
|
+
#duplicate {
|
694
|
+
margin-left: 5px;
|
695
|
+
width: 10px;
|
696
|
+
cursor: pointer;
|
697
|
+
|
698
|
+
svg path {
|
699
|
+
fill: $accent-contrast-color;
|
700
|
+
}
|
701
|
+
}
|
656
702
|
}
|
657
703
|
|
658
704
|
.delete {
|
@@ -25,11 +25,28 @@
|
|
25
25
|
</svg>
|
26
26
|
</div>
|
27
27
|
<div id="console">
|
28
|
-
<svg version="1.1"
|
28
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
29
29
|
<path d="M24,24H0V0h24V24z M2,22h20V2H6v2h16v2H2V22z M2,4h2V2H2V4z M16,16h-6v-2h6V16z M5.7,15.7l-1.4-1.4L6.6,12L4.3,9.7 l1.4-1.4L9.4,12L5.7,15.7z"/>
|
30
30
|
</svg>
|
31
31
|
</div>
|
32
32
|
</div>
|
33
|
+
<div id="io">
|
34
|
+
<input type="file" id="importFlowFile" accept=".cxn">
|
35
|
+
<div id="importFlow">
|
36
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
37
|
+
<polyline id="primary" points="13 9 13 13 9 13" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline>
|
38
|
+
<line id="primary-2" data-name="primary" x1="3" y1="3" x2="13" y2="13" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
39
|
+
<path id="primary-3" data-name="primary" d="M13.89,5H20a1,1,0,0,1,1,1V20a1,1,0,0,1-1,1H6a1,1,0,0,1-1-1V13.89" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
40
|
+
</svg>
|
41
|
+
</div>
|
42
|
+
<div id="exportFlow">
|
43
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
44
|
+
<polyline id="primary" points="17 3 21 3 21 7" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline>
|
45
|
+
<line id="primary-2" data-name="primary" x1="11" y1="13" x2="21" y2="3" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
46
|
+
<path id="primary-3" data-name="primary" d="M19,13.89V20a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V6A1,1,0,0,1,4,5h6.11" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
47
|
+
</svg>
|
48
|
+
</div>
|
49
|
+
</div>
|
33
50
|
<div id="addUnit"><span>+</span></div>
|
34
51
|
<div id="save"><span>save</span></div>
|
35
52
|
<div id="unitMenu"></div>
|
@@ -51,7 +68,14 @@
|
|
51
68
|
<div class="color-code"></div>
|
52
69
|
<div class="content">
|
53
70
|
<div id="unitDetailClass" class="unit-detail-headers">
|
54
|
-
<div class="name"
|
71
|
+
<div class="name-holder">
|
72
|
+
<div class="name"></div>
|
73
|
+
<span id="duplicate">
|
74
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
75
|
+
<path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2zm4 4v-2h2v2h2v2H8v2H6v-2H4v-2h2z"/>
|
76
|
+
</svg>
|
77
|
+
</span>
|
78
|
+
</div>
|
55
79
|
<div class="delete">
|
56
80
|
<div id="toggler">
|
57
81
|
<svg viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
|
data/lib/caixanegra/manager.rb
CHANGED
@@ -6,8 +6,13 @@ module Caixanegra
|
|
6
6
|
|
7
7
|
scopes = (scope || "").split(",").map(&:to_sym)
|
8
8
|
Caixanegra.units.each do |unit|
|
9
|
-
|
10
|
-
|
9
|
+
unit_scope = unit.is_a?(Array) ? unit[0].scope : unit.scope
|
10
|
+
if unit_scope.nil? || unit_scope.any? { |checking_scope| scopes.include?(checking_scope) }
|
11
|
+
if unit.is_a? Array
|
12
|
+
units[unit[1]] = unit[0]
|
13
|
+
else
|
14
|
+
units[unit.name.demodulize.underscore.to_sym] = unit
|
15
|
+
end
|
11
16
|
end
|
12
17
|
end
|
13
18
|
|
@@ -18,11 +23,15 @@ module Caixanegra
|
|
18
23
|
units = {}
|
19
24
|
|
20
25
|
Caixanegra.units.each do |unit|
|
21
|
-
|
26
|
+
if unit.is_a? Array
|
27
|
+
units[unit[1]] = unit[0]
|
28
|
+
else
|
29
|
+
units[unit.name.demodulize.underscore.to_sym] = unit
|
30
|
+
end
|
22
31
|
end
|
23
32
|
|
24
33
|
units
|
25
34
|
end
|
26
35
|
end
|
27
36
|
end
|
28
|
-
end
|
37
|
+
end
|
data/lib/caixanegra/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caixanegra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergiorribeiro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|