diff --git a/dist/css/ueb-style.css b/dist/css/ueb-style.css index 51ec9df..aaacaa6 100644 --- a/dist/css/ueb-style.css +++ b/dist/css/ueb-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")}ueb-blueprint{display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-font-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,.5);z-index:1}.ueb-viewport-zoom{margin-left:auto;color:#4d4d4db7;font-size:20px}.ueb-viewport-body{position:relative;height:var(--ueb-height, 30rem);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-blueprint[data-drag-scrolling=true] .ueb-grid{cursor:grabbing}ueb-blueprint[data-drag-scrolling=false] .ueb-grid{cursor:default}.ueb-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom-7.ueb{--ueb-scale: 2}.ueb-zoom-6.ueb{--ueb-scale: 1.875}.ueb-zoom-5.ueb{--ueb-scale: 1.75}.ueb-zoom-4.ueb{--ueb-scale: 1.675}.ueb-zoom-3.ueb{--ueb-scale: 1.5}.ueb-zoom-2.ueb{--ueb-scale: 1.375}.ueb-zoom-1.ueb{--ueb-scale: 1.25}.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-positioned,ueb-blueprint[data-selecting=true] ueb-selector{--ueb-computed-min-x: min(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-max-x: max(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-min-y: min(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-max-y: max(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-width: max(var(--ueb-from-x) - var(--ueb-to-x), var(--ueb-to-x) - var(--ueb-from-x));--ueb-computed-height: max(var(--ueb-from-y) - var(--ueb-to-y), var(--ueb-to-y) - var(--ueb-from-y));position:absolute;top:calc(var(--ueb-computed-min-y)*1px);left:calc(var(--ueb-computed-min-x)*1px);width:calc(var(--ueb-computed-width)*1px);height:calc(var(--ueb-computed-height)*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-blueprint[data-selecting=true] ueb-selector{visibility:visible}ueb-selector>*{visibility:visible}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 #000,1px 4px 6px 0 rgba(0,0,0,.3)}ueb-blueprint[data-drag-scrolling=false][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>.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 #000;border-radius:var(--ueb-node-radius);background:rgba(10,10,10,.8);overflow:hidden}.ueb-node-header{padding:.3em .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:1px 0;color:#fff;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}.ueb-node-expand{display:none}ueb-node[data-advanced-display] .ueb-node-expand{display:block}ueb-pin{display:block;margin:6px 0;padding:2px 2px}ueb-pin>*{display:inline-block;vertical-align:middle}ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true]{display:none}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-pin:hover{background:var(--ueb-pin-background);cursor:crosshair}.ueb-node-outputs ueb-pin{text-align:right}.ueb-pin-icon-exec{display:inline-block;vertical-align:text-top}.ueb-pin-icon-value{display:inline-block;position:relative;width:1em;height:1em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-pin-icon-value::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-pin-color);border-radius:50%}ueb-pin.ueb-pin-fill .ueb-pin-icon-value::before{background:var(--ueb-pin-color)}.ueb-pin-icon-value::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-pin-color)}.ueb-pin-content>span{display:inline-block;vertical-align:middle}.ueb-pin-exec .ueb-pin-name{display:none}.ueb-pin-input{margin-left:3px;border:1px solid #a0a0a0;border-radius:3px;padding:1px 0 0 3px;color:silver}.ueb-pin-input:hover,.ueb-pin-input:active,.ueb-pin-input:focus,.ueb-pin-input:focus-within{background:#ffffff46;outline:none}.ueb-pin-input-content{display:block;outline:none;border:none;margin:0 3px 1px 0;padding:0;min-width:10px;max-width:400px;max-height:16em;background:none;cursor:text;overflow:auto}.ueb-pin-input-content::-webkit-scrollbar{width:10px;height:10px}.ueb-pin-input-content::-webkit-scrollbar-thumb{background:#575757;border-radius:10px;margin:4px}ueb-link{--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);--ueb-y-opposite: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);display:block;min-width:calc(var(--ueb-link-min-width)*1px);visibility:hidden}ueb-link svg{--ueb-y-opposite-coefficient: calc(2* var(--ueb-y-opposite) - 1);position:absolute;top:0;left:0;width:100%;height:100%;min-height:1px;transform:scaleY(calc(var(--ueb-y-opposite-coefficient) * var(--ueb-from-input-coefficient)));overflow:visible}ueb-link svg path{visibility:visible;stroke:var(--ueb-pin-color);stroke-width:1}ueb-link.ueb-link-dragging svg path,ueb-link svg g:hover path{stroke-width:5;transition:stroke-width .8s}ueb-link-message{display:block;visibility:visible;position:absolute;top:calc(100%*(1 - var(--ueb-y-opposite)) + 22px);left:calc((1 - var(--ueb-from-input))*100% + (var(--ueb-from-input-coefficient))*var(--ueb-start-percentage) + 15px);border:1px solid #000;padding:4px 8px;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-pin-dim-color);white-space:nowrap;z-index:1000000}.ueb{--ueb-pin-color: white;--ueb-pin-dim-color: #afafaf}.ueb-pin-boolean{--ueb-pin-color: #4d0000}.ueb-pin-class{--ueb-pin-color: #5800bb}.ueb-pin-float{--ueb-pin-color: #9ffb44}.ueb-pin-int{--ueb-pin-color: #1fe0ad}.ueb-pin-name{--ueb-pin-color: #cb81fc}.ueb-pin-object{--ueb-pin-color: #006603}.ueb-pin-rotator{--ueb-pin-color: #9eb1fc}.ueb-pin-string{--ueb-pin-color: #fc00d2;--ueb-pin-background: linear-gradient(90deg, #fc00d220, #fc00d280 15%, #fc00d250 85%, transparent)}.ueb-pin-vector{--ueb-pin-color: #fcc823}/*# sourceMappingURL=ueb-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")}ueb-blueprint{display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-font-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,.5);z-index:1}.ueb-viewport-zoom{margin-left:auto;color:#4d4d4db7;font-size:20px}.ueb-viewport-body{position:relative;height:var(--ueb-height, 30rem);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-blueprint[data-drag-scrolling=true] .ueb-grid{cursor:grabbing}ueb-blueprint[data-drag-scrolling=false] .ueb-grid{cursor:default}.ueb-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom-7.ueb{--ueb-scale: 2}.ueb-zoom-6.ueb{--ueb-scale: 1.875}.ueb-zoom-5.ueb{--ueb-scale: 1.75}.ueb-zoom-4.ueb{--ueb-scale: 1.675}.ueb-zoom-3.ueb{--ueb-scale: 1.5}.ueb-zoom-2.ueb{--ueb-scale: 1.375}.ueb-zoom-1.ueb{--ueb-scale: 1.25}.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-positioned,ueb-blueprint[data-selecting=true] ueb-selector{--ueb-computed-min-x: min(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-max-x: max(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-min-y: min(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-max-y: max(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-width: max(var(--ueb-from-x) - var(--ueb-to-x), var(--ueb-to-x) - var(--ueb-from-x));--ueb-computed-height: max(var(--ueb-from-y) - var(--ueb-to-y), var(--ueb-to-y) - var(--ueb-from-y));position:absolute;top:calc(var(--ueb-computed-min-y)*1px);left:calc(var(--ueb-computed-min-x)*1px);width:calc(var(--ueb-computed-width)*1px);height:calc(var(--ueb-computed-height)*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-blueprint[data-selecting=true] ueb-selector{visibility:visible}ueb-selector>*{visibility:visible}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 #000,1px 4px 6px 0 rgba(0,0,0,.3)}ueb-blueprint[data-drag-scrolling=false][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>.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 #000;border-radius:var(--ueb-node-radius);background:rgba(10,10,10,.8);overflow:hidden}.ueb-node-header{padding:.3em .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:1px 0;color:#fff;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}.ueb-node-expand{display:none}ueb-node[data-advanced-display] .ueb-node-expand{display:block}ueb-pin{display:block;margin:6px 0;padding:2px 2px}ueb-pin>*{display:inline-block;vertical-align:middle}ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true]{display:none}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-pin:hover{background:var(--ueb-pin-background);cursor:crosshair}.ueb-node-outputs ueb-pin{text-align:right}.ueb-pin-icon-exec{display:inline-block;vertical-align:text-top}.ueb-pin-icon-value{display:inline-block;position:relative;width:1em;height:1em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-pin-icon-value::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-pin-color);border-radius:50%}ueb-pin.ueb-pin-fill .ueb-pin-icon-value::before{background:var(--ueb-pin-color)}.ueb-pin-icon-value::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-pin-color)}.ueb-pin-content>span{display:inline-block;vertical-align:middle}.ueb-pin-exec .ueb-pin-name{display:none}.ueb-pin-input{margin-left:3px;border:1px solid #a0a0a0;border-radius:3px;padding:1px 0 0 3px;color:silver}.ueb-pin-input:hover,.ueb-pin-input:active,.ueb-pin-input:focus,.ueb-pin-input:focus-within{background:#ffffff46;outline:none}.ueb-pin-input-content{display:block;outline:none;border:none;margin:0 3px 1px 0;padding:0;min-width:10px;max-width:400px;max-height:16em;background:none;color:inherit;cursor:text;overflow:auto}.ueb-pin-input-content::-webkit-scrollbar{width:10px;height:10px}.ueb-pin-input-content::-webkit-scrollbar-thumb{background:#575757;border-radius:10px;margin:4px}ueb-link{--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);--ueb-y-opposite: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);display:block;min-width:calc(var(--ueb-link-min-width)*1px);visibility:hidden}ueb-link svg{--ueb-y-opposite-coefficient: calc(2* var(--ueb-y-opposite) - 1);position:absolute;top:0;left:0;width:100%;height:100%;min-height:1px;transform:scaleY(calc(var(--ueb-y-opposite-coefficient) * var(--ueb-from-input-coefficient)));overflow:visible}ueb-link svg path{visibility:visible;stroke:var(--ueb-pin-color);stroke-width:1}ueb-link.ueb-link-dragging svg path,ueb-link svg g:hover path{stroke-width:5;transition:stroke-width .8s}ueb-link-message{display:block;visibility:visible;position:absolute;top:calc(100%*(1 - var(--ueb-y-opposite)) + 22px);left:calc((1 - var(--ueb-from-input))*100% + (var(--ueb-from-input-coefficient))*var(--ueb-start-percentage) + 15px);border:1px solid #000;padding:4px 8px;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-pin-dim-color);white-space:nowrap;z-index:1000000}.ueb{--ueb-pin-color: white;--ueb-pin-dim-color: #afafaf}.ueb-pin-boolean{--ueb-pin-color: #4d0000}.ueb-pin-class{--ueb-pin-color: #5800bb}.ueb-pin-float{--ueb-pin-color: #9ffb44}.ueb-pin-int{--ueb-pin-color: #1fe0ad}.ueb-pin-name{--ueb-pin-color: #cb81fc}.ueb-pin-object{--ueb-pin-color: #006603}.ueb-pin-rotator{--ueb-pin-color: #9eb1fc}.ueb-pin-string{--ueb-pin-color: #fc00d2;--ueb-pin-background: linear-gradient(90deg, #fc00d220, #fc00d280 15%, #fc00d250 85%, transparent)}.ueb-pin-vector{--ueb-pin-color: #fcc823}/*# sourceMappingURL=ueb-style.css.map */ diff --git a/dist/css/ueb-style.css.map b/dist/css/ueb-style.css.map index 3c18732..453b71d 100644 --- a/dist/css/ueb-style.css.map +++ b/dist/css/ueb-style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-link.scss","../../scss/ueb-type-color.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,0BACA,UAGJ,mBACI,iBACA,gBACA,eAGJ,mBACI,kBACA,gCACA,gBACA,kBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,mDACI,eAGJ,qBAEI,eACA,6CAGJ,gBACI,eAGJ,gBACI,mBAGJ,gBACI,kBAGJ,gBACI,mBAGJ,gBACI,iBAGJ,gBACI,mBAGJ,gBACI,kBAGJ,iBACI,mBAGJ,iBACI,kBAGJ,iBACI,mBAGJ,iBACI,iBACA,uDAGJ,iBACI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBACI,iBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,gEACI,8DACA,8DACA,8DACA,8DACA,oGACA,qGACA,kBACA,wCACA,yCACA,0CACA,4CAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBC7TJ,SACI,cACA,kBACA,sGACA,qCACA,uDAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,6BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,iBACI,aAGJ,iDACI,cC9EJ,QACI,cACA,aACA,gBAEA,UACI,qBACA,sBAIR,wEACI,aAGJ,6EACI,qCACA,iBAGJ,0BACI,iBAGJ,mBACI,qBACA,wBAGJ,oBACI,qBACA,kBACA,UACA,WACA,wBACA,wBAGJ,4BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,sCACA,kBAGJ,iDACI,gCAGJ,2BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,4CAGJ,sBACI,qBACA,sBAGJ,4BACI,aAGJ,eACI,gBACA,yBACA,kBACA,oBACA,aAEA,4FAII,qBACA,aAIR,uBACI,cACA,aACA,YACA,mBACA,UACA,eACA,gBACA,gBACA,gBACA,YACA,cAEA,0CACI,WACA,YAGJ,gDACI,mBACA,mBACA,WCjHR,SACI,kEAEA,uEACA,cACA,8CAKA,kBAGJ,aACI,iEACA,kBACA,MACA,OACA,WACA,YACA,eACA,8FACA,iBAGJ,kBACI,mBACA,4BACA,eAGJ,8DAEI,eACA,4BAGJ,iBACI,cACA,mBACA,kBACA,kDACA,qHAOA,sBACA,gBACA,kBACA,4EACA,+BACA,mBACA,gBCvDJ,KAGI,uBACA,6BAGJ,iBAEI,yBAGJ,eAEI,yBAGJ,eAEI,yBAGJ,aAEI,yBAGJ,cAEI,yBAGJ,gBAGI,yBAGJ,iBAEI,yBAGJ,gBAEI,yBACA,mGAGJ,gBAEI","file":"ueb-style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-link.scss","../../scss/ueb-type-color.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,0BACA,UAGJ,mBACI,iBACA,gBACA,eAGJ,mBACI,kBACA,gCACA,gBACA,kBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,mDACI,eAGJ,qBAEI,eACA,6CAGJ,gBACI,eAGJ,gBACI,mBAGJ,gBACI,kBAGJ,gBACI,mBAGJ,gBACI,iBAGJ,gBACI,mBAGJ,gBACI,kBAGJ,iBACI,mBAGJ,iBACI,kBAGJ,iBACI,mBAGJ,iBACI,iBACA,uDAGJ,iBACI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBACI,iBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,gEACI,8DACA,8DACA,8DACA,8DACA,oGACA,qGACA,kBACA,wCACA,yCACA,0CACA,4CAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBC7TJ,SACI,cACA,kBACA,sGACA,qCACA,uDAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,6BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,iBACI,aAGJ,iDACI,cC9EJ,QACI,cACA,aACA,gBAEA,UACI,qBACA,sBAIR,wEACI,aAGJ,6EACI,qCACA,iBAGJ,0BACI,iBAGJ,mBACI,qBACA,wBAGJ,oBACI,qBACA,kBACA,UACA,WACA,wBACA,wBAGJ,4BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,sCACA,kBAGJ,iDACI,gCAGJ,2BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,4CAGJ,sBACI,qBACA,sBAGJ,4BACI,aAGJ,eACI,gBACA,yBACA,kBACA,oBACA,aAEA,4FAII,qBACA,aAIR,uBACI,cACA,aACA,YACA,mBACA,UACA,eACA,gBACA,gBACA,gBACA,cACA,YACA,cAEA,0CACI,WACA,YAGJ,gDACI,mBACA,mBACA,WClHR,SACI,kEAEA,uEACA,cACA,8CAKA,kBAGJ,aACI,iEACA,kBACA,MACA,OACA,WACA,YACA,eACA,8FACA,iBAGJ,kBACI,mBACA,4BACA,eAGJ,8DAEI,eACA,4BAGJ,iBACI,cACA,mBACA,kBACA,kDACA,qHAOA,sBACA,gBACA,kBACA,4EACA,+BACA,mBACA,gBCvDJ,KAGI,uBACA,6BAGJ,iBAEI,yBAGJ,eAEI,yBAGJ,eAEI,yBAGJ,aAEI,yBAGJ,cAEI,yBAGJ,gBAGI,yBAGJ,iBAEI,yBAGJ,gBAEI,yBACA,mGAGJ,gBAEI","file":"ueb-style.css"} \ No newline at end of file diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index 7000f32..23eef2f 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -5,27 +5,27 @@ */ const html = String.raw; -// @ts-check - -/** - * @typedef {import("../element/IElement").default} IElement - */ -class ITemplate { - - /** - * @param {IElement} entity - */ - render(entity) { - return "" - } - - /** - * @param {IElement} element - */ - apply(element) { - // TODO replace with the safer element.setHTML(...) when it will be availableBreack - element.innerHTML = this.render(element); - } +// @ts-check + +/** + * @typedef {import("../element/IElement").default} IElement + */ +class ITemplate { + + /** + * @param {IElement} entity + */ + render(entity) { + return "" + } + + /** + * @param {IElement} element + */ + apply(element) { + // TODO replace with the safer element.setHTML(...) when it will be availableBreack + element.innerHTML = this.render(element); + } } // @ts-check @@ -354,71 +354,71 @@ class FastSelectionModel { } } -// @ts-check - -/** - * @typedef {import("../Blueprint").default} Blueprint - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../input/IContext").default} IContext - * @typedef {import("../template/ITemplate").default} ITemplate - */ - -class IElement extends HTMLElement { - - static tagName = "" - - /** @type {Blueprint} */ - blueprint - - /** @type {IEntity} */ - entity - - /** @type {ITemplate} */ - template - - /** @type {IContext[]} */ - inputObjects = [] - - constructor(entity, template) { - super(); - this.blueprint = null; - this.entity = entity; - this.template = template; - this.inputObjects = []; - } - - getTemplate() { - return this.template - } - - connectedCallback() { - this.blueprint = this.closest("ueb-blueprint"); - this.template.apply(this); - this.inputObjects = this.createInputObjects(); - } - - disconnectedCallback() { - this.inputObjects.forEach(v => v.unlistenDOMElement()); - } - - /** @param {IElement} element */ - isSameGraph(element) { - return this.blueprint && this.blueprint == element?.blueprint - } - - /** - * @template {IContext} T - * @param {new (...args: any[]) => T} type - * @returns {T} - */ - getInputObject(type) { - return /** @type {T} */ (this.inputObjects.find(object => object.constructor == type)) - } - - // Subclasses will want to override - createInputObjects() { - return [] - } +// @ts-check + +/** + * @typedef {import("../Blueprint").default} Blueprint + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../input/IContext").default} IContext + * @typedef {import("../template/ITemplate").default} ITemplate + */ + +class IElement extends HTMLElement { + + static tagName = "" + + /** @type {Blueprint} */ + blueprint + + /** @type {IEntity} */ + entity + + /** @type {ITemplate} */ + template + + /** @type {IContext[]} */ + inputObjects = [] + + constructor(entity, template) { + super(); + this.blueprint = null; + this.entity = entity; + this.template = template; + this.inputObjects = []; + } + + getTemplate() { + return this.template + } + + connectedCallback() { + this.blueprint = this.closest("ueb-blueprint"); + this.template.apply(this); + this.inputObjects = this.createInputObjects(); + } + + disconnectedCallback() { + this.inputObjects.forEach(v => v.unlistenDOMElement()); + } + + /** @param {IElement} element */ + isSameGraph(element) { + return this.blueprint && this.blueprint == element?.blueprint + } + + /** + * @template {IContext} T + * @param {new (...args: any[]) => T} type + * @returns {T} + */ + getInputObject(type) { + return /** @type {T} */ (this.inputObjects.find(object => object.constructor == type)) + } + + // Subclasses will want to override + createInputObjects() { + return [] + } } // @ts-check @@ -469,43 +469,43 @@ class SelectorTemplate extends ITemplate { } } -// @ts-check - -class SelectorElement extends IElement { - - static tagName = "ueb-selector" - - constructor() { - super({}, new SelectorTemplate()); - this.selectionModel = null; - /** @type {SelectorTemplate} */ - this.template; - } - - /** - * Create a selection rectangle starting from the specified position - * @param {Number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element) - */ - startSelecting(initialPosition) { - this.template.applyStartSelecting(this, initialPosition); - this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction); - } - - /** - * Move selection rectagle to the specified final position. The initial position was specified by startSelecting() - * @param {Number[]} finalPosition - Selection rectangle final position (relative to the .ueb-grid element) - */ - doSelecting(finalPosition) { - this.template.applyDoSelecting(this, finalPosition); - this.selectionModel.selectTo(finalPosition); - } - - finishSelecting() { - this.template.applyFinishSelecting(this); - this.selectionModel = null; - } -} - +// @ts-check + +class SelectorElement extends IElement { + + static tagName = "ueb-selector" + + constructor() { + super({}, new SelectorTemplate()); + this.selectionModel = null; + /** @type {SelectorTemplate} */ + this.template; + } + + /** + * Create a selection rectangle starting from the specified position + * @param {Number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element) + */ + startSelecting(initialPosition) { + this.template.applyStartSelecting(this, initialPosition); + this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction); + } + + /** + * Move selection rectagle to the specified final position. The initial position was specified by startSelecting() + * @param {Number[]} finalPosition - Selection rectangle final position (relative to the .ueb-grid element) + */ + doSelecting(finalPosition) { + this.template.applyDoSelecting(this, finalPosition); + this.selectionModel.selectTo(finalPosition); + } + + finishSelecting() { + this.template.applyFinishSelecting(this); + this.selectionModel = null; + } +} + customElements.define(SelectorElement.tagName, SelectorElement); // @ts-check @@ -658,10 +658,14 @@ class Configuration { deleteNodesKeyboardKey = "Delete" editTextEventName = { begin: "ueb-edit-text-begin", - end: "ueb-edit-text-end" + end: "ueb-edit-text-end", } enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom expandGridSize = 400 + focusEventName = { + begin: "blueprint-focus", + end: "blueprint-unfocus", + } fontSize = "12px" gridAxisLineColor = "black" gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event @@ -693,13 +697,13 @@ class Configuration { selectAllKeyboardKey = "(bCtrl=True,Key=A)" trackingMouseEventName = { begin: "ueb-tracking-mouse-begin", - end: "ueb-tracking-mouse-end" + end: "ueb-tracking-mouse-end", } ModifierKeys = [ "Ctrl", "Shift", "Alt", - "Meta" + "Meta", ] Keys = { /* UE name: JS name */ @@ -794,57 +798,66 @@ class Configuration { } } -// @ts-check - -/** - * @typedef {import("../Blueprint").default} Blueprint - */ -class IContext { - - /** @type {HTMLElement} */ - target - - /** @type {Blueprint} */ - blueprint - - /** @type {Object} */ - options - - constructor(target, blueprint, options) { - this.target = target; - this.blueprint = blueprint; - this.options = options; - let self = this; - this.listenHandler = _ => { - self.listenEvents(); - }; - this.unlistenHandler = _ => { - self.unlistenEvents(); - }; - if (options?.listenOnFocus ?? false) { - this.blueprint.addEventListener("blueprint-focus", this.listenHandler); - this.blueprint.addEventListener("blueprint-unfocus", this.unlistenHandler); - } - if (options?.unlistenOnEditText ?? false) { - this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler); - this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler); - } - } - - unlistenDOMElement() { - this.unlistenEvents(); - this.blueprint.removeEventListener("blueprint-focus", this.listenHandler); - this.blueprint.removeEventListener("blueprint-unfocus", this.unlistenHandler); - this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler); - this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler); - } - - /* Subclasses will probabily override the following methods */ - listenEvents() { - } - - unlistenEvents() { - } +// @ts-check + +/** + * @typedef {import("../Blueprint").default} Blueprint + */ +class IContext { + + /** @type {HTMLElement} */ + #target + get target() { + return this.#target + } + + /** @type {Blueprint} */ + #blueprint + get blueprint() { + return this.#blueprint + } + + /** @type {Object} */ + options + + /** + * @param {HTMLElement} target + * @param {Blueprint} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options) { + this.#target = target; + this.#blueprint = blueprint; + this.options = options; + this.options.listenOnFocus = this.options?.listenOnFocus ?? false; + this.options.unlistenOnTextEdit = this.options?.unlistenOnTextEdit ?? false; + let self = this; + this.listenHandler = _ => self.listenEvents(); + this.unlistenHandler = _ => self.unlistenEvents(); + if (this.options.listenOnFocus) { + this.blueprint.addEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler); + this.blueprint.addEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler); + } + if (options?.unlistenOnTextEdit ?? false) { + this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler); + this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler); + } + } + + unlistenDOMElement() { + this.unlistenEvents(); + this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler); + this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler); + this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler); + this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler); + } + + /* Subclasses will probabily override the following methods */ + listenEvents() { + } + + unlistenEvents() { + } } // @ts-check @@ -996,66 +1009,66 @@ class Utility { } } -// @ts-check - -class IEntity { - - static attributes = {} - - constructor(options = {}) { - /** - * @param {String[]} prefix - * @param {Object} target - * @param {Object} properties - */ - const defineAllAttributes = (prefix, target, properties) => { - let fullKey = prefix.concat(""); - const last = fullKey.length - 1; - for (let property of Object.getOwnPropertyNames(properties)) { - fullKey[last] = property; - // Not instanceof because all objects are instenceof Object, exact match needed - if (properties[property]?.constructor === Object) { - target[property] = {}; - defineAllAttributes(fullKey, target[property], properties[property]); - continue - } - /* - * The value can either be: - * - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World"). - * - TypeInitialization: contains the maximum amount of information about the attribute. - * - A type: the default value will be default constructed object without arguments. - * - A proper value. - */ - const value = Utility.objectGet(options, fullKey); - if (value !== undefined) { - target[property] = value; - continue - } - let defaultValue = properties[property]; - if (defaultValue instanceof TypeInitialization) { - if (!defaultValue.showDefault) { - target[property] = undefined; // to preserve the order - continue - } - defaultValue = defaultValue.value; - } - if (defaultValue instanceof Array) { - target[property] = []; - continue - } - if (defaultValue instanceof Function) { - defaultValue = TypeInitialization.sanitize(new defaultValue()); - } - target[property] = TypeInitialization.sanitize(defaultValue); - } - }; - // @ts-expect-error - defineAllAttributes([], this, this.constructor.attributes); - } - - empty() { - return true - } +// @ts-check + +class IEntity { + + static attributes = {} + + constructor(options = {}) { + /** + * @param {String[]} prefix + * @param {Object} target + * @param {Object} properties + */ + const defineAllAttributes = (prefix, target, properties) => { + let fullKey = prefix.concat(""); + const last = fullKey.length - 1; + for (let property of Object.getOwnPropertyNames(properties)) { + fullKey[last] = property; + // Not instanceof because all objects are instenceof Object, exact match needed + if (properties[property]?.constructor === Object) { + target[property] = {}; + defineAllAttributes(fullKey, target[property], properties[property]); + continue + } + /* + * The value can either be: + * - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World"). + * - TypeInitialization: contains the maximum amount of information about the attribute. + * - A type: the default value will be default constructed object without arguments. + * - A proper value. + */ + const value = Utility.objectGet(options, fullKey); + if (value !== undefined) { + target[property] = value; + continue + } + let defaultValue = properties[property]; + if (defaultValue instanceof TypeInitialization) { + if (!defaultValue.showDefault) { + target[property] = undefined; // to preserve the order + continue + } + defaultValue = defaultValue.value; + } + if (defaultValue instanceof Array) { + target[property] = []; + continue + } + if (defaultValue instanceof Function) { + defaultValue = TypeInitialization.sanitize(new defaultValue()); + } + target[property] = TypeInitialization.sanitize(defaultValue); + } + }; + // @ts-expect-error + defineAllAttributes([], this, this.constructor.attributes); + } + + empty() { + return true + } } // @ts-check @@ -1128,32 +1141,32 @@ class GuidEntity extends IEntity { } } -// @ts-check - -class IdentifierEntity extends IEntity { - - static attributes = { - value: String, - } - - constructor(options = {}) { - // Not instanceof to pick also primitive string - if (options.constructor === String) { - options = { - value: options - }; - } - super(options); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } +// @ts-check + +class IdentifierEntity extends IEntity { + + static attributes = { + value: String, + } + + constructor(options = {}) { + // Not instanceof to pick also primitive string + if (options.constructor === String) { + options = { + value: options + }; + } + super(options); + /** @type {String} */ this.value; + } + + valueOf() { + return this.value + } + + toString() { + return this.value + } } // @ts-check @@ -1186,28 +1199,28 @@ class IntegerEntity extends IEntity { } } -// @ts-check - -class KeyBindingEntity extends IEntity { - - static attributes = { - ActionName: "", - bShift: false, - bCtrl: false, - bAlt: false, - bCmd: false, - Key: IdentifierEntity, - } - - constructor(options = {}) { - super(options); - /** @type {String} */ this.ActionName; - /** @type {Boolean} */ this.bShift; - /** @type {Boolean} */ this.bCtrl; - /** @type {Boolean} */ this.bAlt; - /** @type {Boolean} */ this.bCmd; - /** @type {IdentifierEntity} */ this.Key; - } +// @ts-check + +class KeyBindingEntity extends IEntity { + + static attributes = { + ActionName: "", + bShift: false, + bCtrl: false, + bAlt: false, + bCmd: false, + Key: IdentifierEntity, + } + + constructor(options = {}) { + super(options); + /** @type {String} */ this.ActionName; + /** @type {Boolean} */ this.bShift; + /** @type {Boolean} */ this.bCtrl; + /** @type {Boolean} */ this.bAlt; + /** @type {Boolean} */ this.bCmd; + /** @type {IdentifierEntity} */ this.Key; + } } // @ts-check @@ -1734,84 +1747,84 @@ class SerializerFactory { } } -// @ts-check - -class ISerializer { - - static grammar = Parsimmon.createLanguage(new Grammar()) - - constructor(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) { - this.entityType = entityType; - this.prefix = prefix ?? ""; - this.separator = separator ?? ","; - this.trailingSeparator = trailingSeparator ?? false; - this.attributeValueConjunctionSign = attributeValueConjunctionSign ?? "="; - this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join(".")); - } - - writeValue(value) { - if (value === null) { - return "()" - } - const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v); - // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically) - switch (value?.constructor) { - case Function: - return this.writeValue(value()) - case Boolean: - return Utility.FirstCapital(value.toString()) - case Number: - return value.toString() - case String: - return `"${value}"` - } - if (value instanceof Array) { - return `(${value.map(v => serialize(v) + ",").join("")})` - } - if (value instanceof IEntity) { - return serialize(value) - } - } - - /** - * @param {String[]} key - * @param {Object} object - * @returns {String} - */ - subWrite(key, object) { - let result = ""; - let fullKey = key.concat(""); - const last = fullKey.length - 1; - for (const property of Object.getOwnPropertyNames(object)) { - fullKey[last] = property; - const value = object[property]; - if (object[property]?.constructor === Object) { - // Recursive call when finding an object - result += (result.length ? this.separator : "") - + this.subWrite(fullKey, value); - } else if (value !== undefined && this.showProperty(object, fullKey, value)) { - result += (result.length ? this.separator : "") - + this.prefix - + this.attributeKeyPrinter(fullKey) - + this.attributeValueConjunctionSign - + this.writeValue(value); - } - } - if (this.trailingSeparator && result.length && fullKey.length === 1) { - // append separator at the end if asked and there was printed content - result += this.separator; - } - return result - } - - showProperty(object, attributeKey, attributeValue) { - const attributes = this.entityType.attributes; - const attribute = Utility.objectGet(attributes, attributeKey); - if (attribute instanceof TypeInitialization) { - return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault - } - return true - } +// @ts-check + +class ISerializer { + + static grammar = Parsimmon.createLanguage(new Grammar()) + + constructor(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) { + this.entityType = entityType; + this.prefix = prefix ?? ""; + this.separator = separator ?? ","; + this.trailingSeparator = trailingSeparator ?? false; + this.attributeValueConjunctionSign = attributeValueConjunctionSign ?? "="; + this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join(".")); + } + + writeValue(value) { + if (value === null) { + return "()" + } + const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v); + // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically) + switch (value?.constructor) { + case Function: + return this.writeValue(value()) + case Boolean: + return Utility.FirstCapital(value.toString()) + case Number: + return value.toString() + case String: + return `"${value}"` + } + if (value instanceof Array) { + return `(${value.map(v => serialize(v) + ",").join("")})` + } + if (value instanceof IEntity) { + return serialize(value) + } + } + + /** + * @param {String[]} key + * @param {Object} object + * @returns {String} + */ + subWrite(key, object) { + let result = ""; + let fullKey = key.concat(""); + const last = fullKey.length - 1; + for (const property of Object.getOwnPropertyNames(object)) { + fullKey[last] = property; + const value = object[property]; + if (object[property]?.constructor === Object) { + // Recursive call when finding an object + result += (result.length ? this.separator : "") + + this.subWrite(fullKey, value); + } else if (value !== undefined && this.showProperty(object, fullKey, value)) { + result += (result.length ? this.separator : "") + + this.prefix + + this.attributeKeyPrinter(fullKey) + + this.attributeValueConjunctionSign + + this.writeValue(value); + } + } + if (this.trailingSeparator && result.length && fullKey.length === 1) { + // append separator at the end if asked and there was printed content + result += this.separator; + } + return result + } + + showProperty(object, attributeKey, attributeValue) { + const attributes = this.entityType.attributes; + const attribute = Utility.objectGet(attributes, attributeKey); + if (attribute instanceof TypeInitialization) { + return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault + } + return true + } } // @ts-check @@ -1877,10 +1890,12 @@ End Object\n`; class Copy extends IContext { + /** @type {(e: ClipboardEvent) => void} */ #copyHandler constructor(target, blueprint, options = {}) { options.listenOnFocus = true; + options.unlistenOnTextEdit = true; super(target, blueprint, options); this.serializer = new ObjectSerializer(); let self = this; @@ -1901,96 +1916,96 @@ class Copy extends IContext { } } -// @ts-check - -class IKeyboardShortcut extends IContext { - - /** @type {KeyBindingEntity[]} */ - #activationKeys - - constructor(target, blueprint, options = {}) { - options.listenOnFocus = true; - options.activationKeys ??= []; - if (!(options.activationKeys instanceof Array)) { - options.activationKeys = [options.activationKeys]; - } - options.activationKeys = options.activationKeys.map(v => { - if (v instanceof KeyBindingEntity) { - return v - } - if (v.constructor === String) { - // @ts-expect-error - const parsed = ISerializer.grammar.KeyBinding.parse(v); - if (parsed.status) { - return parsed.value - } - } - throw new Error("Unexpected key value") - }); - - super(target, blueprint, options); - - this.#activationKeys = this.options.activationKeys ?? []; - - const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"; - const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"; - const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"; - - let self = this; - /** @param {KeyboardEvent} e */ - this.keyDownHandler = e => { - if ( - self.#activationKeys.some(keyEntry => - wantsShift(keyEntry) == e.shiftKey - && wantsCtrl(keyEntry) == e.ctrlKey - && wantsAlt(keyEntry) == e.altKey - && this.blueprint.settings.Keys[keyEntry.Key] == e.code - )) { - if (options.consumeEvent) { - e.stopImmediatePropagation(); - } - self.fire(); - document.removeEventListener("keydown", self.keyDownHandler); - document.addEventListener("keyup", self.keyUpHandler); - } - }; - - /** @param {KeyboardEvent} e */ - this.keyUpHandler = e => { - if ( - self.#activationKeys.some(keyEntry => - keyEntry.bShift && e.key == "Shift" - || keyEntry.bCtrl && e.key == "Control" - || keyEntry.bAlt && e.key == "Alt" - || keyEntry.bCmd && e.key == "Meta" - || this.blueprint.settings.Keys[keyEntry.Key] == e.code - )) { - if (options.consumeEvent) { - e.stopImmediatePropagation(); - } - self.unfire(); - document.removeEventListener("keyup", this.keyUpHandler); - document.addEventListener("keydown", this.keyDownHandler); - } - }; - - } - - listenEvents() { - document.addEventListener("keydown", this.keyDownHandler); - } - - unlistenEvents() { - document.removeEventListener("keydown", this.keyDownHandler); - } - - // Subclasses will want to override - - fire() { - } - - unfire() { - } +// @ts-check + +class IKeyboardShortcut extends IContext { + + /** @type {KeyBindingEntity[]} */ + #activationKeys + + constructor(target, blueprint, options = {}) { + options.listenOnFocus = true; + options.activationKeys ??= []; + if (!(options.activationKeys instanceof Array)) { + options.activationKeys = [options.activationKeys]; + } + options.activationKeys = options.activationKeys.map(v => { + if (v instanceof KeyBindingEntity) { + return v + } + if (v.constructor === String) { + // @ts-expect-error + const parsed = ISerializer.grammar.KeyBinding.parse(v); + if (parsed.status) { + return parsed.value + } + } + throw new Error("Unexpected key value") + }); + + super(target, blueprint, options); + + this.#activationKeys = this.options.activationKeys ?? []; + + const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"; + const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"; + const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"; + + let self = this; + /** @param {KeyboardEvent} e */ + this.keyDownHandler = e => { + if ( + self.#activationKeys.some(keyEntry => + wantsShift(keyEntry) == e.shiftKey + && wantsCtrl(keyEntry) == e.ctrlKey + && wantsAlt(keyEntry) == e.altKey + && this.blueprint.settings.Keys[keyEntry.Key] == e.code + )) { + if (options.consumeEvent) { + e.stopImmediatePropagation(); + } + self.fire(); + document.removeEventListener("keydown", self.keyDownHandler); + document.addEventListener("keyup", self.keyUpHandler); + } + }; + + /** @param {KeyboardEvent} e */ + this.keyUpHandler = e => { + if ( + self.#activationKeys.some(keyEntry => + keyEntry.bShift && e.key == "Shift" + || keyEntry.bCtrl && e.key == "Control" + || keyEntry.bAlt && e.key == "Alt" + || keyEntry.bCmd && e.key == "Meta" + || this.blueprint.settings.Keys[keyEntry.Key] == e.code + )) { + if (options.consumeEvent) { + e.stopImmediatePropagation(); + } + self.unfire(); + document.removeEventListener("keyup", this.keyUpHandler); + document.addEventListener("keydown", this.keyDownHandler); + } + }; + + } + + listenEvents() { + document.addEventListener("keydown", this.keyDownHandler); + } + + unlistenEvents() { + document.removeEventListener("keydown", this.keyDownHandler); + } + + // Subclasses will want to override + + fire() { + } + + unfire() { + } } // @ts-check @@ -2015,72 +2030,72 @@ class KeyboardCanc extends IKeyboardShortcut { } } -// @ts-check - -class IPointing extends IContext { - - constructor(target, blueprint, options) { - super(target, blueprint, options); - this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement; - } - - /** - * @param {MouseEvent} mouseEvent - */ - locationFromEvent(mouseEvent) { - return this.blueprint.compensateTranslation( - Utility.convertLocation( - [mouseEvent.clientX, mouseEvent.clientY], - this.movementSpace)) - } +// @ts-check + +class IPointing extends IContext { + + constructor(target, blueprint, options) { + super(target, blueprint, options); + this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement; + } + + /** + * @param {MouseEvent} mouseEvent + */ + locationFromEvent(mouseEvent) { + return this.blueprint.compensateTranslation( + Utility.convertLocation( + [mouseEvent.clientX, mouseEvent.clientY], + this.movementSpace)) + } } -// @ts-check - -class IMouseWheel extends IPointing { - - /** @type {(e: WheelEvent) => void} */ - #mouseWheelHandler - - /** @type {(e: WheelEvent) => void} */ - #mouseParentWheelHandler - - /** - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options) { - options.listenOnFocus = true; - super(target, blueprint, options); - this.looseTarget = options?.looseTarget ?? true; - let self = this; - - this.#mouseWheelHandler = e => { - e.preventDefault(); - const location = self.locationFromEvent(e); - self.wheel(Math.sign(e.deltaY), location); - }; - this.#mouseParentWheelHandler = e => e.preventDefault(); - - if (this.blueprint.focused) { - this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); - } - } - - listenEvents() { - this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); - this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); - } - - unlistenEvents() { - this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); - this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); - } - - /* Subclasses will override the following method */ - wheel(variation, location) { - } +// @ts-check + +class IMouseWheel extends IPointing { + + /** @type {(e: WheelEvent) => void} */ + #mouseWheelHandler + + /** @type {(e: WheelEvent) => void} */ + #mouseParentWheelHandler + + /** + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options) { + options.listenOnFocus = true; + super(target, blueprint, options); + this.looseTarget = options?.looseTarget ?? true; + let self = this; + + this.#mouseWheelHandler = e => { + e.preventDefault(); + const location = self.locationFromEvent(e); + self.wheel(Math.sign(e.deltaY), location); + }; + this.#mouseParentWheelHandler = e => e.preventDefault(); + + if (this.blueprint.focused) { + this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); + } + } + + listenEvents() { + this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); + } + + unlistenEvents() { + this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); + } + + /* Subclasses will override the following method */ + wheel(variation, location) { + } } // @ts-check @@ -2112,34 +2127,34 @@ class Zoom extends IMouseWheel { } } -// @ts-check - -class KeyboardEnableZoom extends IKeyboardShortcut { - - /** @type {Zoom} */ - #zoomInputObject - - /** - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: blueprint.settings.enableZoomIn - }; - super(target, blueprint, options); - } - - fire() { - this.#zoomInputObject = this.blueprint.getInputObject(Zoom); - this.#zoomInputObject.enableZoonIn = true; - } - - unfire() { - this.#zoomInputObject.enableZoonIn = false; - } +// @ts-check + +class KeyboardEnableZoom extends IKeyboardShortcut { + + /** @type {Zoom} */ + #zoomInputObject + + /** + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options = { + ...options, + activationKeys: blueprint.settings.enableZoomIn + }; + super(target, blueprint, options); + } + + fire() { + this.#zoomInputObject = this.blueprint.getInputObject(Zoom); + this.#zoomInputObject.enableZoonIn = true; + } + + unfire() { + this.#zoomInputObject.enableZoonIn = false; + } } // @ts-check @@ -2333,362 +2348,362 @@ class LinkTemplate extends ITemplate { } } -// @ts-check - -/** - * @typedef {import("./PinElement").default} PinElement - * @typedef {import("./LinkMessageElement").default} LinkMessageElement - */ -class LinkElement extends IElement { - - static tagName = "ueb-link" - /** @type {PinElement} */ - #source - /** @type {PinElement} */ - #destination - #nodeDeleteHandler - #nodeDragSourceHandler - #nodeDragDestinatonHandler - sourceLocation = [0, 0] - /** @type {SVGPathElement} */ - pathElement - /** @type {LinkMessageElement} */ - linkMessageElement - originatesFromInput = false - destinationLocation = [0, 0] - - /** - * @param {PinElement} source - * @param {PinElement} destination - */ - constructor(source, destination) { - super({}, new LinkTemplate()); - /** @type {import("../template/LinkTemplate").default} */ - this.template; - const self = this; - this.#nodeDeleteHandler = _ => self.remove(); - this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value); - this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value); - if (source) { - this.setSourcePin(source); - } - if (destination) { - this.setDestinationPin(destination); - } - if (source && destination) { - this.#linkPins(); - } - } - - #linkPins() { - this.#source.linkTo(this.#destination); - this.#destination.linkTo(this.#source); - } - - #unlinkPins() { - if (this.#source && this.#destination) { - this.#source.unlinkFrom(this.#destination); - this.#destination.unlinkFrom(this.#source); - } - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.#unlinkPins(); - } - - /** - * @returns {Number[]} - */ - getSourceLocation() { - return this.sourceLocation - } - - /** - * @param {Number[]} offset - */ - addSourceLocation(offset) { - const location = [ - this.sourceLocation[0] + offset[0], - this.sourceLocation[1] + offset[1] - ]; - this.sourceLocation = location; - this.template.applyFullLocation(this); - } - - /** - * @param {Number[]} location - */ - setSourceLocation(location = null) { - if (location == null) { - location = this.#source.template.getLinkLocation(this.#source); - } - this.sourceLocation = location; - this.template.applySourceLocation(this); - } - - /** - * @returns {Number[]} - */ - getDestinationLocation() { - return this.destinationLocation - } - - /** - * @param {Number[]} offset - */ - addDestinationLocation(offset) { - const location = [ - this.destinationLocation[0] + offset[0], - this.destinationLocation[1] + offset[1] - ]; - this.setDestinationLocation(location); - } - - /** - * @param {Number[]} location - */ - setDestinationLocation(location = null) { - if (location == null) { - location = this.#destination.template.getLinkLocation(this.#destination); - } - this.destinationLocation = location; - this.template.applyFullLocation(this); - } - - /** - * @returns {PinElement} - */ - getSourcePin() { - return this.#source - } - - /** - * @param {PinElement} pin - */ - setSourcePin(pin) { - if (this.#source) { - const settings = this.#source.blueprint.settings; - const nodeElement = this.#source.getNodeElement(); - nodeElement.removeEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.removeEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); - if (this.#destination) { - this.#unlinkPins(); - } - } - this.#source = pin; - if (this.#source) { - const nodeElement = this.#source.getNodeElement(); - const settings = this.#source.blueprint.settings; - this.originatesFromInput = pin.isInput(); - nodeElement.addEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.addEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); - this.setSourceLocation(); - if (this.#destination) { - this.#linkPins(); - } - } - } - - /** - * @returns {PinElement} - */ - getDestinationPin() { - return this.#destination - } - - /** - * @param {PinElement} pin - */ - setDestinationPin(pin) { - if (this.#destination) { - const nodeElement = this.#destination.getNodeElement(); - nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); - if (this.#source) { - this.#unlinkPins(); - } - } - this.#destination = pin; - if (this.#destination) { - const nodeElement = this.#destination.getNodeElement(); - nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); - this.setDestinationLocation(); - if (this.#source) { - this.#linkPins(); - } - } - } - - /** - * @param {LinkMessageElement} linkMessage - */ - setLinkMessage(linkMessage) { - if (linkMessage) { - this.template.applyLinkMessage(this, linkMessage); - } else if (this.linkMessageElement) { - this.linkMessageElement.remove(); - this.linkMessageElement = null; - } - } - - startDragging() { - this.template.applyStartDragging(this); - } - - finishDragging() { - this.template.applyFinishDragging(this); - } -} - +// @ts-check + +/** + * @typedef {import("./PinElement").default} PinElement + * @typedef {import("./LinkMessageElement").default} LinkMessageElement + */ +class LinkElement extends IElement { + + static tagName = "ueb-link" + /** @type {PinElement} */ + #source + /** @type {PinElement} */ + #destination + #nodeDeleteHandler + #nodeDragSourceHandler + #nodeDragDestinatonHandler + sourceLocation = [0, 0] + /** @type {SVGPathElement} */ + pathElement + /** @type {LinkMessageElement} */ + linkMessageElement + originatesFromInput = false + destinationLocation = [0, 0] + + /** + * @param {PinElement} source + * @param {PinElement} destination + */ + constructor(source, destination) { + super({}, new LinkTemplate()); + /** @type {import("../template/LinkTemplate").default} */ + this.template; + const self = this; + this.#nodeDeleteHandler = _ => self.remove(); + this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value); + this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value); + if (source) { + this.setSourcePin(source); + } + if (destination) { + this.setDestinationPin(destination); + } + if (source && destination) { + this.#linkPins(); + } + } + + #linkPins() { + this.#source.linkTo(this.#destination); + this.#destination.linkTo(this.#source); + } + + #unlinkPins() { + if (this.#source && this.#destination) { + this.#source.unlinkFrom(this.#destination); + this.#destination.unlinkFrom(this.#source); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.#unlinkPins(); + } + + /** + * @returns {Number[]} + */ + getSourceLocation() { + return this.sourceLocation + } + + /** + * @param {Number[]} offset + */ + addSourceLocation(offset) { + const location = [ + this.sourceLocation[0] + offset[0], + this.sourceLocation[1] + offset[1] + ]; + this.sourceLocation = location; + this.template.applyFullLocation(this); + } + + /** + * @param {Number[]} location + */ + setSourceLocation(location = null) { + if (location == null) { + location = this.#source.template.getLinkLocation(this.#source); + } + this.sourceLocation = location; + this.template.applySourceLocation(this); + } + + /** + * @returns {Number[]} + */ + getDestinationLocation() { + return this.destinationLocation + } + + /** + * @param {Number[]} offset + */ + addDestinationLocation(offset) { + const location = [ + this.destinationLocation[0] + offset[0], + this.destinationLocation[1] + offset[1] + ]; + this.setDestinationLocation(location); + } + + /** + * @param {Number[]} location + */ + setDestinationLocation(location = null) { + if (location == null) { + location = this.#destination.template.getLinkLocation(this.#destination); + } + this.destinationLocation = location; + this.template.applyFullLocation(this); + } + + /** + * @returns {PinElement} + */ + getSourcePin() { + return this.#source + } + + /** + * @param {PinElement} pin + */ + setSourcePin(pin) { + if (this.#source) { + const settings = this.#source.blueprint.settings; + const nodeElement = this.#source.getNodeElement(); + nodeElement.removeEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); + if (this.#destination) { + this.#unlinkPins(); + } + } + this.#source = pin; + if (this.#source) { + const nodeElement = this.#source.getNodeElement(); + const settings = this.#source.blueprint.settings; + this.originatesFromInput = pin.isInput(); + nodeElement.addEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); + this.setSourceLocation(); + if (this.#destination) { + this.#linkPins(); + } + } + } + + /** + * @returns {PinElement} + */ + getDestinationPin() { + return this.#destination + } + + /** + * @param {PinElement} pin + */ + setDestinationPin(pin) { + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement(); + nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); + if (this.#source) { + this.#unlinkPins(); + } + } + this.#destination = pin; + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement(); + nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); + this.setDestinationLocation(); + if (this.#source) { + this.#linkPins(); + } + } + } + + /** + * @param {LinkMessageElement} linkMessage + */ + setLinkMessage(linkMessage) { + if (linkMessage) { + this.template.applyLinkMessage(this, linkMessage); + } else if (this.linkMessageElement) { + this.linkMessageElement.remove(); + this.linkMessageElement = null; + } + } + + startDragging() { + this.template.applyStartDragging(this); + } + + finishDragging() { + this.template.applyFinishDragging(this); + } +} + customElements.define(LinkElement.tagName, LinkElement); -// @ts-check - -/** - * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses. - */ -class IMouseClickDrag extends IPointing { - - /** @type {(e: MouseEvent) => void} */ - #mouseDownHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseStartedMovingHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseMoveHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseUpHandler - - #trackingMouse = false - - started = false - - constructor(target, blueprint, options) { - super(target, blueprint, options); - this.clickButton = options?.clickButton ?? 0; - this.exitAnyButton = options?.exitAnyButton ?? true; - this.moveEverywhere = options?.moveEverywhere ?? false; - this.looseTarget = options?.looseTarget ?? false; - this.consumeEvent = options?.consumeEvent ?? true; - this.clickedPosition = [0, 0]; - - const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace; - let self = this; - - this.#mouseDownHandler = e => { - this.blueprint.setFocused(true); - switch (e.button) { - case self.clickButton: - // Either doesn't matter or consider the click only when clicking on the parent, not descandants - if (self.looseTarget || e.target == e.currentTarget) { - if (this.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Attach the listeners - movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler); - document.addEventListener("mouseup", self.#mouseUpHandler); - self.clickedPosition = self.locationFromEvent(e); - self.clicked(self.clickedPosition); - } - break - default: - if (!self.exitAnyButton) { - self.#mouseUpHandler(e); - } - break - } - }; - - this.#mouseStartedMovingHandler = e => { - if (this.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Delegate from now on to self.#mouseMoveHandler - movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); - movementListenedElement.addEventListener("mousemove", self.#mouseMoveHandler); - // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false - const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.begin); - self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; - // Do actual actions - self.startDrag(); - self.started = true; - }; - - this.#mouseMoveHandler = e => { - if (this.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - const location = self.locationFromEvent(e); - const movement = [e.movementX, e.movementY]; - self.dragTo(location, movement); - if (self.#trackingMouse) { - self.blueprint.mousePosition = self.locationFromEvent(e); - } - }; - - this.#mouseUpHandler = e => { - if (!self.exitAnyButton || e.button == self.clickButton) { - if (this.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Remove the handlers of "mousemove" and "mouseup" - movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); - movementListenedElement.removeEventListener("mousemove", self.#mouseMoveHandler); - document.removeEventListener("mouseup", self.#mouseUpHandler); - if (self.started) { - self.endDrag(); - } - self.unclicked(); - if (self.#trackingMouse) { - const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end); - this.target.dispatchEvent(dragEvent); - self.#trackingMouse = false; - } - self.started = false; - } - }; - - this.target.addEventListener("mousedown", this.#mouseDownHandler); - if (this.clickButton == 2) { - this.target.addEventListener("contextmenu", e => e.preventDefault()); - } - } - - getEvent(eventName) { - return new CustomEvent(eventName, { - detail: { - tracker: this - }, - bubbles: true, - cancelable: true - }) - } - - unlistenDOMElement() { - super.unlistenDOMElement(); - this.target.removeEventListener("mousedown", this.#mouseDownHandler); - if (this.clickButton == 2) ; - } - - /* Subclasses will override the following methods */ - clicked(location) { - } - - startDrag(location) { - } - - dragTo(location, movement) { - } - - endDrag() { - } - - unclicked(location) { - } +// @ts-check + +/** + * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses. + */ +class IMouseClickDrag extends IPointing { + + /** @type {(e: MouseEvent) => void} */ + #mouseDownHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseStartedMovingHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseMoveHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseUpHandler + + #trackingMouse = false + + started = false + + constructor(target, blueprint, options) { + super(target, blueprint, options); + this.clickButton = options?.clickButton ?? 0; + this.exitAnyButton = options?.exitAnyButton ?? true; + this.moveEverywhere = options?.moveEverywhere ?? false; + this.looseTarget = options?.looseTarget ?? false; + this.consumeEvent = options?.consumeEvent ?? true; + this.clickedPosition = [0, 0]; + + const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace; + let self = this; + + this.#mouseDownHandler = e => { + this.blueprint.setFocused(true); + switch (e.button) { + case self.clickButton: + // Either doesn't matter or consider the click only when clicking on the parent, not descandants + if (self.looseTarget || e.target == e.currentTarget) { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Attach the listeners + movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler); + document.addEventListener("mouseup", self.#mouseUpHandler); + self.clickedPosition = self.locationFromEvent(e); + self.clicked(self.clickedPosition); + } + break + default: + if (!self.exitAnyButton) { + self.#mouseUpHandler(e); + } + break + } + }; + + this.#mouseStartedMovingHandler = e => { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Delegate from now on to self.#mouseMoveHandler + movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); + movementListenedElement.addEventListener("mousemove", self.#mouseMoveHandler); + // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false + const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.begin); + self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; + // Do actual actions + self.startDrag(); + self.started = true; + }; + + this.#mouseMoveHandler = e => { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + const location = self.locationFromEvent(e); + const movement = [e.movementX, e.movementY]; + self.dragTo(location, movement); + if (self.#trackingMouse) { + self.blueprint.mousePosition = self.locationFromEvent(e); + } + }; + + this.#mouseUpHandler = e => { + if (!self.exitAnyButton || e.button == self.clickButton) { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Remove the handlers of "mousemove" and "mouseup" + movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); + movementListenedElement.removeEventListener("mousemove", self.#mouseMoveHandler); + document.removeEventListener("mouseup", self.#mouseUpHandler); + if (self.started) { + self.endDrag(); + } + self.unclicked(); + if (self.#trackingMouse) { + const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end); + this.target.dispatchEvent(dragEvent); + self.#trackingMouse = false; + } + self.started = false; + } + }; + + this.target.addEventListener("mousedown", this.#mouseDownHandler); + if (this.clickButton == 2) { + this.target.addEventListener("contextmenu", e => e.preventDefault()); + } + } + + getEvent(eventName) { + return new CustomEvent(eventName, { + detail: { + tracker: this + }, + bubbles: true, + cancelable: true + }) + } + + unlistenDOMElement() { + super.unlistenDOMElement(); + this.target.removeEventListener("mousedown", this.#mouseDownHandler); + if (this.clickButton == 2) ; + } + + /* Subclasses will override the following methods */ + clicked(location) { + } + + startDrag(location) { + } + + dragTo(location, movement) { + } + + endDrag() { + } + + unclicked(location) { + } } // @ts-check @@ -2831,193 +2846,193 @@ class MouseMoveNodes extends IMouseClickDrag { } } -// @ts-check - -/** - * @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate - * @typedef {import("../entity/IntegerEntity").default} IntegerEntity - */ -class ISelectableDraggableElement extends IElement { - - constructor(...args) { - super(...args); - this.dragObject = null; - this.location = [0, 0]; - this.selected = false; - /** @type {SelectableDraggableTemplate} */ - this.template; - - let self = this; - this.dragHandler = (e) => { - self.addLocation(e.detail.value); - }; - } - - createInputObjects() { - return [ - new MouseMoveNodes(this, this.blueprint, { - looseTarget: true - }), - ] - } - - /** - * @param {Number[]} value - */ - setLocation(value = [0, 0]) { - const d = [value[0] - this.location[0], value[1] - this.location[1]]; - this.location = value; - this.template.applyLocation(this); - if (this.blueprint) { - const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, { - detail: { - value: d - }, - bubbles: false, - cancelable: true - }); - this.dispatchEvent(dragLocalEvent); - } - } - - addLocation(value) { - this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]); - } - - setSelected(value = true) { - if (this.selected == value) { - return - } - this.selected = value; - if (this.selected) { - this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); - } else { - this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); - } - this.template.applySelected(this); - } - - dispatchDragEvent(value) { - if (!this.selected) { - this.blueprint.unselectAll(); - this.setSelected(true); - } - const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, { - detail: { - value: value - }, - bubbles: true, - cancelable: true - }); - this.dispatchEvent(dragEvent); - } - - snapToGrid() { - let snappedLocation = this.blueprint.snapToGrid(this.location); - if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) { - this.setLocation(snappedLocation); - } - } +// @ts-check + +/** + * @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate + * @typedef {import("../entity/IntegerEntity").default} IntegerEntity + */ +class ISelectableDraggableElement extends IElement { + + constructor(...args) { + super(...args); + this.dragObject = null; + this.location = [0, 0]; + this.selected = false; + /** @type {SelectableDraggableTemplate} */ + this.template; + + let self = this; + this.dragHandler = (e) => { + self.addLocation(e.detail.value); + }; + } + + createInputObjects() { + return [ + new MouseMoveNodes(this, this.blueprint, { + looseTarget: true + }), + ] + } + + /** + * @param {Number[]} value + */ + setLocation(value = [0, 0]) { + const d = [value[0] - this.location[0], value[1] - this.location[1]]; + this.location = value; + this.template.applyLocation(this); + if (this.blueprint) { + const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, { + detail: { + value: d + }, + bubbles: false, + cancelable: true + }); + this.dispatchEvent(dragLocalEvent); + } + } + + addLocation(value) { + this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]); + } + + setSelected(value = true) { + if (this.selected == value) { + return + } + this.selected = value; + if (this.selected) { + this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); + } else { + this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); + } + this.template.applySelected(this); + } + + dispatchDragEvent(value) { + if (!this.selected) { + this.blueprint.unselectAll(); + this.setSelected(true); + } + const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, { + detail: { + value: value + }, + bubbles: true, + cancelable: true + }); + this.dispatchEvent(dragEvent); + } + + snapToGrid() { + let snappedLocation = this.blueprint.snapToGrid(this.location); + if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) { + this.setLocation(snappedLocation); + } + } } -// @ts-check - -/** - * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement - */ -class LinkMessageTemplate extends ITemplate { - - /** - * @param {LinkMessageElement} linkMessage - */ - render(linkMessage) { - return html` - - - ` - } - - /** - * Applies the style to the element. - * @param {LinkMessageElement} linkMessage - */ - apply(linkMessage) { - super.apply(linkMessage); - const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( - linkMessage.linkElement.getSourcePin(), - linkMessage.linkElement.getDestinationPin() - ); - linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); - if (linkMessage.linkElement) { - linkMessageSetup(); - } else { - window.customElements.whenDefined(linkMessage.constructor.tagName).then(linkMessage); - } - } - -} - -// @ts-check - -/** - * @typedef {import("./PinElement").default} PinElement - * @typedef {import("./LinkElement").default} LinkElement - * @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval - */ -class LinkMessageElement extends IElement { - - static tagName = "ueb-link-message" - static convertType = _ => new LinkMessageElement( - "ueb-icon-conver-type", - /** @type {LinkRetrieval} */ - (s, d) => `Convert ${s.getType()} to ${d.getType()}.` - ) - static correct = _ => new LinkMessageElement( - "ueb-icon-correct", - /** @type {LinkRetrieval} */ - (s, d) => "" - ) - static directionsIncompatible = _ => new LinkMessageElement( - "ueb-icon-directions-incompatible", - /** @type {LinkRetrieval} */ - (s, d) => "Directions are not compatbile." - ) - static placeNode = _ => new LinkMessageElement( - "ueb-icon-place-node", - /** @type {LinkRetrieval} */ - (s, d) => "Place a new node." - ) - static replaceLink = _ => new LinkMessageElement( - "ueb-icon-replace-link", - /** @type {LinkRetrieval} */ - (s, d) => "Replace existing input connections." - ) - static sameNode = _ => new LinkMessageElement( - "ueb-icon-same-node", - /** @type {LinkRetrieval} */ - (s, d) => "Both are on the same node." - ) - static typesIncompatible = _ => new LinkMessageElement( - "ueb-icon-types-incompatible", - /** @type {LinkRetrieval} */ - (s, d) => `${s.getType()} is not compatible with ${d.getType()}.` - ) - - /** @type {String} */ - icon - /** @type {String} */ - message - /** @type {LinkElement} */ - linkElement - - constructor(icon, message) { - super({}, new LinkMessageTemplate()); - this.icon = icon; - this.message = message; - } - +// @ts-check + +/** + * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement + */ +class LinkMessageTemplate extends ITemplate { + + /** + * @param {LinkMessageElement} linkMessage + */ + render(linkMessage) { + return html` + + + ` + } + + /** + * Applies the style to the element. + * @param {LinkMessageElement} linkMessage + */ + apply(linkMessage) { + super.apply(linkMessage); + const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( + linkMessage.linkElement.getSourcePin(), + linkMessage.linkElement.getDestinationPin() + ); + linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); + if (linkMessage.linkElement) { + linkMessageSetup(); + } else { + window.customElements.whenDefined(linkMessage.constructor.tagName).then(linkMessage); + } + } + } +// @ts-check + +/** + * @typedef {import("./PinElement").default} PinElement + * @typedef {import("./LinkElement").default} LinkElement + * @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval + */ +class LinkMessageElement extends IElement { + + static tagName = "ueb-link-message" + static convertType = _ => new LinkMessageElement( + "ueb-icon-conver-type", + /** @type {LinkRetrieval} */ + (s, d) => `Convert ${s.getType()} to ${d.getType()}.` + ) + static correct = _ => new LinkMessageElement( + "ueb-icon-correct", + /** @type {LinkRetrieval} */ + (s, d) => "" + ) + static directionsIncompatible = _ => new LinkMessageElement( + "ueb-icon-directions-incompatible", + /** @type {LinkRetrieval} */ + (s, d) => "Directions are not compatbile." + ) + static placeNode = _ => new LinkMessageElement( + "ueb-icon-place-node", + /** @type {LinkRetrieval} */ + (s, d) => "Place a new node." + ) + static replaceLink = _ => new LinkMessageElement( + "ueb-icon-replace-link", + /** @type {LinkRetrieval} */ + (s, d) => "Replace existing input connections." + ) + static sameNode = _ => new LinkMessageElement( + "ueb-icon-same-node", + /** @type {LinkRetrieval} */ + (s, d) => "Both are on the same node." + ) + static typesIncompatible = _ => new LinkMessageElement( + "ueb-icon-types-incompatible", + /** @type {LinkRetrieval} */ + (s, d) => `${s.getType()} is not compatible with ${d.getType()}.` + ) + + /** @type {String} */ + icon + /** @type {String} */ + message + /** @type {LinkElement} */ + linkElement + + constructor(icon, message) { + super({}, new LinkMessageTemplate()); + this.icon = icon; + this.message = message; + } + +} + customElements.define(LinkMessageElement.tagName, LinkMessageElement); // @ts-check @@ -3225,217 +3240,189 @@ class PinTemplate extends ITemplate { } } -// @ts-check - -/** - * @typedef {import("../element/PinElement").default} PinElement - */ -class ExecPinTemplate extends PinTemplate { - - /** - * @param {PinElement} pin - */ - renderIcon(pin) { - return html` - - - - ` - } -} - -// @ts-check - -/** - * @typedef {import("../element/PinElement").default} PinElement - */ -class StringPinTemplate extends PinTemplate { - - hasInput() { - return true - } - - /** - * @param {PinElement} pin - */ - renderInput(pin) { - return html` - - - - ` - } +// @ts-check + +/** + * @typedef {import("../element/PinElement").default} PinElement + */ +class ExecPinTemplate extends PinTemplate { + + /** + * @param {PinElement} pin + */ + renderIcon(pin) { + return html` + + + + ` + } } // @ts-check -class KeyboardIgnoreSelectAll extends KeyboardSelectAll { +/** + * @typedef {import("../element/PinElement").default} PinElement + */ +class StringPinTemplate extends PinTemplate { + + hasInput() { + return true + } /** - * @param {HTMLElement} target - * @param {any} blueprint - * @param {Object} options + * @param {PinElement} pin */ - constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: blueprint.settings.selectAllKeyboardKey - }; - super(target, blueprint, options); - } - - fire() { - } - - unfire() { - + renderInput(pin) { + return html` + + + + ` } } -// @ts-check - -/** - * @typedef {import("../entity/GuidEntity").default} GuidEntity - * @typedef {import("../entity/PinEntity").default} PinEntity - * @typedef {import("./NodeElement").default} NodeElement - */ -class PinElement extends IElement { - - static tagName = "ueb-pin" - - static #typeTemplateMap = { - "exec": ExecPinTemplate, - "string": StringPinTemplate, - } - - /** @type {NodeElement} */ - nodeElement - - /** @type {HTMLElement} */ - clickableElement - - /** @type {String} */ - #color - - constructor(entity) { - super( - entity, - new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)() - ); - - /** @type {PinEntity} */ - this.entity; - - /** @type {PinTemplate} */ - this.template; - } - - connectedCallback() { - super.connectedCallback(); - this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color"); - } - - createInputObjects() { - return [ - new MouseCreateLink(this.clickableElement, this.blueprint, { - moveEverywhere: true, - looseTarget: true - }), - new KeyboardIgnoreSelectAll(this, this.blueprint, { - consumeEvent: true - }) - ] - } - - /** @return {GuidEntity} */ - GetPinId() { - return this.entity.PinId - } - - /** @return {String} */ - GetPinIdValue() { - return this.GetPinId().value - } - - /** - * @returns {String} - */ - getPinName() { - return this.entity.PinName - } - - /** - * @returns {String} - */ - getPinDisplayName() { - return this.entity.PinName - } - - isInput() { - return this.entity.isInput() - } - - isOutput() { - return this.entity.isOutput() - } - - isLinked() { - return this.entity.isLinked() - } - - getType() { - return this.entity.getType() - } - - getClickableElement() { - return this.clickableElement - } - - getColor() { - return this.#color - } - - /** - * Returns The exact location where the link originates from or arrives at. - * @returns {Number[]} The location array - */ - getLinkLocation() { - return this.template.getLinkLocation(this) - } - - /** - * @returns {NodeElement} - */ - getNodeElement() { - return this.closest("ueb-node") - } - - getLinks() { - return this.entity.LinkedTo?.map(pinReference => - pinReference - ) ?? [] - } - - /** - * @param {PinElement} targetPinElement - */ - linkTo(targetPinElement) { - this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); - this.template.applyConnected(this); - } - - /** - * @param {PinElement} targetPinElement - */ - unlinkFrom(targetPinElement) { - this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); - this.template.applyConnected(this); - } -} - +// @ts-check + +/** + * @typedef {import("../entity/GuidEntity").default} GuidEntity + * @typedef {import("../entity/PinEntity").default} PinEntity + * @typedef {import("./NodeElement").default} NodeElement + */ +class PinElement extends IElement { + + static tagName = "ueb-pin" + + static #typeTemplateMap = { + "exec": ExecPinTemplate, + "string": StringPinTemplate, + } + + /** @type {NodeElement} */ + nodeElement + + /** @type {HTMLElement} */ + clickableElement + + /** @type {String} */ + #color + + constructor(entity) { + super( + entity, + new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)() + ); + + /** @type {PinEntity} */ + this.entity; + + /** @type {PinTemplate} */ + this.template; + } + + connectedCallback() { + super.connectedCallback(); + this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color"); + } + + createInputObjects() { + return [ + new MouseCreateLink(this.clickableElement, this.blueprint, { + moveEverywhere: true, + looseTarget: true + }) + ] + } + + /** @return {GuidEntity} */ + GetPinId() { + return this.entity.PinId + } + + /** @return {String} */ + GetPinIdValue() { + return this.GetPinId().value + } + + /** + * @returns {String} + */ + getPinName() { + return this.entity.PinName + } + + /** + * @returns {String} + */ + getPinDisplayName() { + return this.entity.PinName + } + + isInput() { + return this.entity.isInput() + } + + isOutput() { + return this.entity.isOutput() + } + + isLinked() { + return this.entity.isLinked() + } + + getType() { + return this.entity.getType() + } + + getClickableElement() { + return this.clickableElement + } + + getColor() { + return this.#color + } + + /** + * Returns The exact location where the link originates from or arrives at. + * @returns {Number[]} The location array + */ + getLinkLocation() { + return this.template.getLinkLocation(this) + } + + /** + * @returns {NodeElement} + */ + getNodeElement() { + return this.closest("ueb-node") + } + + getLinks() { + return this.entity.LinkedTo?.map(pinReference => + pinReference + ) ?? [] + } + + /** + * @param {PinElement} targetPinElement + */ + linkTo(targetPinElement) { + this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); + this.template.applyConnected(this); + } + + /** + * @param {PinElement} targetPinElement + */ + unlinkFrom(targetPinElement) { + this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); + this.template.applyConnected(this); + } +} + customElements.define(PinElement.tagName, PinElement); // @ts-check @@ -3534,83 +3521,85 @@ class NodeTemplate extends SelectableDraggableTemplate { } } -// @ts-check - -class NodeElement extends ISelectableDraggableElement { - - static tagName = "ueb-node" - - /** - * @param {ObjectEntity} entity - */ - constructor(entity) { - super(entity, new NodeTemplate()); - /** @type {ObjectEntity} */ - this.entity; - /** @type {NodeTemplate} */ - this.template; - this.dragLinkObjects = []; - super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value]); - } - - static fromSerializedObject(str) { - let entity = SerializerFactory.getSerializer(ObjectEntity).read(str); - return new NodeElement(entity) - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.dispatchDeleteEvent(); - } - - getNodeName() { - return this.entity.getName() - } - - getPinElements() { - return this.template.getPinElements(this) - } - - /** - * @returns {PinEntity[]} - */ - getPinEntities() { - return this.entity.CustomProperties.filter(v => v instanceof PinEntity) - } - - connectedCallback() { - this.getAttribute("type")?.trim(); - super.connectedCallback(); - } - - setLocation(value = [0, 0]) { - let nodeType = this.entity.NodePosX.constructor; - // @ts-expect-error - this.entity.NodePosX = new nodeType(value[0]); - // @ts-expect-error - this.entity.NodePosY = new nodeType(value[1]); - super.setLocation(value); - } - - dispatchDeleteEvent(value) { - let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, { - bubbles: true, - cancelable: true, - }); - this.dispatchEvent(deleteEvent); - } -} - +// @ts-check + +class NodeElement extends ISelectableDraggableElement { + + static tagName = "ueb-node" + + /** + * @param {ObjectEntity} entity + */ + constructor(entity) { + super(entity, new NodeTemplate()); + /** @type {ObjectEntity} */ + this.entity; + /** @type {NodeTemplate} */ + this.template; + this.dragLinkObjects = []; + super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value]); + } + + static fromSerializedObject(str) { + let entity = SerializerFactory.getSerializer(ObjectEntity).read(str); + return new NodeElement(entity) + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.dispatchDeleteEvent(); + } + + getNodeName() { + return this.entity.getName() + } + + getPinElements() { + return this.template.getPinElements(this) + } + + /** + * @returns {PinEntity[]} + */ + getPinEntities() { + return this.entity.CustomProperties.filter(v => v instanceof PinEntity) + } + + connectedCallback() { + this.getAttribute("type")?.trim(); + super.connectedCallback(); + } + + setLocation(value = [0, 0]) { + let nodeType = this.entity.NodePosX.constructor; + // @ts-expect-error + this.entity.NodePosX = new nodeType(value[0]); + // @ts-expect-error + this.entity.NodePosY = new nodeType(value[1]); + super.setLocation(value); + } + + dispatchDeleteEvent(value) { + let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, { + bubbles: true, + cancelable: true, + }); + this.dispatchEvent(deleteEvent); + } +} + customElements.define(NodeElement.tagName, NodeElement); // @ts-check class Paste extends IContext { + /** @type {(e: ClipboardEvent) => void} */ #pasteHandle constructor(target, blueprint, options = {}) { options.listenOnFocus = true; + options.unlistenOnTextEdit = true; super(target, blueprint, options); this.serializer = new ObjectSerializer(); let self = this; diff --git a/js/Configuration.js b/js/Configuration.js index 4545384..4149eeb 100755 --- a/js/Configuration.js +++ b/js/Configuration.js @@ -4,10 +4,14 @@ export default class Configuration { deleteNodesKeyboardKey = "Delete" editTextEventName = { begin: "ueb-edit-text-begin", - end: "ueb-edit-text-end" + end: "ueb-edit-text-end", } enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom expandGridSize = 400 + focusEventName = { + begin: "blueprint-focus", + end: "blueprint-unfocus", + } fontSize = "12px" gridAxisLineColor = "black" gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event @@ -39,13 +43,13 @@ export default class Configuration { selectAllKeyboardKey = "(bCtrl=True,Key=A)" trackingMouseEventName = { begin: "ueb-tracking-mouse-begin", - end: "ueb-tracking-mouse-end" + end: "ueb-tracking-mouse-end", } ModifierKeys = [ "Ctrl", "Shift", "Alt", - "Meta" + "Meta", ] Keys = { /* UE name: JS name */ diff --git a/js/element/PinElement.js b/js/element/PinElement.js index d7e388c..7bd5214 100644 --- a/js/element/PinElement.js +++ b/js/element/PinElement.js @@ -5,7 +5,6 @@ import MouseCreateLink from "../input/mouse/MouseCreateLink" import PinTemplate from "../template/PinTemplate" import ExecPinTemplate from "../template/ExecPinTemplate" import StringPinTemplate from "../template/StringPinTemplate" -import KeyboardIgnoreSelectAll from "../input/keybaord/KeyboardIgnoreSelectAll" /** * @typedef {import("../entity/GuidEntity").default} GuidEntity @@ -53,9 +52,6 @@ export default class PinElement extends IElement { new MouseCreateLink(this.clickableElement, this.blueprint, { moveEverywhere: true, looseTarget: true - }), - new KeyboardIgnoreSelectAll(this, this.blueprint, { - consumeEvent: true }) ] } diff --git a/js/input/IContext.js b/js/input/IContext.js index 6ff5a55..8e0404a 100644 --- a/js/input/IContext.js +++ b/js/input/IContext.js @@ -6,30 +6,39 @@ export default class IContext { /** @type {HTMLElement} */ - target + #target + get target() { + return this.#target + } /** @type {Blueprint} */ - blueprint + #blueprint + get blueprint() { + return this.#blueprint + } /** @type {Object} */ options + /** + * @param {HTMLElement} target + * @param {Blueprint} blueprint + * @param {Object} options + */ constructor(target, blueprint, options) { - this.target = target - this.blueprint = blueprint + this.#target = target + this.#blueprint = blueprint this.options = options + this.options.listenOnFocus = this.options?.listenOnFocus ?? false + this.options.unlistenOnTextEdit = this.options?.unlistenOnTextEdit ?? false let self = this - this.listenHandler = _ => { - self.listenEvents() + this.listenHandler = _ => self.listenEvents() + this.unlistenHandler = _ => self.unlistenEvents() + if (this.options.listenOnFocus) { + this.blueprint.addEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler) + this.blueprint.addEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler) } - this.unlistenHandler = _ => { - self.unlistenEvents() - } - if (options?.listenOnFocus ?? false) { - this.blueprint.addEventListener("blueprint-focus", this.listenHandler) - this.blueprint.addEventListener("blueprint-unfocus", this.unlistenHandler) - } - if (options?.unlistenOnEditText ?? false) { + if (options?.unlistenOnTextEdit ?? false) { this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler) this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler) } @@ -37,8 +46,8 @@ export default class IContext { unlistenDOMElement() { this.unlistenEvents() - this.blueprint.removeEventListener("blueprint-focus", this.listenHandler) - this.blueprint.removeEventListener("blueprint-unfocus", this.unlistenHandler) + this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler) + this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler) this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler) this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler) } diff --git a/js/input/common/Copy.js b/js/input/common/Copy.js index 56f709e..402a5df 100755 --- a/js/input/common/Copy.js +++ b/js/input/common/Copy.js @@ -5,10 +5,12 @@ import ObjectSerializer from "../../serialization/ObjectSerializer" export default class Copy extends IContext { + /** @type {(e: ClipboardEvent) => void} */ #copyHandler constructor(target, blueprint, options = {}) { options.listenOnFocus = true + options.unlistenOnTextEdit = true super(target, blueprint, options) this.serializer = new ObjectSerializer() let self = this diff --git a/js/input/common/Paste.js b/js/input/common/Paste.js index a54ce3b..c38304b 100755 --- a/js/input/common/Paste.js +++ b/js/input/common/Paste.js @@ -6,10 +6,12 @@ import ObjectSerializer from "../../serialization/ObjectSerializer" export default class Paste extends IContext { + /** @type {(e: ClipboardEvent) => void} */ #pasteHandle constructor(target, blueprint, options = {}) { options.listenOnFocus = true + options.unlistenOnTextEdit = true super(target, blueprint, options) this.serializer = new ObjectSerializer() let self = this diff --git a/js/input/keybaord/KeyboardIgnoreSelectAll.js b/js/input/keybaord/KeyboardIgnoreSelectAll.js deleted file mode 100755 index f873ef2..0000000 --- a/js/input/keybaord/KeyboardIgnoreSelectAll.js +++ /dev/null @@ -1,26 +0,0 @@ -// @ts-check - -import KeyboardSelectAll from "./KeyboardSelectAll" - -export default class KeyboardIgnoreSelectAll extends KeyboardSelectAll { - - /** - * @param {HTMLElement} target - * @param {any} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: blueprint.settings.selectAllKeyboardKey - } - super(target, blueprint, options) - } - - fire() { - } - - unfire() { - - } -} diff --git a/js/template/StringPinTemplate.js b/js/template/StringPinTemplate.js index baf110d..3f5816f 100644 --- a/js/template/StringPinTemplate.js +++ b/js/template/StringPinTemplate.js @@ -20,8 +20,8 @@ export default class StringPinTemplate extends PinTemplate { return html` ` diff --git a/scss/ueb-pin.scss b/scss/ueb-pin.scss index 9299b7c..49ec64b 100644 --- a/scss/ueb-pin.scss +++ b/scss/ueb-pin.scss @@ -100,6 +100,7 @@ ueb-pin.ueb-pin-fill .ueb-pin-icon-value::before { max-width : 400px; max-height: 16em; background: none; + color : inherit; cursor : text; overflow : auto;