diff --git a/dist/css/ueblueprint-style.css b/dist/css/ueblueprint-style.css
index 6cb7f3e..16337af 100755
--- a/dist/css/ueblueprint-style.css
+++ b/dist/css/ueblueprint-style.css
@@ -1 +1 @@
-@font-face{font-family:"Roboto";font-style:light;src:url("../font/roboto-light.woff2") format("woff2"),url("../font/roboto-light.woff") format("woff")}@font-face{font-family:"Roboto";font-style:regular;src:url("../font/roboto-regular.woff2") format("woff2"),url("../font/roboto-regular.woff") format("woff")}:root{--ueb-fron-size: 13px;--ueb-viewport-height: 30rem;--ueb-viewport-width: 100%;--ueb-grid-size: 16px;--ueb-grid-line-width: 2px;--ueb-grid-line-color: #353535;--ueb-grid-set: 8;--ueb-grid-set-line-color: #161616;--ueb-grid-axis-line-color: black;--ueb-grid-snap: 16px;--ueb-node-radius: 8px}ueb-blueprint{display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-fron-size);user-select:none}.ueb-viewport-header{display:flex;position:absolute;top:0;right:0;left:0;height:1.5em;background:rgba(0, 0, 0, 0.5);z-index:1}.ueb-viewport-zoom{color:#4d4d4db7}.ueb-viewport-body{position:relative;height:var(--ueb-viewport-height);width:var(--ueb-viewport-width);overflow:hidden;scrollbar-width:0}ueb-blueprint[data-focused=true] .ueb-viewport-body{overflow:scroll}.ueb-grid{--ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));position:absolute;min-width:100%;min-height:100%;width:calc((100% + var(--ueb-additional-x)*1px)/var(--ueb-scale));height:calc((100% + var(--ueb-additional-y)*1px)/var(--ueb-scale));background-color:#262626;background-image:linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(to right, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to right, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent);background-size:100% var(--ueb-grid-line-actual-width),var(--ueb-grid-line-actual-width) 100%,calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);background-position:calc(var(--ueb-translate-x)*1px) calc(var(--ueb-translate-y)*1px);background-repeat:repeat-x,repeat-y,repeat,repeat,repeat,repeat;transform:scale(var(--ueb-scale), var(--ueb-scale));transform-origin:0 0;overflow:hidden}.ueb-grid[data-drag-scrolling=true]{cursor:grabbing}.ueb-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom--1.ueb{--ueb-scale: 0.875}.ueb-zoom--2.ueb{--ueb-scale: 0.75}.ueb-zoom--3.ueb{--ueb-scale: 0.675}.ueb-zoom--4.ueb{--ueb-scale: 0.5;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--5.ueb{--ueb-scale: 0.375;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--6.ueb{--ueb-scale: 0.333333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--7.ueb{--ueb-scale: 0.3;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--8.ueb{--ueb-scale: 0.266666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--9.ueb{--ueb-scale: 0.233333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--10.ueb{--ueb-scale: 0.2;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--11.ueb{--ueb-scale: 0.166666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-zoom--12.ueb{--ueb-scale: 0.133333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}ueb-node{display:block;position:absolute;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 black,1px 4px 6px 0 rgba(0, 0, 0, 0.3);will-change:transform}.ueb-grid[data-drag-scrolling=false] ueb-selector[data-selecting=false]~ueb-node{cursor:move}.ueb-node-border{margin:-3px;padding:3px;border-radius:calc(var(--ueb-node-radius)*1.4)}.ueb-selected{z-index:1}.ueb-selected>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y;outline:3px solid #cc6700;outline-offset:-6px}.ueb-node-content{position:relative;padding:1px;box-shadow:inset 0 0 2px 0 black;border-radius:var(--ueb-node-radius);background:rgba(0, 0, 0, 0.7);overflow:hidden}.ueb-node-header{padding:.2em .7em;box-shadow:inset 0 1px 2px 0 #313631,inset 0 2px 0 0 #92c381;border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);color:silver;font-weight:600;white-space:nowrap}.ueb-node-name{background:radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);margin:-0.1em -1.6em;padding:.1em 1.6em}.ueb-node-body{display:flex;padding:6px 0;color:white;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}ueb-pin{display:block;padding:1px 2px}.ueb-grid[data-drag-scrolling=false]{cursor:default}ueb-selector[data-selecting=false]~ueb-node ueb-pin:hover{background:var(--ueb-node-value-background)}.ueb-grid[data-drag-scrolling=false] ueb-selector[data-selecting=false]~ueb-node ueb-pin .ueb-node-value-icon{cursor:crosshair}.ueb-node-value-icon{display:inline-block;position:relative;width:.85em;height:.85em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-node-value-icon::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-node-value-color);border-radius:50%}.ueb-node-value-fill::before{background:var(--ueb-node-value-color)}.ueb-node-value-icon::after{content:"";display:block;position:absolute;top:calc(50% - .3em);left:calc(100% + 1px);width:0;height:0;border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid var(--ueb-node-value-color)}.ueb-selector{display:block;position:absolute;visibility:hidden;top:0;left:0;width:0;height:0;background-image:repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(1px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale)));background-size:100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%,calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%;background-position:0 calc(1px/var(--ueb-scale)),0 0,0 calc(100% - 1px/var(--ueb-scale)),0 100%,calc(1px/var(--ueb-scale)) 0,0 0,calc(100% - 1px/var(--ueb-scale)) 0,100% 0;background-repeat:no-repeat}ueb-selector>*{visibility:visible}ueb-selector[data-selecting=true]{visibility:visible;top:min(var(--ueb-select-from-y)*1px,var(--ueb-select-to-y)*1px);left:min(var(--ueb-select-from-x)*1px,var(--ueb-select-to-x)*1px);width:calc(max(var(--ueb-select-from-x) - var(--ueb-select-to-x),var(--ueb-select-to-x) - var(--ueb-select-from-x))*1px);height:calc(max(var(--ueb-select-from-y) - var(--ueb-select-to-y),var(--ueb-select-to-y) - var(--ueb-select-from-y))*1px)}/*# sourceMappingURL=ueblueprint-style.css.map */
+@font-face{font-family:"Roboto";font-style:light;src:url("../font/roboto-light.woff2") format("woff2"),url("../font/roboto-light.woff") format("woff")}@font-face{font-family:"Roboto";font-style:regular;src:url("../font/roboto-regular.woff2") format("woff2"),url("../font/roboto-regular.woff") format("woff")}:root{--ueb-fron-size: 13px;--ueb-viewport-height: 30rem;--ueb-viewport-width: 100%;--ueb-grid-size: 16px;--ueb-grid-line-width: 2px;--ueb-grid-line-color: #353535;--ueb-grid-set: 8;--ueb-grid-set-line-color: #161616;--ueb-grid-axis-line-color: black;--ueb-grid-snap: 16px;--ueb-node-radius: 8px}ueb-blueprint{display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-fron-size);user-select:none}.ueb-viewport-header{display:flex;position:absolute;top:0;right:0;left:0;height:1.5em;background:rgba(0, 0, 0, 0.5);z-index:1}.ueb-viewport-zoom{color:#4d4d4db7}.ueb-viewport-body{position:relative;height:var(--ueb-viewport-height);width:var(--ueb-viewport-width);overflow:hidden;scrollbar-width:0}ueb-blueprint[data-focused=true] .ueb-viewport-body{overflow:scroll}.ueb-grid{--ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));position:absolute;min-width:100%;min-height:100%;width:calc((100% + var(--ueb-additional-x)*1px)/var(--ueb-scale));height:calc((100% + var(--ueb-additional-y)*1px)/var(--ueb-scale));background-color:#262626;background-image:linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(to right, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to right, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent);background-size:100% var(--ueb-grid-line-actual-width),var(--ueb-grid-line-actual-width) 100%,calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);background-position:calc(var(--ueb-translate-x)*1px) calc(var(--ueb-translate-y)*1px);background-repeat:repeat-x,repeat-y,repeat,repeat,repeat,repeat;transform:scale(var(--ueb-scale), var(--ueb-scale));transform-origin:0 0;overflow:hidden}.ueb-grid[data-drag-scrolling=true]{cursor:grabbing}.ueb-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom--1.ueb{--ueb-scale: 0.875}.ueb-zoom--2.ueb{--ueb-scale: 0.75}.ueb-zoom--3.ueb{--ueb-scale: 0.675}.ueb-zoom--4.ueb{--ueb-scale: 0.5;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--5.ueb{--ueb-scale: 0.375;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--6.ueb{--ueb-scale: 0.333333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--7.ueb{--ueb-scale: 0.3;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--8.ueb{--ueb-scale: 0.266666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--9.ueb{--ueb-scale: 0.233333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--10.ueb{--ueb-scale: 0.2;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--11.ueb{--ueb-scale: 0.166666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-zoom--12.ueb{--ueb-scale: 0.133333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}ueb-node{display:block;position:absolute;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 black,1px 4px 6px 0 rgba(0, 0, 0, 0.3);will-change:transform}.ueb-grid[data-drag-scrolling=false] ueb-selector[data-selecting=false]~ueb-node{cursor:move}.ueb-node-border{margin:-3px;padding:3px;border-radius:calc(var(--ueb-node-radius)*1.4)}.ueb-selected{z-index:1}.ueb-selected>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y;outline:3px solid #cc6700;outline-offset:-6px}.ueb-node-content{position:relative;padding:1px;box-shadow:inset 0 0 2px 0 black;border-radius:var(--ueb-node-radius);background:rgba(0, 0, 0, 0.7);overflow:hidden}.ueb-node-header{padding:.2em .7em;box-shadow:inset 0 1px 2px 0 #313631,inset 0 2px 0 0 #92c381;border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);color:silver;font-weight:600;white-space:nowrap}.ueb-node-name{background:radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);margin:-0.1em -1.6em;padding:.1em 1.6em}.ueb-node-body{display:flex;padding:6px 0;color:white;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}ueb-pin{display:block;padding:1px 2px}.ueb-grid[data-drag-scrolling=false]{cursor:default}.ueb-grid[data-drag-scrolling=false] ueb-selector[data-selecting=false]~ueb-node ueb-pin:hover{background:var(--ueb-node-value-background);cursor:crosshair}.ueb-node-value-icon{display:inline-block;position:relative;width:.85em;height:.85em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-node-value-icon::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-node-value-color);border-radius:50%}.ueb-node-value-fill::before{background:var(--ueb-node-value-color)}.ueb-node-value-icon::after{content:"";display:block;position:absolute;top:calc(50% - .3em);left:calc(100% + 1px);width:0;height:0;border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid var(--ueb-node-value-color)}ueb-selector[data-selecting=true],ueb-link{visibility:visible;top:min(var(--ueb-from-y)*1px,var(--ueb-to-y)*1px);left:min(var(--ueb-from-x)*1px,var(--ueb-to-x)*1px);width:calc(max(var(--ueb-from-x) - var(--ueb-to-x),var(--ueb-to-x) - var(--ueb-from-x))*1px);height:calc(max(var(--ueb-from-y) - var(--ueb-to-y),var(--ueb-to-y) - var(--ueb-from-y))*1px)}ueb-selector{display:block;position:absolute;visibility:hidden;top:0;left:0;width:0;height:0;background-image:repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(1px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale)));background-size:100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%,calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%;background-position:0 calc(1px/var(--ueb-scale)),0 0,0 calc(100% - 1px/var(--ueb-scale)),0 100%,calc(1px/var(--ueb-scale)) 0,0 0,calc(100% - 1px/var(--ueb-scale)) 0,100% 0;background-repeat:no-repeat}ueb-selector>*{visibility:visible}ueb-link{display:block;position:absolute;border:2px solid red}/*# sourceMappingURL=ueblueprint-style.css.map */
diff --git a/dist/css/ueblueprint-style.css.map b/dist/css/ueblueprint-style.css.map
index 114b740..008d1e9 100755
--- a/dist/css/ueblueprint-style.css.map
+++ b/dist/css/ueblueprint-style.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-style.css"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,MACI,sBACA,6BACA,2BACA,sBACA,2BACA,+BACA,kBACA,mCACA,kCACA,sBACA,uBAGJ,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,8BACA,UAGJ,mBACI,gBAGJ,mBACI,kBACA,kCACA,gCACA,gBACA,kBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,oCACI,gBAGJ,qBAGI,eACA,6CAGJ,iBAEI,mBAGJ,iBAEI,kBAGJ,iBAEI,mBAGJ,iBAEI,iBACA,uDAGJ,iBAEI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBAEI,iBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,SACI,cACA,kBACA,sGACA,qCACA,4DACA,sBAGJ,iFACI,YAGJ,iBACI,YACA,YACA,+CAGJ,cACI,UAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,iCACA,qCACA,8BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,YACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,QACI,cACA,gBAGJ,qCACI,eAGJ,0DACI,4CAGJ,8GACI,iBAGJ,qBACI,qBACA,kBACA,YACA,aACA,wBACA,wBAGJ,6BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,6CACA,kBAGJ,6BACI,uCAGJ,4BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,mDAGJ,cACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,eACI,mBAGJ,kCACI,mBACA,iEACA,kEACA,yHACA","file":"ueblueprint-style.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-style.css"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,MACI,sBACA,6BACA,2BACA,sBACA,2BACA,+BACA,kBACA,mCACA,kCACA,sBACA,uBAGJ,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,8BACA,UAGJ,mBACI,gBAGJ,mBACI,kBACA,kCACA,gCACA,gBACA,kBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,oCACI,gBAGJ,qBAGI,eACA,6CAGJ,iBAEI,mBAGJ,iBAEI,kBAGJ,iBAEI,mBAGJ,iBAEI,iBACA,uDAGJ,iBAEI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBAEI,iBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,SACI,cACA,kBACA,sGACA,qCACA,4DACA,sBAGJ,iFACI,YAGJ,iBACI,YACA,YACA,+CAGJ,cACI,UAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,iCACA,qCACA,8BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,YACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,QACI,cACA,gBAGJ,qCACI,eAGJ,+FACI,4CACA,iBAGJ,qBACI,qBACA,kBACA,YACA,aACA,wBACA,wBAGJ,6BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,6CACA,kBAGJ,6BACI,uCAGJ,4BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,mDAGJ,2CAEI,mBACA,mDACA,oDACA,6FACA,8FAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,eACI,mBAGJ,SACI,cACA,kBACA","file":"ueblueprint-style.css"}
\ No newline at end of file
diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js
index 5dd5452..724c815 100755
--- a/dist/ueblueprint.js
+++ b/dist/ueblueprint.js
@@ -318,11 +318,11 @@ class GraphElement extends HTMLElement {
*/
constructor(entity, template) {
super();
- /** @type {Blueprint}" */
+ /** @type {Blueprint} */
this.blueprint = null;
- /** @type {Entity}" */
+ /** @type {Entity} */
this.entity = entity;
- /** @type {Template}" */
+ /** @type {Template} */
this.template = template;
/** @type {Context[]} */
this.inputObjects = [];
@@ -357,7 +357,7 @@ const tagReplacement = {
'"': '"'
};
-function sanitizeText$1(value) {
+function sanitizeText(value) {
if (value.constructor === String) {
return value.replace(/[&<>'"]/g, tag => tagReplacement[tag])
}
@@ -399,7 +399,6 @@ class SelectorTemplate extends Template {
*/
apply(selector) {
super.apply(selector);
- selector.classList.add("ueb-selector");
this.applyFinishSelecting(selector);
}
@@ -409,11 +408,11 @@ class SelectorTemplate extends Template {
*/
applyStartSelecting(selector, initialPosition) {
// Set initial position
- selector.style.setProperty("--ueb-select-from-x", sanitizeText$1(initialPosition[0]));
- selector.style.setProperty("--ueb-select-from-y", sanitizeText$1(initialPosition[1]));
+ selector.style.setProperty("--ueb-from-x", sanitizeText(initialPosition[0]));
+ selector.style.setProperty("--ueb-from-y", sanitizeText(initialPosition[1]));
// Final position coincide with the initial position, at the beginning of selection
- selector.style.setProperty("--ueb-select-to-x", sanitizeText$1(initialPosition[0]));
- selector.style.setProperty("--ueb-select-to-y", sanitizeText$1(initialPosition[1]));
+ selector.style.setProperty("--ueb-to-x", sanitizeText(initialPosition[0]));
+ selector.style.setProperty("--ueb-to-y", sanitizeText(initialPosition[1]));
selector.dataset.selecting = "true";
}
@@ -422,8 +421,8 @@ class SelectorTemplate extends Template {
* @param {GraphSelector} selector Selector element
*/
applyDoSelecting(selector, finalPosition) {
- selector.style.setProperty("--ueb-select-to-x", sanitizeText$1(finalPosition[0]));
- selector.style.setProperty("--ueb-select-to-y", sanitizeText$1(finalPosition[1]));
+ selector.style.setProperty("--ueb-to-x", sanitizeText(finalPosition[0]));
+ selector.style.setProperty("--ueb-to-y", sanitizeText(finalPosition[1]));
}
/**
@@ -472,6 +471,9 @@ class GraphSelector extends GraphElement {
customElements.define("ueb-selector", GraphSelector);
+/**
+ * This solves the sole purpose of providing compression capability for html inside template literals strings. Check rollup.config.js function minifyHTML()
+ */
const html = String.raw;
/** @typedef {import("../Blueprint").default} Blueprint */
@@ -500,10 +502,10 @@ class BlueprintTemplate extends Template {
@@ -546,8 +548,8 @@ class BlueprintTemplate extends Template {
* @param {Blueprint} blueprint The blueprint element
*/
applyZoom(blueprint, newZoom) {
- blueprint.classList.remove("ueb-zoom-" + sanitizeText$1(blueprint.zoom));
- blueprint.classList.add("ueb-zoom-" + sanitizeText$1(newZoom));
+ blueprint.classList.remove("ueb-zoom-" + sanitizeText(blueprint.zoom));
+ blueprint.classList.add("ueb-zoom-" + sanitizeText(newZoom));
}
/**
@@ -555,8 +557,8 @@ class BlueprintTemplate extends Template {
* @param {Blueprint} blueprint The blueprint element
*/
applyExpand(blueprint) {
- blueprint.gridElement.style.setProperty("--ueb-additional-x", sanitizeText$1(blueprint.additional[0]));
- blueprint.gridElement.style.setProperty("--ueb-additional-y", sanitizeText$1(blueprint.additional[1]));
+ blueprint.gridElement.style.setProperty("--ueb-additional-x", sanitizeText(blueprint.additional[0]));
+ blueprint.gridElement.style.setProperty("--ueb-additional-y", sanitizeText(blueprint.additional[1]));
}
/**
@@ -564,8 +566,8 @@ class BlueprintTemplate extends Template {
* @param {Blueprint} blueprint The blueprint element
*/
applyTranlate(blueprint) {
- blueprint.gridElement.style.setProperty("--ueb-translate-x", sanitizeText$1(blueprint.translateValue[0]));
- blueprint.gridElement.style.setProperty("--ueb-translate-y", sanitizeText$1(blueprint.translateValue[1]));
+ blueprint.gridElement.style.setProperty("--ueb-translate-x", sanitizeText(blueprint.translateValue[0]));
+ blueprint.gridElement.style.setProperty("--ueb-translate-y", sanitizeText(blueprint.translateValue[1]));
}
/**
@@ -753,6 +755,16 @@ class Utility {
return getComputedStyle(element).getPropertyValue("--ueb-scale")
}
+ static convertLocation(viewportLocation, movementElement) {
+ const scaleCorrection = 1 / Utility.getScale(movementElement);
+ const targetOffset = movementElement.getBoundingClientRect();
+ let location = [
+ Math.round((viewportLocation[0] - targetOffset.x) * scaleCorrection),
+ Math.round((viewportLocation[1] - targetOffset.y) * scaleCorrection)
+ ];
+ return location
+ }
+
/**
* Sets a value in an object
* @param {String[]} keys The chained keys to access from object in order to set the value
@@ -1462,6 +1474,176 @@ class Copy extends Context {
}
}
+/**
+ * @typedef {import("../graph/GraphLink").default} GraphLink
+ */
+class LinkTemplate extends Template {
+
+ /**
+ * Computes the html content of the target element.
+ * @param {GraphLink} link Link connecting two graph nodes
+ * @returns The result html
+ */
+ render(link) {
+ return html`
+
+ `
+ }
+
+ /**
+ * Applies the style to the element.
+ * @param {GraphLink} link Element of the graph
+ */
+ apply(link) {
+ super.apply(link);
+ }
+
+ /**
+ * Applies the style relative to the source pin location.
+ * @param {GraphLink} link Link element
+ */
+ applySourceLocation(link, initialPosition) {
+ // Set initial position
+ link.style.setProperty("--ueb-from-x", sanitizeText(initialPosition[0]));
+ link.style.setProperty("--ueb-from-y", sanitizeText(initialPosition[1]));
+ }
+
+ /**
+ * Applies the style relative to the destination pin location.
+ * @param {GraphLink} link Link element
+ */
+ applyDestinationLocation(link, finalPosition) {
+ link.style.setProperty("--ueb-to-x", sanitizeText(finalPosition[0]));
+ link.style.setProperty("--ueb-to-y", sanitizeText(finalPosition[1]));
+ }
+}
+
+/**
+ * @typedef {import("./GraphPin").default} GraphPin
+ */
+class GraphLink extends GraphElement {
+
+ /** @type {GraphPin} */
+ #source
+ /** @type {GraphPin} */
+ #destination
+ #nodeDeleteHandler = _ => this.blueprint.removeGraphElement(this)
+ #nodeDragSourceHandler = _ => this.setSourceLocation(this.#source.getLinkLocation())
+ #nodeDragDestinatonHandler = _ => this.setDestinationLocation(this.#destination.getLinkLocation())
+
+ /**
+ * @param {?GraphPin} source
+ * @param {?GraphPin} destination
+ */
+ constructor(source, destination) {
+ super({}, new LinkTemplate());
+ /** @type {import("../template/LinkTemplate").default} */
+ this.template;
+ this.setSourcePin(source);
+ this.setDestinationPin(destination);
+ }
+
+ setSourceLocation(location) {
+ if (location == null) {
+ location = this.#source.template.getLinkLocation(this.#source);
+ }
+ this.template.applySourceLocation(this, location);
+ }
+
+ setDestinationLocation(location) {
+ if (location == null) {
+ location = this.#destination.template.getLinkLocation(this.#destination);
+ }
+ this.template.applyDestinationLocation(this, location);
+ }
+
+
+ getSourcePin() {
+ return this.#source
+ }
+
+ /**
+ * @param {GraphPin} graphPin
+ */
+ setSourcePin(graphPin) {
+ this.#source?.removeEventListener("ueb-node-delete", this.#nodeDeleteHandler);
+ this.#source?.removeEventListener("ueb-node-drag", this.#nodeDragSourceHandler);
+ this.#source = graphPin;
+ this.#source?.addEventListener("ueb-node-delete", this.#nodeDeleteHandler);
+ this.#source?.addEventListener("ueb-node-drag", this.#nodeDragSourceHandler);
+ this.setSourceLocation();
+ this.originatesFromInput = this.#destination == null;
+ }
+
+ getDestinationPin() {
+ return this.#destination
+ }
+
+ /**
+ *
+ * @param {GraphPin} graphPin
+ */
+ setDestinationPin(graphPin) {
+ this.#destination?.removeEventListener("ueb-node-delete", this.#nodeDeleteHandler);
+ this.#destination?.removeEventListener("ueb-node-drag", this.#nodeDragDestinatonHandler);
+ this.#destination = graphPin;
+ this.#destination?.addEventListener("ueb-node-delete", this.#nodeDeleteHandler);
+ this.#destination?.addEventListener("ueb-node-drag", this.#nodeDragDestinatonHandler);
+ this.originatesFromInput = false;
+ }
+}
+
+customElements.define("ueb-link", GraphLink);
+
+/**
+ * @typedef {import("../graph/GraphPin").default} GraphPin
+ */
+class PinTemplate extends Template {
+
+ /**
+ * Computes the html content of the pin.
+ * @param {GraphPin} pin Pin entity
+ * @returns The result html
+ */
+ render(pin) {
+ if (pin.isInput()) {
+ return html`
+
+ ${sanitizeText(pin.getPinDisplayName())}
+ `
+ } else {
+ return html`
+ ${sanitizeText(pin.getPinDisplayName())}
+
+ `
+ }
+ }
+
+ /**
+ * Applies the style to the element.
+ * @param {GraphPin} pin Element of the graph
+ */
+ apply(pin) {
+ super.apply(pin);
+ pin.classList.add("ueb-node-" + pin.isInput() ? "input" : "output", "ueb-node-value-" + sanitizeText(pin.getType()));
+ pin.clickableElement = pin;
+ }
+
+ /**
+ *
+ * @param {GraphPin} pin
+ * @returns
+ */
+ getLinkLocation(pin) {
+ const rect = pin.querySelector(".ueb-node-value-icon").getBoundingClientRect();
+ return Utility.convertLocation(
+ [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2],
+ pin.blueprint.gridElement)
+ }
+}
+
class Pointing extends Context {
constructor(target, blueprint, options) {
@@ -1474,14 +1656,8 @@ class Pointing extends Context {
* @param {MouseEvent} mouseEvent
* @returns
*/
- getLocation(mouseEvent) {
- const scaleCorrection = 1 / Utility.getScale(this.target);
- const targetOffset = this.movementSpace.getBoundingClientRect();
- let location = [
- Math.round((mouseEvent.clientX - targetOffset.x) * scaleCorrection),
- Math.round((mouseEvent.clientY - targetOffset.y) * scaleCorrection)
- ];
- return location
+ locationFromEvent(mouseEvent) {
+ return Utility.convertLocation([mouseEvent.clientX, mouseEvent.clientY], this.movementSpace)
}
}
@@ -1514,7 +1690,7 @@ class MouseClickDrag extends Pointing {
// Attach the listeners
movementListenedElement.addEventListener("mousemove", self.mouseStartedMovingHandler);
document.addEventListener("mouseup", self.mouseUpHandler);
- self.clickedPosition = self.getLocation(e);
+ self.clickedPosition = self.locationFromEvent(e);
self.clicked(self.clickedPosition);
return true
}
@@ -1544,7 +1720,7 @@ class MouseClickDrag extends Pointing {
this.mouseMoveHandler = e => {
e.preventDefault();
e.stopPropagation();
- const location = self.getLocation(e);
+ const location = self.locationFromEvent(e);
const movement = [e.movementX, e.movementY];
self.dragTo(location, movement);
};
@@ -1591,183 +1767,60 @@ class MouseClickDrag extends Pointing {
}
}
-class MouseScrollGraph extends MouseClickDrag {
-
- startDrag() {
- this.blueprint.template.applyStartDragScrolling(this.blueprint);
- }
-
- dragTo(location, movement) {
- this.blueprint.scrollDelta([-movement[0], -movement[1]]);
- }
-
- endDrag() {
- this.blueprint.template.applyEndDragScrolling(this.blueprint);
- }
-}
-
-/**
- * @typedef {import("../graph/GraphLink").default} GraphLink
- */
-class LinkTemplate extends Template {
-
- /**
- * Computes the html content of the target element.
- * @param {GraphLink} link Link connecting two graph nodes
- * @returns The result html
- */
- render(link) {
- return html`
-
- `
- }
-
- /**
- * Applies the style to the element.
- * @param {GraphLink} link Element of the graph
- */
- apply(link) {
- super.apply(link);
-
- }
-
- /**
- * Applies the style relative to the source pin location.
- * @param {GraphLink} link Link element
- */
- applySourceLocation(link, initialPosition) {
- // Set initial position
- link.style.setProperty("--ueb-link-from-x", sanitizeText(initialPosition[0]));
- link.style.setProperty("--ueb-link-from-y", sanitizeText(initialPosition[1]));
- }
-
- /**
- * Applies the style relative to the destination pin location.
- * @param {GraphLink} link Link element
- */
- applyDestinationLocation(link, finalPosition) {
- link.style.setProperty("--ueb-link-to-x", sanitizeText(finalPosition[0]));
- link.style.setProperty("--ueb-link-to-y", sanitizeText(finalPosition[1]));
- }
-}
-
-/**
- * @type {import("./GraphPin").default} GraphPin
- */
-class GraphLink extends GraphElement {
-
- /** @type {GraphPin} */
- #source
- /** @type {GraphPin} */
- #destination
- #nodeDeleteHandler = _ => this.blueprint.removeGraphElement(this)
- #nodeDragSourceHandler = _ => this.setSourceLocation(this.#source.getLinkLocation())
- #nodeDragDestinatonHandler = _ => this.setDestinationLocation(this.#destination.getLinkLocation())
-
- /**
- * @param {?GraphPin} source
- * @param {?GraphPin} destination
- */
- constructor(source, destination) {
- super(this, new LinkTemplate());
- /** @type {import("../template/LinkTemplate").default} */
- this.template;
- this.setSource(source);
- this.setDestination(destination);
- }
-
- setSourceLocation(location) {
- this.template.applySourceLocation(this.#source.getLinkLocation());
- }
-
- setDestinationLocation(location) {
- this.template.applyDestinationLocation(this.#destination.getLinkLocation());
- }
-
- /**
- * @param {GraphPin} graphPin
- */
- setSourcePin(graphPin) {
- this.#source?.removeEventListener("ueb-node-delete", this.#nodeDeleteHandler);
- this.#source?.removeEventListener("ueb-node-drag", this.#nodeDragSourceHandler);
- this.#source = graphPin;
- this.#source?.addEventListener("ueb-node-delete", this.#nodeDeleteHandler);
- this.#source?.addEventListener("ueb-node-drag", this.#nodeDragSourceHandler);
- }
-
- /**
- *
- * @param {GraphPin} graphPin
- */
- setDestinationPin(graphPin) {
- this.#destination?.removeEventListener("ueb-node-delete", this.#nodeDeleteHandler);
- this.#destination?.removeEventListener("ueb-node-drag", this.#nodeDragDestinatonHandler);
- this.#destination = graphPin;
- this.#destination?.addEventListener("ueb-node-delete", this.#nodeDeleteHandler);
- this.#destination?.addEventListener("ueb-node-drag", this.#nodeDragDestinatonHandler);
- }
-}
-
-customElements.define("ueb-link", GraphLink);
-
-/**
- * @typedef {import("../graph/GraphPin").default} GraphPin
- */
-class PinTemplate extends Template {
-
- /**
- * Computes the html content of the pin.
- * @param {GraphPin} pin Pin entity
- * @returns The result html
- */
- render(pin) {
- if (pin.isInput()) {
- return html`
-
- ${sanitizeText$1(pin.getPinDisplayName())}
- `
- } else {
- return html`
- ${sanitizeText$1(pin.getPinDisplayName())}
-
- `
- }
- }
-
- /**
- * Applies the style to the element.
- * @param {GraphPin} pin Element of the graph
- */
- apply(pin) {
- super.apply(pin);
- pin.classList.add("ueb-node-" + pin.isInput() ? "input" : "output", "ueb-node-value-" + sanitizeText$1(pin.getType()));
- pin.clickableElement = pin.querySelector(".ueb-node-value-icon");
- }
-}
-
class MouseCreateLink extends MouseClickDrag {
+ /** @type {(e: MouseEvent) => void} */
+ #mouseenterHandler
+
+ /** @type {(e: MouseEvent) => void} */
+ #mouseleaveHandler
+
constructor(target, blueprint, options) {
super(target, blueprint, options);
/** @type {import("../../graph/GraphPin").default} */
this.target;
/** @type {import("../../graph/GraphLink").default} */
this.link;
+ /** @type {import("../../entity/PinEntity").default} */
+ this.enteredPin;
+
+ let self = this;
+ this.#mouseenterHandler = e => {
+ if (!self.enteredPin) {
+ self.enteredPin = e.target;
+ }
+ };
+ this.#mouseleaveHandler = e => {
+ if (self.enteredPin == e.target) {
+ self.enteredPin = null;
+ }
+ };
}
startDrag() {
- this.target.dragLink();
-
+ this.link = new GraphLink(this.target, null);
+ this.blueprint.nodesContainerElement.insertBefore(this.link, this.blueprint.selectorElement.nextElementSibling);
+ this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
+ .forEach(pin => {
+ pin.addEventListener("mouseenter", this.#mouseenterHandler);
+ pin.addEventListener("mouseleave", this.#mouseleaveHandler);
+ });
}
dragTo(location, movement) {
- //this.selectorElement.doSelecting(location)
+ this.link.setDestinationLocation(location);
}
endDrag() {
- if (this.started) ;
+ this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
+ .forEach(pin => {
+ pin.removeEventListener("mouseenter", this.#mouseenterHandler);
+ pin.removeEventListener("mouseleave", this.#mouseleaveHandler);
+ });
+ if (this.enteredPin) {
+ this.link.setDestinationPin(this.link);
+ }
+ this.link = null;
}
}
@@ -1777,6 +1830,8 @@ class GraphPin extends GraphElement {
super(entity, new PinTemplate());
/** @type {import("../entity/PinEntity").default} */
this.entity;
+ /** @type {PinTemplate} */
+ this.template;
/** @type {HTMLElement} */
this.clickableElement = null;
}
@@ -1784,7 +1839,8 @@ class GraphPin extends GraphElement {
createInputObjects() {
return [
new MouseCreateLink(this.clickableElement, this.blueprint, {
- moveEverywhere: true
+ moveEverywhere: true,
+ looseTarget: true
}),
]
}
@@ -1817,15 +1873,6 @@ class GraphPin extends GraphElement {
return this.entity.getType()
}
- /**
- *
- * @returns {GraphLink} The link created
- */
- dragLink() {
- let link = new GraphLink(this);
- return link
- }
-
/**
* Returns The exact location where the link originates from or arrives at.
* @returns {Number[]} The location array
@@ -1847,8 +1894,8 @@ class SelectableDraggableTemplate extends Template {
* @param {SelectableDraggable} element Element of the graph
*/
applyLocation(element) {
- element.style.setProperty("--ueb-position-x", sanitizeText$1(element.location[0]));
- element.style.setProperty("--ueb-position-y", sanitizeText$1(element.location[1]));
+ element.style.setProperty("--ueb-position-x", sanitizeText(element.location[0]));
+ element.style.setProperty("--ueb-position-y", sanitizeText(element.location[1]));
}
/**
@@ -1904,7 +1951,7 @@ class NodeTemplate extends SelectableDraggableTemplate {
@@ -1925,8 +1972,8 @@ class NodeTemplate extends SelectableDraggableTemplate {
if (node.selected) {
node.classList.add("ueb-selected");
}
- node.style.setProperty("--ueb-position-x", sanitizeText$1(node.location[0]));
- node.style.setProperty("--ueb-position-y", sanitizeText$1(node.location[1]));
+ node.style.setProperty("--ueb-position-x", sanitizeText(node.location[0]));
+ node.style.setProperty("--ueb-position-y", sanitizeText(node.location[1]));
/** @type {HTMLElement} */
let inputContainer = node.querySelector(".ueb-node-inputs");
/** @type {HTMLElement} */
@@ -2211,6 +2258,39 @@ class KeyvoardCanc extends KeyboardShortcut {
}
}
+class KeyboardSelectAll extends KeyboardShortcut {
+
+ /**
+ *
+ * @param {HTMLElement} target
+ * @param {import("../../Blueprint").default} blueprint
+ * @param {Object} options
+ */
+ constructor(target, blueprint, options = {}) {
+ options = KeyboardShortcut.keyOptionsParse(options, Configuration.selectAllKeyboardKey);
+ super(target, blueprint, options);
+ }
+
+ fire() {
+ this.blueprint.selectAll();
+ }
+}
+
+class MouseScrollGraph extends MouseClickDrag {
+
+ startDrag() {
+ this.blueprint.template.applyStartDragScrolling(this.blueprint);
+ }
+
+ dragTo(location, movement) {
+ this.blueprint.scrollDelta([-movement[0], -movement[1]]);
+ }
+
+ endDrag() {
+ this.blueprint.template.applyEndDragScrolling(this.blueprint);
+ }
+}
+
class MouseTracking extends Pointing {
constructor(target, blueprint, options = {}) {
@@ -2219,7 +2299,7 @@ class MouseTracking extends Pointing {
let self = this;
this.mousemoveHandler = e => {
- self.blueprint.entity.mousePosition = self.getLocation(e);
+ self.blueprint.entity.mousePosition = self.locationFromEvent(e);
return true
};
}
@@ -2356,7 +2436,7 @@ class MouseWheel extends Pointing {
this.mouseWheelHandler = e => {
e.preventDefault();
- const location = self.getLocation(e);
+ const location = self.locationFromEvent(e);
self.wheel(Math.sign(e.deltaY), location);
return true
};
@@ -2391,24 +2471,6 @@ class Zoom extends MouseWheel {
}
}
-class KeyboardSelectAll extends KeyboardShortcut {
-
- /**
- *
- * @param {HTMLElement} target
- * @param {import("../../Blueprint").default} blueprint
- * @param {Object} options
- */
- constructor(target, blueprint, options = {}) {
- options = KeyboardShortcut.keyOptionsParse(options, Configuration.selectAllKeyboardKey);
- super(target, blueprint, options);
- }
-
- fire() {
- this.blueprint.selectAll();
- }
-}
-
class Blueprint extends GraphElement {
constructor() {
@@ -2488,7 +2550,6 @@ class Blueprint extends GraphElement {
createInputObjects() {
return [
-
new Copy(this.getGridDOMElement(), this),
new Paste(this.getGridDOMElement(), this),
new KeyvoardCanc(this.getGridDOMElement(), this),
diff --git a/js/Blueprint.js b/js/Blueprint.js
index 548f6cf..080195a 100755
--- a/js/Blueprint.js
+++ b/js/Blueprint.js
@@ -1,19 +1,19 @@
import BlueprintTemplate from "./template/BlueprintTemplate"
import Configuration from "./Configuration"
import Copy from "./input/common/Copy"
-import MouseScrollGraph from "./input/mouse/MouseScrollGraph"
import GraphElement from "./graph/GraphElement"
import GraphLink from "./graph/GraphLink"
import GraphNode from "./graph/GraphNode"
import GraphSelector from "./graph/GraphSelector"
import KeyboardCanc from "./input/keybaord/KeyboardCanc"
+import KeyboardSelectAll from "./input/keybaord/KeyboardSelectAll"
+import MouseScrollGraph from "./input/mouse/MouseScrollGraph"
import MouseTracking from "./input/mouse/MouseTracking"
import Paste from "./input/common/Paste"
import Select from "./input/mouse/Select"
import Unfocus from "./input/mouse/Unfocus"
import Utility from "./Utility"
import Zoom from "./input/mouse/Zoom"
-import KeyboardSelectAll from "./input/keybaord/KeyboardSelectAll"
export default class Blueprint extends GraphElement {
@@ -94,7 +94,6 @@ export default class Blueprint extends GraphElement {
createInputObjects() {
return [
-
new Copy(this.getGridDOMElement(), this),
new Paste(this.getGridDOMElement(), this),
new KeyboardCanc(this.getGridDOMElement(), this),
diff --git a/js/Utility.js b/js/Utility.js
index ba6a6b9..dd53a4a 100755
--- a/js/Utility.js
+++ b/js/Utility.js
@@ -10,6 +10,16 @@ export default class Utility {
return getComputedStyle(element).getPropertyValue("--ueb-scale")
}
+ static convertLocation(viewportLocation, movementElement) {
+ const scaleCorrection = 1 / Utility.getScale(movementElement)
+ const targetOffset = movementElement.getBoundingClientRect()
+ let location = [
+ Math.round((viewportLocation[0] - targetOffset.x) * scaleCorrection),
+ Math.round((viewportLocation[1] - targetOffset.y) * scaleCorrection)
+ ]
+ return location
+ }
+
/**
* Sets a value in an object
* @param {String[]} keys The chained keys to access from object in order to set the value
diff --git a/js/graph/GraphElement.js b/js/graph/GraphElement.js
index c510686..c326e4d 100755
--- a/js/graph/GraphElement.js
+++ b/js/graph/GraphElement.js
@@ -14,11 +14,11 @@ export default class GraphElement extends HTMLElement {
*/
constructor(entity, template) {
super()
- /** @type {Blueprint}" */
+ /** @type {Blueprint} */
this.blueprint = null
- /** @type {Entity}" */
+ /** @type {Entity} */
this.entity = entity
- /** @type {Template}" */
+ /** @type {Template} */
this.template = template
/** @type {Context[]} */
this.inputObjects = []
diff --git a/js/graph/GraphLink.js b/js/graph/GraphLink.js
index 046182f..01bf52b 100755
--- a/js/graph/GraphLink.js
+++ b/js/graph/GraphLink.js
@@ -3,7 +3,7 @@ import LinkTemplate from "../template/LinkTemplate"
/**
- * @type {import("./GraphPin").default} GraphPin
+ * @typedef {import("./GraphPin").default} GraphPin
*/
export default class GraphLink extends GraphElement {
@@ -20,19 +20,30 @@ export default class GraphLink extends GraphElement {
* @param {?GraphPin} destination
*/
constructor(source, destination) {
- super(this, new LinkTemplate())
+ super({}, new LinkTemplate())
/** @type {import("../template/LinkTemplate").default} */
this.template
- this.setSource(source)
- this.setDestination(destination)
+ this.setSourcePin(source)
+ this.setDestinationPin(destination)
}
setSourceLocation(location) {
- this.template.applySourceLocation(this.#source.getLinkLocation())
+ if (location == null) {
+ location = this.#source.template.getLinkLocation(this.#source)
+ }
+ this.template.applySourceLocation(this, location)
}
setDestinationLocation(location) {
- this.template.applyDestinationLocation(this.#destination.getLinkLocation())
+ if (location == null) {
+ location = this.#destination.template.getLinkLocation(this.#destination)
+ }
+ this.template.applyDestinationLocation(this, location)
+ }
+
+
+ getSourcePin() {
+ return this.#source
}
/**
@@ -44,6 +55,12 @@ export default class GraphLink extends GraphElement {
this.#source = graphPin
this.#source?.addEventListener("ueb-node-delete", this.#nodeDeleteHandler)
this.#source?.addEventListener("ueb-node-drag", this.#nodeDragSourceHandler)
+ this.setSourceLocation()
+ this.originatesFromInput = this.#destination == null
+ }
+
+ getDestinationPin() {
+ return this.#destination
}
/**
@@ -56,6 +73,7 @@ export default class GraphLink extends GraphElement {
this.#destination = graphPin
this.#destination?.addEventListener("ueb-node-delete", this.#nodeDeleteHandler)
this.#destination?.addEventListener("ueb-node-drag", this.#nodeDragDestinatonHandler)
+ this.originatesFromInput = false
}
}
diff --git a/js/graph/GraphNode.js b/js/graph/GraphNode.js
index 1e0f755..4348251 100755
--- a/js/graph/GraphNode.js
+++ b/js/graph/GraphNode.js
@@ -1,8 +1,8 @@
import NodeTemplate from "../template/NodeTemplate"
import ObjectEntity from "../entity/ObjectEntity"
+import PinEntity from "../entity/PinEntity"
import SelectableDraggable from "./SelectableDraggable"
import SerializerFactory from "../serialization/SerializerFactory"
-import PinEntity from "../entity/PinEntity"
export default class GraphNode extends SelectableDraggable {
diff --git a/js/graph/GraphPin.js b/js/graph/GraphPin.js
index ab8a255..cba80fb 100755
--- a/js/graph/GraphPin.js
+++ b/js/graph/GraphPin.js
@@ -1,7 +1,6 @@
import GraphElement from "./GraphElement"
import PinTemplate from "../template/PinTemplate"
import MouseCreateLink from "../input/mouse/MouseCreateLink"
-import GraphLink from "./GraphLink"
export default class GraphPin extends GraphElement {
@@ -9,6 +8,8 @@ export default class GraphPin extends GraphElement {
super(entity, new PinTemplate())
/** @type {import("../entity/PinEntity").default} */
this.entity
+ /** @type {PinTemplate} */
+ this.template
/** @type {HTMLElement} */
this.clickableElement = null
}
@@ -16,7 +17,8 @@ export default class GraphPin extends GraphElement {
createInputObjects() {
return [
new MouseCreateLink(this.clickableElement, this.blueprint, {
- moveEverywhere: true
+ moveEverywhere: true,
+ looseTarget: true
}),
]
}
@@ -49,15 +51,6 @@ export default class GraphPin extends GraphElement {
return this.entity.getType()
}
- /**
- *
- * @returns {GraphLink} The link created
- */
- dragLink() {
- let link = new GraphLink(this)
- return link
- }
-
/**
* Returns The exact location where the link originates from or arrives at.
* @returns {Number[]} The location array
diff --git a/js/input/mouse/MouseClickDrag.js b/js/input/mouse/MouseClickDrag.js
index 9f1b223..97db318 100755
--- a/js/input/mouse/MouseClickDrag.js
+++ b/js/input/mouse/MouseClickDrag.js
@@ -29,7 +29,7 @@ export default class MouseClickDrag extends Pointing {
// Attach the listeners
movementListenedElement.addEventListener("mousemove", self.mouseStartedMovingHandler)
document.addEventListener("mouseup", self.mouseUpHandler)
- self.clickedPosition = self.getLocation(e)
+ self.clickedPosition = self.locationFromEvent(e)
self.clicked(self.clickedPosition)
return true
}
@@ -59,7 +59,7 @@ export default class MouseClickDrag extends Pointing {
this.mouseMoveHandler = e => {
e.preventDefault()
e.stopPropagation()
- const location = self.getLocation(e)
+ const location = self.locationFromEvent(e)
const movement = [e.movementX, e.movementY]
self.dragTo(location, movement)
}
diff --git a/js/input/mouse/MouseCreateLink.js b/js/input/mouse/MouseCreateLink.js
index bd25764..50188ee 100755
--- a/js/input/mouse/MouseCreateLink.js
+++ b/js/input/mouse/MouseCreateLink.js
@@ -1,29 +1,61 @@
+import GraphLink from "../../graph/GraphLink"
import MouseClickDrag from "./MouseClickDrag"
export default class MouseCreateLink extends MouseClickDrag {
+ /** @type {(e: MouseEvent) => void} */
+ #mouseenterHandler
+
+ /** @type {(e: MouseEvent) => void} */
+ #mouseleaveHandler
+
constructor(target, blueprint, options) {
super(target, blueprint, options)
/** @type {import("../../graph/GraphPin").default} */
this.target
/** @type {import("../../graph/GraphLink").default} */
this.link
+ /** @type {import("../../entity/PinEntity").default} */
+ this.enteredPin
+
+ let self = this
+ this.#mouseenterHandler = e => {
+ if (!self.enteredPin) {
+ self.enteredPin = e.target
+ }
+ }
+ this.#mouseleaveHandler = e => {
+ if (self.enteredPin == e.target) {
+ self.enteredPin = null
+ }
+ }
}
startDrag() {
- let link = this.target.dragLink()
-
+ this.link = new GraphLink(this.target, null)
+ this.blueprint.nodesContainerElement.insertBefore(this.link, this.blueprint.selectorElement.nextElementSibling)
+ this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
+ .forEach(pin => {
+ pin.addEventListener("mouseenter", this.#mouseenterHandler)
+ pin.addEventListener("mouseleave", this.#mouseleaveHandler)
+ })
}
dragTo(location, movement) {
- //this.selectorElement.doSelecting(location)
+ this.link.setDestinationLocation(location)
}
endDrag() {
- if (this.started) {
- //this.selectorElement.finishSelecting()
+ this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
+ .forEach(pin => {
+ pin.removeEventListener("mouseenter", this.#mouseenterHandler)
+ pin.removeEventListener("mouseleave", this.#mouseleaveHandler)
+ })
+ if (this.enteredPin) {
+ this.link.setDestinationPin(this.link)
} else {
- // this.blueprint.unselectAll()
+ // this.link.remove()
}
+ this.link = null
}
}
diff --git a/js/input/mouse/MouseTracking.js b/js/input/mouse/MouseTracking.js
index 143c81a..95b2071 100755
--- a/js/input/mouse/MouseTracking.js
+++ b/js/input/mouse/MouseTracking.js
@@ -8,7 +8,7 @@ export default class MouseTracking extends Pointing {
let self = this
this.mousemoveHandler = e => {
- self.blueprint.entity.mousePosition = self.getLocation(e)
+ self.blueprint.entity.mousePosition = self.locationFromEvent(e)
return true
}
}
diff --git a/js/input/mouse/MouseWheel.js b/js/input/mouse/MouseWheel.js
index 32162bf..54e0c70 100755
--- a/js/input/mouse/MouseWheel.js
+++ b/js/input/mouse/MouseWheel.js
@@ -16,7 +16,7 @@ export default class MouseWheel extends Pointing {
this.mouseWheelHandler = e => {
e.preventDefault()
- const location = self.getLocation(e)
+ const location = self.locationFromEvent(e)
self.wheel(Math.sign(e.deltaY), location)
return true
}
diff --git a/js/input/mouse/Pointing.js b/js/input/mouse/Pointing.js
index 81e6626..2d15d70 100755
--- a/js/input/mouse/Pointing.js
+++ b/js/input/mouse/Pointing.js
@@ -13,13 +13,7 @@ export default class Pointing extends Context {
* @param {MouseEvent} mouseEvent
* @returns
*/
- getLocation(mouseEvent) {
- const scaleCorrection = 1 / Utility.getScale(this.target)
- const targetOffset = this.movementSpace.getBoundingClientRect()
- let location = [
- Math.round((mouseEvent.clientX - targetOffset.x) * scaleCorrection),
- Math.round((mouseEvent.clientY - targetOffset.y) * scaleCorrection)
- ]
- return location
+ locationFromEvent(mouseEvent) {
+ return Utility.convertLocation([mouseEvent.clientX, mouseEvent.clientY], this.movementSpace)
}
}
diff --git a/js/template/LinkTemplate.js b/js/template/LinkTemplate.js
index 7f1889f..8c08e68 100755
--- a/js/template/LinkTemplate.js
+++ b/js/template/LinkTemplate.js
@@ -1,4 +1,5 @@
import html from "./html"
+import sanitizeText from "./sanitizeText"
import Template from "./Template"
/**
@@ -25,7 +26,6 @@ export default class LinkTemplate extends Template {
*/
apply(link) {
super.apply(link)
-
}
/**
@@ -34,8 +34,8 @@ export default class LinkTemplate extends Template {
*/
applySourceLocation(link, initialPosition) {
// Set initial position
- link.style.setProperty("--ueb-link-from-x", sanitizeText(initialPosition[0]))
- link.style.setProperty("--ueb-link-from-y", sanitizeText(initialPosition[1]))
+ link.style.setProperty("--ueb-from-x", sanitizeText(initialPosition[0]))
+ link.style.setProperty("--ueb-from-y", sanitizeText(initialPosition[1]))
}
/**
@@ -43,7 +43,7 @@ export default class LinkTemplate extends Template {
* @param {GraphLink} link Link element
*/
applyDestinationLocation(link, finalPosition) {
- link.style.setProperty("--ueb-link-to-x", sanitizeText(finalPosition[0]))
- link.style.setProperty("--ueb-link-to-y", sanitizeText(finalPosition[1]))
+ link.style.setProperty("--ueb-to-x", sanitizeText(finalPosition[0]))
+ link.style.setProperty("--ueb-to-y", sanitizeText(finalPosition[1]))
}
}
diff --git a/js/template/PinTemplate.js b/js/template/PinTemplate.js
index f906900..a843e76 100755
--- a/js/template/PinTemplate.js
+++ b/js/template/PinTemplate.js
@@ -1,3 +1,4 @@
+import Utility from "../Utility"
import html from "./html"
import sanitizeText from "./sanitizeText"
import Template from "./Template"
@@ -33,6 +34,18 @@ export default class PinTemplate extends Template {
apply(pin) {
super.apply(pin)
pin.classList.add("ueb-node-" + pin.isInput() ? "input" : "output", "ueb-node-value-" + sanitizeText(pin.getType()))
- pin.clickableElement = pin.querySelector(".ueb-node-value-icon")
+ pin.clickableElement = pin
}
-}
\ No newline at end of file
+
+ /**
+ *
+ * @param {GraphPin} pin
+ * @returns
+ */
+ getLinkLocation(pin) {
+ const rect = pin.querySelector(".ueb-node-value-icon").getBoundingClientRect()
+ return Utility.convertLocation(
+ [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2],
+ pin.blueprint.gridElement)
+ }
+}
diff --git a/js/template/SelectorTemplate.js b/js/template/SelectorTemplate.js
index 5b653ee..a9c4b83 100755
--- a/js/template/SelectorTemplate.js
+++ b/js/template/SelectorTemplate.js
@@ -12,7 +12,6 @@ export default class SelectorTemplate extends Template {
*/
apply(selector) {
super.apply(selector)
- selector.classList.add("ueb-selector")
this.applyFinishSelecting(selector)
}
@@ -22,11 +21,11 @@ export default class SelectorTemplate extends Template {
*/
applyStartSelecting(selector, initialPosition) {
// Set initial position
- selector.style.setProperty("--ueb-select-from-x", sanitizeText(initialPosition[0]))
- selector.style.setProperty("--ueb-select-from-y", sanitizeText(initialPosition[1]))
+ selector.style.setProperty("--ueb-from-x", sanitizeText(initialPosition[0]))
+ selector.style.setProperty("--ueb-from-y", sanitizeText(initialPosition[1]))
// Final position coincide with the initial position, at the beginning of selection
- selector.style.setProperty("--ueb-select-to-x", sanitizeText(initialPosition[0]))
- selector.style.setProperty("--ueb-select-to-y", sanitizeText(initialPosition[1]))
+ selector.style.setProperty("--ueb-to-x", sanitizeText(initialPosition[0]))
+ selector.style.setProperty("--ueb-to-y", sanitizeText(initialPosition[1]))
selector.dataset.selecting = "true"
}
@@ -35,8 +34,8 @@ export default class SelectorTemplate extends Template {
* @param {GraphSelector} selector Selector element
*/
applyDoSelecting(selector, finalPosition) {
- selector.style.setProperty("--ueb-select-to-x", sanitizeText(finalPosition[0]))
- selector.style.setProperty("--ueb-select-to-y", sanitizeText(finalPosition[1]))
+ selector.style.setProperty("--ueb-to-x", sanitizeText(finalPosition[0]))
+ selector.style.setProperty("--ueb-to-y", sanitizeText(finalPosition[1]))
}
/**
diff --git a/js/template/html.js b/js/template/html.js
index 856a94c..d24bfd1 100755
--- a/js/template/html.js
+++ b/js/template/html.js
@@ -1,2 +1,5 @@
+/**
+ * This solves the sole purpose of providing compression capability for html inside template literals strings. Check rollup.config.js function minifyHTML()
+ */
const html = String.raw
export default html
diff --git a/scss/ueblueprint-style.css b/scss/ueblueprint-style.css
index b9e766f..318efa6 100755
--- a/scss/ueblueprint-style.css
+++ b/scss/ueblueprint-style.css
@@ -1,77 +1,77 @@
@font-face {
- font-family: "Roboto";
- font-style : light;
- src :
+ font-family : "Roboto";
+ font-style : light;
+ src :
url("../font/roboto-light.woff2") format("woff2"),
url("../font/roboto-light.woff") format("woff");
}
@font-face {
- font-family: "Roboto";
- font-style : regular;
- src :
+ font-family : "Roboto";
+ font-style : regular;
+ src :
url("../font/roboto-regular.woff2") format("woff2"),
url("../font/roboto-regular.woff") format("woff");
}
:root {
- --ueb-fron-size : 13px;
- --ueb-viewport-height : 30rem;
- --ueb-viewport-width : 100%;
- --ueb-grid-size : 16px;
- --ueb-grid-line-width : 2px;
- --ueb-grid-line-color : #353535;
- --ueb-grid-set : 8;
- --ueb-grid-set-line-color : #161616;
- --ueb-grid-axis-line-color: black;
- --ueb-grid-snap : 16px;
- --ueb-node-radius : 8px;
+ --ueb-fron-size : 13px;
+ --ueb-viewport-height : 30rem;
+ --ueb-viewport-width : 100%;
+ --ueb-grid-size : 16px;
+ --ueb-grid-line-width : 2px;
+ --ueb-grid-line-color : #353535;
+ --ueb-grid-set : 8;
+ --ueb-grid-set-line-color : #161616;
+ --ueb-grid-axis-line-color : black;
+ --ueb-grid-snap : 16px;
+ --ueb-node-radius : 8px;
}
ueb-blueprint {
- display : block;
- position : relative;
- font-family: Roboto, Noto, Oxygen, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif;
- font-size : var(--ueb-fron-size);
- user-select: none;
+ display : block;
+ position : relative;
+ font-family : Roboto, Noto, Oxygen, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif;
+ font-size : var(--ueb-fron-size);
+ user-select : none;
}
.ueb-viewport-header {
- display : flex;
- position : absolute;
- top : 0;
- right : 0;
- left : 0;
- height : 1.5em;
- background: rgba(0, 0, 0, 0.5);
- z-index : 1;
+ display : flex;
+ position : absolute;
+ top : 0;
+ right : 0;
+ left : 0;
+ height : 1.5em;
+ background : rgba(0, 0, 0, 0.5);
+ z-index : 1;
}
.ueb-viewport-zoom {
- color: #4d4d4db7;
+ color : #4d4d4db7;
}
.ueb-viewport-body {
- position : relative;
- height : var(--ueb-viewport-height);
- width : var(--ueb-viewport-width);
- overflow : hidden;
- scrollbar-width: 0;
+ position : relative;
+ height : var(--ueb-viewport-height);
+ width : var(--ueb-viewport-width);
+ overflow : hidden;
+ scrollbar-width : 0;
}
ueb-blueprint[data-focused="true"] .ueb-viewport-body {
- overflow: scroll;
+ overflow : scroll;
}
.ueb-grid {
- --ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));
- position : absolute;
- min-width : 100%;
- min-height : 100%;
- width : calc((100% + var(--ueb-additional-x) * 1px) / var(--ueb-scale));
- height : calc((100% + var(--ueb-additional-y) * 1px) / var(--ueb-scale));
- background-color : #262626;
- background-image :
+ --ueb-grid-line-actual-width : calc(var(--ueb-grid-line-width) / var(--ueb-scale));
+ position : absolute;
+ min-width : 100%;
+ min-height : 100%;
+ width : calc((100% + var(--ueb-additional-x) * 1px) / var(--ueb-scale));
+ height : calc((100% + var(--ueb-additional-y) * 1px) / var(--ueb-scale));
+ background-color : #262626;
+ background-image :
/* Axis lines */
linear-gradient(var(--ueb-grid-axis-line-color),
var(--ueb-grid-axis-line-color)),
@@ -109,117 +109,117 @@ ueb-blueprint[data-focused="true"] .ueb-viewport-body {
/* Light grid */
var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),
var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);
- background-position: calc(var(--ueb-translate-x) * 1px) calc(var(--ueb-translate-y) * 1px);
- background-repeat : repeat-x, repeat-y, repeat, repeat, repeat, repeat;
- transform : scale(var(--ueb-scale), var(--ueb-scale));
- transform-origin : 0 0;
- overflow : hidden;
+ background-position : calc(var(--ueb-translate-x) * 1px) calc(var(--ueb-translate-y) * 1px);
+ background-repeat : repeat-x, repeat-y, repeat, repeat, repeat, repeat;
+ transform : scale(var(--ueb-scale), var(--ueb-scale));
+ transform-origin : 0 0;
+ overflow : hidden;
}
.ueb-grid[data-drag-scrolling="true"] {
- cursor: grabbing;
+ cursor : grabbing;
}
.ueb-zoom--.ueb,
.ueb {
/* 16/16 */
- --ueb-scale : 1;
- --ueb-grid-actual-size: var(--ueb-grid-size);
+ --ueb-scale : 1;
+ --ueb-grid-actual-size : var(--ueb-grid-size);
}
.ueb-zoom--1.ueb {
/* 14/16 */
- --ueb-scale: 0.875;
+ --ueb-scale : 0.875;
}
.ueb-zoom--2.ueb {
/* 12/16 */
- --ueb-scale: 0.75;
+ --ueb-scale : 0.75;
}
.ueb-zoom--3.ueb {
/* 10.8/16 */
- --ueb-scale: 0.675;
+ --ueb-scale : 0.675;
}
.ueb-zoom--4.ueb {
/* 8/16 */
- --ueb-scale : 0.5;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2);
+ --ueb-scale : 0.5;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 2);
}
.ueb-zoom--5.ueb {
/* 6/16 */
- --ueb-scale : 0.375;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2);
+ --ueb-scale : 0.375;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 2);
}
.ueb-zoom--6.ueb {
- --ueb-scale : 0.333333;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3);
+ --ueb-scale : 0.333333;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 3);
}
.ueb-zoom--7.ueb {
- --ueb-scale : 0.3;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3);
+ --ueb-scale : 0.3;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 3);
}
.ueb-zoom--8.ueb {
- --ueb-scale : 0.266666;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3);
+ --ueb-scale : 0.266666;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 3);
}
.ueb-zoom--9.ueb {
- --ueb-scale : 0.233333;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3);
+ --ueb-scale : 0.233333;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 3);
}
.ueb-zoom--10.ueb {
/* 12/16 */
- --ueb-scale : 0.2;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3);
+ --ueb-scale : 0.2;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 3);
}
.ueb-zoom--11.ueb {
/* 12/16 */
- --ueb-scale : 0.166666;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6);
+ --ueb-scale : 0.166666;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 6);
}
.ueb-zoom--12.ueb {
/* 12/16 */
- --ueb-scale : 0.133333;
- --ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6);
+ --ueb-scale : 0.133333;
+ --ueb-grid-actual-size : calc(var(--ueb-grid-size) * 6);
}
.ueb-grid-content {
- position : relative;
- width : 0;
- height : 0;
- transform: translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px));
+ position : relative;
+ width : 0;
+ height : 0;
+ transform : translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px));
}
ueb-node {
- display : block;
- position : absolute;
- transform : translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));
- border-radius: var(--ueb-node-radius);
- box-shadow : 0 0 1px 0 black, 1px 4px 6px 0 rgba(0, 0, 0, 0.3);
- will-change : transform;
+ display : block;
+ position : absolute;
+ transform : translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));
+ border-radius : var(--ueb-node-radius);
+ box-shadow : 0 0 1px 0 black, 1px 4px 6px 0 rgba(0, 0, 0, 0.3);
+ will-change : transform;
}
.ueb-grid[data-drag-scrolling="false"] ueb-selector[data-selecting="false"]~ueb-node {
- cursor: move;
+ cursor : move;
}
.ueb-node-border {
- margin : -3px;
- padding : 3px;
- border-radius: calc(var(--ueb-node-radius) * 1.4);
+ margin : -3px;
+ padding : 3px;
+ border-radius : calc(var(--ueb-node-radius) * 1.4);
}
.ueb-selected {
- z-index: 1;
+ z-index : 1;
}
.ueb-selected>.ueb-node-border {
@@ -228,118 +228,124 @@ ueb-node {
linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),
linear-gradient(to right, #cc6700 0%, #cc6700 100%),
linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);
- background-size : 100% 7px, 7px 100%, 100% 7px, 7px 100%;
- background-position: top, right, bottom, left;
- background-repeat : repeat-x, repeat-y, repeat-x, repeat-y;
- outline : 3px solid #cc6700;
- outline-offset : -6px;
+ background-size : 100% 7px, 7px 100%, 100% 7px, 7px 100%;
+ background-position : top, right, bottom, left;
+ background-repeat : repeat-x, repeat-y, repeat-x, repeat-y;
+ outline : 3px solid #cc6700;
+ outline-offset : -6px;
}
.ueb-node-content {
- position : relative;
- padding : 1px;
- box-shadow : inset 0 0 2px 0 black;
- border-radius: var(--ueb-node-radius);
- background : rgba(0, 0, 0, 0.7);
- overflow : hidden;
+ position : relative;
+ padding : 1px;
+ box-shadow : inset 0 0 2px 0 black;
+ border-radius : var(--ueb-node-radius);
+ background : rgba(0, 0, 0, 0.7);
+ overflow : hidden;
}
.ueb-node-header {
- padding : 0.2em 0.7em;
- box-shadow : inset 0 1px 2px 0 #313631, inset 0 2px 0 0 #92c381;
- border-radius: var(--ueb-node-radius) var(--ueb-node-radius) 0 0;
- background : linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);
- color : #c0c0c0;
- font-weight : 600;
- white-space : nowrap;
+ padding : 0.2em 0.7em;
+ box-shadow : inset 0 1px 2px 0 #313631, inset 0 2px 0 0 #92c381;
+ border-radius : var(--ueb-node-radius) var(--ueb-node-radius) 0 0;
+ background : linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);
+ color : #c0c0c0;
+ font-weight : 600;
+ white-space : nowrap;
}
.ueb-node-name {
- background: radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);
- margin : -0.1em -1.6em;
- padding : 0.1em 1.6em;
+ background : radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);
+ margin : -0.1em -1.6em;
+ padding : 0.1em 1.6em;
}
.ueb-node-body {
- display : flex;
- padding : 6px 0;
- color : white;
- font-weight: 100;
- white-space: nowrap;
+ display : flex;
+ padding : 6px 0;
+ color : white;
+ font-weight : 100;
+ white-space : nowrap;
}
.ueb-node-inputs {
- margin-right: auto;
- padding-left: 8px;
+ margin-right : auto;
+ padding-left : 8px;
}
.ueb-node-outputs {
- padding-right: 8px;
+ padding-right : 8px;
}
ueb-pin {
- display: block;
- padding: 1px 2px;
+ display : block;
+ padding : 1px 2px;
}
.ueb-grid[data-drag-scrolling="false"] {
- cursor: default;
+ cursor : default;
}
-ueb-selector[data-selecting="false"]~ueb-node ueb-pin:hover {
- background: var(--ueb-node-value-background);
-}
-
-.ueb-grid[data-drag-scrolling="false"] ueb-selector[data-selecting="false"]~ueb-node ueb-pin .ueb-node-value-icon {
- cursor: crosshair;
+.ueb-grid[data-drag-scrolling="false"] ueb-selector[data-selecting="false"]~ueb-node ueb-pin:hover {
+ background : var(--ueb-node-value-background);
+ cursor : crosshair;
}
.ueb-node-value-icon {
- display : inline-block;
- position : relative;
- width : 0.85em;
- height : 0.85em;
- vertical-align: baseline;
- margin : 0 0.4em -1px 0.1em;
+ display : inline-block;
+ position : relative;
+ width : 0.85em;
+ height : 0.85em;
+ vertical-align : baseline;
+ margin : 0 0.4em -1px 0.1em;
}
.ueb-node-value-icon::before {
- content : "";
- display : block;
- position : absolute;
- top : 0;
- right : 0;
- bottom : 0;
- left : 0;
- border : 2px solid var(--ueb-node-value-color);
- border-radius: 50%;
+ content : "";
+ display : block;
+ position : absolute;
+ top : 0;
+ right : 0;
+ bottom : 0;
+ left : 0;
+ border : 2px solid var(--ueb-node-value-color);
+ border-radius : 50%;
}
.ueb-node-value-fill::before {
- background: var(--ueb-node-value-color);
+ background : var(--ueb-node-value-color);
}
.ueb-node-value-icon::after {
- content : "";
- display : block;
- position : absolute;
- top : calc(50% - 0.3em);
- left : calc(100% + 1px);
- width : 0;
- height : 0;
- border-top : 0.3em solid transparent;
- border-bottom: 0.3em solid transparent;
- border-left : 0.3em solid var(--ueb-node-value-color);
+ content : "";
+ display : block;
+ position : absolute;
+ top : calc(50% - 0.3em);
+ left : calc(100% + 1px);
+ width : 0;
+ height : 0;
+ border-top : 0.3em solid transparent;
+ border-bottom : 0.3em solid transparent;
+ border-left : 0.3em solid var(--ueb-node-value-color);
}
-.ueb-selector {
- display : block;
- position : absolute;
- visibility: hidden;
- top : 0;
- left : 0;
- width : 0;
- height : 0;
+ueb-selector[data-selecting="true"],
+ueb-link {
+ visibility : visible;
+ top : min(var(--ueb-from-y) * 1px, var(--ueb-to-y) * 1px);
+ left : min(var(--ueb-from-x) * 1px, var(--ueb-to-x) * 1px);
+ width : calc(max(var(--ueb-from-x) - var(--ueb-to-x), var(--ueb-to-x) - var(--ueb-from-x)) * 1px);
+ height : calc(max(var(--ueb-from-y) - var(--ueb-to-y), var(--ueb-to-y) - var(--ueb-from-y)) * 1px);
+}
+
+ueb-selector {
+ display : block;
+ position : absolute;
+ visibility : hidden;
+ top : 0;
+ left : 0;
+ width : 0;
+ height : 0;
background-image:
/* Top */
repeating-linear-gradient(90deg,
@@ -415,17 +421,15 @@ ueb-selector[data-selecting="false"]~ueb-node ueb-pin:hover {
calc(1px / var(--ueb-scale)) 0, 0 0,
/* Right */
calc(100% - 1px / var(--ueb-scale)) 0, 100% 0;
- background-repeat: no-repeat;
+ background-repeat : no-repeat;
}
ueb-selector>* {
- visibility: visible;
+ visibility : visible;
}
-ueb-selector[data-selecting="true"] {
- visibility: visible;
- top : min(var(--ueb-select-from-y) * 1px, var(--ueb-select-to-y) * 1px);
- left : min(var(--ueb-select-from-x) * 1px, var(--ueb-select-to-x) * 1px);
- width : calc(max(var(--ueb-select-from-x) - var(--ueb-select-to-x), var(--ueb-select-to-x) - var(--ueb-select-from-x)) * 1px);
- height : calc(max(var(--ueb-select-from-y) - var(--ueb-select-to-y), var(--ueb-select-to-y) - var(--ueb-select-from-y)) * 1px);
+ueb-link {
+ display : block;
+ position : absolute;
+ border : 2px solid red;
}
\ No newline at end of file