caixanegra 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37e8a2fbf540c8bfe1f1c85cf1dd82638bde895abe10bf24369860a6996c1c7b
4
- data.tar.gz: f65ae1c5c97c7cd8264c157d93748748ec7e8084e7baba2c3f3c7113adfd60d9
3
+ metadata.gz: 002efa6f5c1a220426d102e5f055b50eff9fb55e5742d6c2c33d6474f890fd8a
4
+ data.tar.gz: 03c265dbfa950af4d043bfc56711593b2cb1661f76fc2ca40327fbc7a7f5e309
5
5
  SHA512:
6
- metadata.gz: 7eb792dd9e30f18921dd6ff8c4bab52a9c564f4f93204aeb8925bf5e0255093e64e00dfb9b16b9ac0f19991fccf9868157e17c788cafa66932ef5112f96b187d
7
- data.tar.gz: '09b9f690181fd28e25f5a8cd25ee1e3588af0c9efdadcd3597676fb80d9b93cbec43563d73a4086314f73fab34b11c27653cab163f10521121dd04c85a53a756'
6
+ metadata.gz: da48c854b8646515af968b7c685cece267a5bed236f1f58d5331de39a2d75bded3f0896b84d09de7cbe70c0fad7f388ef5891684bc04f45995e0b1afdc9d1784
7
+ data.tar.gz: 28b98d23640368db0f32475c4cd6209fb39648e723e63d70ae2fd814e88c05ca6c9033dccc1accfa3c2c331413569fb54e1b52f115cc26841703019dec4fa0a2
data/README.md CHANGED
@@ -46,7 +46,7 @@ This will give you the UID that **caixanegra** designer will understand, using t
46
46
 
47
47
  ```ruby
48
48
  my_flow = somewhere.get_flow # get from your own persistence or transport solution
49
- uid = Caixanegra::Manager.handler(my_flow || {})
49
+ uid = Caixanegra::Manager.handler(flow_definition: my_flow || {})
50
50
  ```
51
51
 
52
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.#reveal();
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?.units || []).forEach((unit) => {
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.#reveal();
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
- #reveal() {
677
+ reveal() {
617
678
  if (this.#loadedComponents.flow && this.#loadedComponents.catalog) {
618
679
  this.toggleBlocker(false);
619
680
  }
@@ -713,6 +774,21 @@ window.Caixanegra.Designer = {
713
774
  });
714
775
  }
715
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
+
716
792
  showError(message) {
717
793
  this.actionMessage(true, message, "error");
718
794
  setTimeout(() => {this.actionMessage(false)}, 5000);
@@ -762,6 +838,73 @@ window.Caixanegra.Designer = {
762
838
  this.unitDebugHitsPane.classList.remove("-open");
763
839
  }
764
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
+
765
908
  #executeFlow() {
766
909
  this.toggleBlocker(true, "saving your flow");
767
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" id="XMLID_75_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
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"></div>
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">
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title>Caixanegra</title>
4
+ <title>caixanegra flow designer</title>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
@@ -3,7 +3,7 @@
3
3
  module Caixanegra
4
4
  class Manager
5
5
  class << self
6
- def handler(flow_definition = {})
6
+ def handler(flow_definition: {})
7
7
  transient_store = Caixanegra.transient_store
8
8
  uid = transient_store.new_uid
9
9
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Caixanegra
4
- VERSION = '0.3.4'
4
+ VERSION = '0.4.0'
5
5
  end
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.3.4
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-09-20 00:00:00.000000000 Z
11
+ date: 2023-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails