diff --git a/dist/css/ueblueprint-style.css b/dist/css/ueblueprint-style.css index cf79568..d9c08c1 100755 --- a/dist/css/ueblueprint-style.css +++ b/dist/css/ueblueprint-style.css @@ -1 +1 @@ -@font-face{font-family:"Roboto";font-style:light;src:url("../font/roboto-light.woff2") format("woff2"),url("../font/roboto-light.woff") format("woff")}@font-face{font-family:"Roboto";font-style:regular;src:url("../font/roboto-regular.woff2") format("woff2"),url("../font/roboto-regular.woff") format("woff")}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{color:#4d4d4db7}.ueb-viewport-body{position:relative;height:30rem;overflow:hidden;scrollbar-width:none}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-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom--1.ueb{--ueb-scale: 0.875}.ueb-zoom--2.ueb{--ueb-scale: 0.75}.ueb-zoom--3.ueb{--ueb-scale: 0.675}.ueb-zoom--4.ueb{--ueb-scale: 0.5;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--5.ueb{--ueb-scale: 0.375;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--6.ueb{--ueb-scale: 0.333333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--7.ueb{--ueb-scale: 0.3;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--8.ueb{--ueb-scale: 0.266666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--9.ueb{--ueb-scale: 0.233333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--10.ueb{--ueb-scale: 0.2;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--11.ueb{--ueb-scale: 0.166666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-zoom--12.ueb{--ueb-scale: 0.133333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}ueb-node{display:block;position:absolute;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 #000,1px 4px 6px 0 rgba(0,0,0,.3);will-change:transform}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{z-index:1}.ueb-selected>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y;outline:3px solid #cc6700;outline-offset:-6px}.ueb-node-content{position:relative;padding:1px;box-shadow:inset 0 0 2px 0 #000;border-radius:var(--ueb-node-radius);background:rgba(0,0,0,.7);overflow:hidden}.ueb-node-header{padding:.2em .7em;box-shadow:inset 0 1px 2px 0 #313631,inset 0 2px 0 0 #92c381;border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);color:silver;font-weight:600;white-space:nowrap}.ueb-node-name{background:radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);margin:-0.1em -1.6em;padding:.1em 1.6em}.ueb-node-body{display:flex;padding:6px 0;color:#fff;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}ueb-pin{display:block;padding:1px 2px}ueb-blueprint[data-drag-scrolling=false] .ueb-grid{cursor:default}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-node ueb-pin:hover{background:var(--ueb-node-value-background);cursor:crosshair}.ueb-node-value-icon{display:inline-block;position:relative;width:.85em;height:.85em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-node-value-icon::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-node-value-color);border-radius:50%}.ueb-node-value-fill::before{background:var(--ueb-node-value-color)}.ueb-node-value-icon::after{content:"";display:block;position:absolute;top:calc(50% - .3em);left:calc(100% + 1px);width:0;height:0;border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid var(--ueb-node-value-color)}.ueb-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-link{--ueb-computed-width-px: calc(var(--ueb-computed-width) * 1px);display:block;min-width:calc(var(--ueb-link-min-width)*1px);border:1px solid red}ueb-link svg{--ueb-output-invert: calc(2 * var(--ueb-from-input) - 1);position:absolute;top:0;left:0;width:100%;height:100%;transform:scaleY(clamp(-1, (var(--ueb-to-y) - var(--ueb-from-y)) * var(--ueb-output-invert), 1));overflow:visible}ueb-link svg path{--ueb-width-below-threshold: clamp( /* min */ 0, /* input */ calc(var(--ueb-width-threshold) - (var(--ueb-to-x) - var(--ueb-from-x))), /* max */ 1);transform:skewX(calc(var(--ueb-width-below-threshold) * var(--ueb-link-skew) * -1rad));transform-origin:0 0;stroke-width:2;transition:stroke-width .5s}ueb-link svg path:hover{stroke-width:5}ueb-link-message{display:block;position:absolute;left:calc(var(--ueb-start-percentage) + 15px);bottom:-42px;border:1px solid #000;padding:4px 8px;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-node-value-dim-color);white-space:nowrap}/*# sourceMappingURL=ueblueprint-style.css.map */ +@font-face{font-family:"Roboto";font-style:light;src:url("../font/roboto-light.woff2") format("woff2"),url("../font/roboto-light.woff") format("woff")}@font-face{font-family:"Roboto";font-style:regular;src:url("../font/roboto-regular.woff2") format("woff2"),url("../font/roboto-regular.woff") format("woff")}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{color:#4d4d4db7}.ueb-viewport-body{position:relative;height:30rem;overflow:hidden;scrollbar-width:none}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-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom--1.ueb{--ueb-scale: 0.875}.ueb-zoom--2.ueb{--ueb-scale: 0.75}.ueb-zoom--3.ueb{--ueb-scale: 0.675}.ueb-zoom--4.ueb{--ueb-scale: 0.5;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--5.ueb{--ueb-scale: 0.375;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--6.ueb{--ueb-scale: 0.333333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--7.ueb{--ueb-scale: 0.3;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--8.ueb{--ueb-scale: 0.266666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--9.ueb{--ueb-scale: 0.233333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--10.ueb{--ueb-scale: 0.2;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--11.ueb{--ueb-scale: 0.166666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-zoom--12.ueb{--ueb-scale: 0.133333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}ueb-node{display:block;position:absolute;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 #000,1px 4px 6px 0 rgba(0,0,0,.3);will-change:transform}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{z-index:1}.ueb-selected>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y;outline:3px solid #cc6700;outline-offset:-6px}.ueb-node-content{position:relative;padding:1px;box-shadow:inset 0 0 2px 0 #000;border-radius:var(--ueb-node-radius);background:rgba(0,0,0,.7);overflow:hidden}.ueb-node-header{padding:.2em .7em;box-shadow:inset 0 1px 2px 0 #313631,inset 0 2px 0 0 #92c381;border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);color:silver;font-weight:600;white-space:nowrap}.ueb-node-name{background:radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);margin:-0.1em -1.6em;padding:.1em 1.6em}.ueb-node-body{display:flex;padding:6px 0;color:#fff;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}ueb-pin{display:block;padding:1px 2px}ueb-blueprint[data-drag-scrolling=false] .ueb-grid{cursor:default}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-node ueb-pin:hover{background:var(--ueb-node-value-background);cursor:crosshair}.ueb-node-value-icon{display:inline-block;position:relative;width:.85em;height:.85em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-node-value-icon::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-node-value-color);border-radius:50%}.ueb-node-value-fill::before{background:var(--ueb-node-value-color)}.ueb-node-value-icon::after{content:"";display:block;position:absolute;top:calc(50% - .3em);left:calc(100% + 1px);width:0;height:0;border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid var(--ueb-node-value-color)}.ueb-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-link{--ueb-computed-width-px: calc(var(--ueb-computed-width) * 1px);display:block;min-width:calc(var(--ueb-link-min-width)*1px);border:1px solid red}ueb-link svg{--ueb-output-invert: calc(2 * var(--ueb-from-input) - 1);position:absolute;top:0;left:0;width:100%;height:100%;transform:scaleY(clamp(-1, (var(--ueb-to-y) - var(--ueb-from-y)) * var(--ueb-output-invert), 1));overflow:visible}ueb-link svg path{stroke:var(--ueb-node-value-color)}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;position:absolute;left:calc(var(--ueb-start-percentage) + 15px);bottom:-42px;border:1px solid #000;padding:4px 8px;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-node-value-dim-color);white-space:nowrap;z-index:1000000}/*# sourceMappingURL=ueblueprint-style.css.map */ diff --git a/dist/css/ueblueprint-style.css.map b/dist/css/ueblueprint-style.css.map index d7496b5..f6bf47c 100755 --- a/dist/css/ueblueprint-style.css.map +++ b/dist/css/ueblueprint-style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-style.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,gBAGJ,mBACI,kBACA,aACA,gBACA,qBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,qBAGI,eACA,6CAGJ,iBAEI,mBAGJ,iBAEI,kBAGJ,iBAEI,mBAGJ,iBAEI,iBACA,uDAGJ,iBAEI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBAEI,iBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,SACI,cACA,kBACA,sGACA,qCACA,uDACA,sBAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,cACI,UAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,0BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,QACI,cACA,gBAGJ,mDACI,eAGJ,sFACI,4CACA,iBAGJ,qBACI,qBACA,kBACA,YACA,aACA,wBACA,wBAGJ,6BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,6CACA,kBAGJ,6BACI,uCAGJ,4BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,mDAGJ,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,mBAGJ,SACI,+DACA,cACA,8CACA,qBAGJ,aACI,yDACA,kBACA,MACA,OACA,WACA,YACA,iGAKA,iBAGJ,kBAEI,oJAOA,uFACA,qBACA,eACA,4BAGJ,wBACI,eAGJ,iBACI,cACA,kBACA,8CACA,aACA,sBACA,gBACA,kBACA,4EACA,sCACA","file":"ueblueprint-style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-style.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,gBAGJ,mBACI,kBACA,aACA,gBACA,qBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,qBAGI,eACA,6CAGJ,iBAEI,mBAGJ,iBAEI,kBAGJ,iBAEI,mBAGJ,iBAEI,iBACA,uDAGJ,iBAEI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBAEI,iBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,SACI,cACA,kBACA,sGACA,qCACA,uDACA,sBAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,cACI,UAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,0BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,QACI,cACA,gBAGJ,mDACI,eAGJ,sFACI,4CACA,iBAGJ,qBACI,qBACA,kBACA,YACA,aACA,wBACA,wBAGJ,6BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,6CACA,kBAGJ,6BACI,uCAGJ,4BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,mDAGJ,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,mBAGJ,SACI,+DACA,cACA,8CACA,qBAGJ,aACI,yDACA,kBACA,MACA,OACA,WACA,YACA,iGAKA,iBAGJ,kBACI,mCAGJ,8DAEI,eACA,4BAGJ,iBACI,cACA,kBACA,8CACA,aACA,sBACA,gBACA,kBACA,4EACA,sCACA,mBACA","file":"ueblueprint-style.css"} \ No newline at end of file diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index c729b0b..125a1a2 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -129,6 +129,30 @@ class Configuration { */ const html = String.raw; +/** + * @typedef {import("../element/IElement").default} IElement + */ +class ITemplate { + + /** + * Computes the html content of the target element. + * @param {IElement} entity Element of the graph + * @returns The result html + */ + render(entity) { + return "" + } + + /** + * Applies the style to the element. + * @param {IElement} element Element of the graph + */ + apply(element) { + // TODO replace with the safer element.setHTML(...) when it will be available + element.innerHTML = this.render(element); + } +} + document.createElement("div"); const tagReplacement = { @@ -494,30 +518,6 @@ class IElement extends HTMLElement { } } -/** - * @typedef {import("../element/IElement").default} IElement - */ -class ITemplate { - - /** - * Computes the html content of the target element. - * @param {IElement} entity Element of the graph - * @returns The result html - */ - render(entity) { - return "" - } - - /** - * Applies the style to the element. - * @param {IElement} element Element of the graph - */ - apply(element) { - // TODO replace with the safer element.setHTML(...) when it will be available - element.innerHTML = this.render(element); - } -} - /** * @typedef {import("../element/SelectorElement").default} SelectorElement */ @@ -1517,6 +1517,126 @@ class Copy extends IContext { } } +let P = Parsimmon; + +class KeyGrammar { + + // Creates a grammar where each alternative is the string from ModifierKey mapped to a number for bit or use + ModifierKey = r => P.alt(...Configuration.ModifierKeys.map((v, i) => P.string(v).map(_ => 1 << i))) + Key = r => P.alt(...Object.keys(Configuration.Keys).map(v => P.string(v))).map(v => Configuration.Keys[v]) + KeyboardShortcut = r => P.alt( + P.seqMap( + P.seqMap(r.ModifierKey, P.optWhitespace, P.string(Configuration.keysSeparator), (v, _, __) => v) + .atLeast(1) + .map(v => v.reduce((acc, cur) => acc | cur)), + P.optWhitespace, + r.Key, + (modifierKeysFlag, _, key) => ({ + key: key, + ctrlKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Ctrl"))), + shiftKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Shift"))), + altKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Alt"))), + metaKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Meta"))) + }) + ), + r.Key.map(v => ({ key: v })) + ) + .trim(P.optWhitespace) +} + +class IKeyboardShortcut extends IContext { + + static keyGrammar = P.createLanguage(new KeyGrammar()) + + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true; + super(target, blueprint, options); + + /** @type {String[]} */ + this.key = this.options.key; + this.ctrlKey = options.ctrlKey ?? false; + this.shiftKey = options.shiftKey ?? false; + this.altKey = options.altKey ?? false; + this.metaKey = options.metaKey ?? false; + + let self = this; + this.keyDownHandler = e => { + if ( + e.code == self.key + && e.ctrlKey === self.ctrlKey + && e.shiftKey === self.shiftKey + && e.altKey === self.altKey + && e.metaKey === self.metaKey + ) { + self.fire(); + e.preventDefault(); + return true + } + return false + }; + } + + /** + * + * @param {String} keyString + * @returns {Object} + */ + static keyOptionsParse(options, keyString) { + options = { + ...options, + ...IKeyboardShortcut.keyGrammar.KeyboardShortcut.parse(keyString).value + }; + return options + } + + listenEvents() { + document.addEventListener("keydown", this.keyDownHandler); + } + + unlistenEvents() { + document.removeEventListener("keydown", this.keyDownHandler); + } + + fire() { + } +} + +class KeyvoardCanc extends IKeyboardShortcut { + + /** + * + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {OBject} options + */ + constructor(target, blueprint, options = {}) { + options = IKeyboardShortcut.keyOptionsParse(options, Configuration.deleteNodesKeyboardKey); + super(target, blueprint, options); + } + + fire() { + this.blueprint.removeGraphElement(...this.blueprint.getNodes(true)); + } +} + +class KeyboardSelectAll extends IKeyboardShortcut { + + /** + * + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options = IKeyboardShortcut.keyOptionsParse(options, Configuration.selectAllKeyboardKey); + super(target, blueprint, options); + } + + fire() { + this.blueprint.selectAll(); + } +} + /** * @typedef {import("../element/LinkElement").default} LinkElement * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement @@ -1537,9 +1657,13 @@ class LinkTemplate extends ITemplate { * @returns The result html */ render(link) { + const uniqueId = crypto.randomUUID(); return html` - + + + + ` } @@ -1549,17 +1673,39 @@ class LinkTemplate extends ITemplate { * @param {LinkElement} link Element of the graph */ apply(link) { - super.apply(link); - link.classList.add("ueb-positioned"); - link.pathElement = link.querySelector("path"); if (link.linkMessageElement) { link.appendChild(link.linkMessageElement); } + super.apply(link); + link.classList.add("ueb-positioned"); + link.pathElement = link.querySelector("path"); + } + + /** + * + * @param {LinkElement} link element + */ + applyStartDragging(link) { + link.blueprint.dataset.creatingLink = true; + const referencePin = link.getSourcePin() ?? link.getDestinationPin(); + if (referencePin) { + link.style.setProperty("--ueb-node-value-color", referencePin.getColor()); + } + link.classList.add("ueb-link-dragging"); + } + + /** + * + * @param {LinkElement} link element + */ + applyFinishDragging(link) { + link.blueprint.dataset.creatingLink = false; + link.classList.remove("ueb-link-dragging"); } /** * Applies the style relative to the source pin location. - * @param {LinkElement} link Link element + * @param {LinkElement} link element */ applySourceLocation(link) { link.style.setProperty("--ueb-from-input", link.originatesFromInput ? "0" : "1"); @@ -1794,146 +1940,18 @@ class LinkElement extends IElement { this.linkMessageElement = null; } } + + startDragging() { + this.template.applyStartDragging(this); + } + + finishDragging() { + this.template.applyFinishDragging(this); + } } customElements.define(LinkElement.tagName, LinkElement); -/** - * @typedef {import("../element/PinElement").default} PinElement - */ -class PinTemplate extends ITemplate { - - /** - * Computes the html content of the pin. - * @param {PinElement} pin html element - * @returns The result html - */ - render(pin) { - if (pin.isInput()) { - return html` - - ${sanitizeText(pin.getPinDisplayName())} - ` - } else { - return html` - ${sanitizeText(pin.getPinDisplayName())} - - ` - } - } - - /** - * Applies the style to the element. - * @param {PinElement} pin element of the graph - */ - apply(pin) { - super.apply(pin); - pin.classList.add( - "ueb-node-" + (pin.isInput() ? "input" : pin.isOutput() ? "output" : "hidden"), "ueb-node-value-" + sanitizeText(pin.getType())); - pin.clickableElement = pin; - } - - /** - * - * @param {PinElement} pin - * @returns - */ - getLinkLocation(pin) { - const rect = pin.querySelector(".ueb-node-value-icon").getBoundingClientRect(); - return pin.blueprint.compensateTranslation(Utility.convertLocation( - [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2], - pin.blueprint.gridElement)) - } -} - -/** - * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement - */ -class LinkMessageTemplate extends ITemplate { - - /** - * Computes the html content of the target element. - * @param {LinkMessageElement} linkMessage attached to link destination - * @returns The result html - */ - render(linkMessage) { - return html` - - - ` - } - - /** - * Applies the style to the element. - * @param {LinkMessageElement} linkMessage element - */ - apply(linkMessage) { - super.apply(linkMessage); - linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); - linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( - linkMessage.linkElement.getSourcePin(), - linkMessage.linkElement.getDestinationPin() - ); - } - -} - -/** - * @typedef {import("./PinElement").default} PinElement - * @typedef {import("./LinkElement").default} LinkElement - * @typedef {(sourcePin: PinElement, sourcePin: 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 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); - class IPointing extends IContext { constructor(target, blueprint, options) { @@ -2091,224 +2109,80 @@ class IMouseClickDrag extends IPointing { } } -/** - * @typedef {import("../../element/PinElement").default} PinElement - * @typedef {import("../../element/LinkElement").default} LinkElement - */ -class MouseCreateLink extends IMouseClickDrag { - - /** @type {NodeListOf} */ - #listenedPins - - /** @type {(e: MouseEvent) => void} */ - #mouseenterHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseleaveHandler - - constructor(target, blueprint, options) { - super(target, blueprint, options); - /** @type {PinElement} */ - this.target; - /** @type {LinkElement} */ - this.link; - /** @type {PinElement} */ - this.enteredPin; - - let self = this; - this.#mouseenterHandler = e => { - if (!self.enteredPin) { - self.enteredPin = e.target; - } - }; - this.#mouseleaveHandler = e => { - if (self.enteredPin == e.target) { - self.enteredPin = null; - } - }; - } +class MouseScrollGraph extends IMouseClickDrag { startDrag() { - this.link = new LinkElement(this.target, null); - this.link.setLinkMessage(LinkMessageElement.placeNode()); - this.blueprint.nodesContainerElement.insertBefore(this.link, this.blueprint.selectorElement.nextElementSibling); - this.#listenedPins = this.blueprint.querySelectorAll(this.target.constructor.tagName); - this.#listenedPins.forEach(pin => { - if (pin != this.target) { - pin.getClickableElement().addEventListener("mouseenter", this.#mouseenterHandler); - pin.getClickableElement().addEventListener("mouseleave", this.#mouseleaveHandler); - } - }); + this.blueprint.template.applyStartDragScrolling(this.blueprint); } dragTo(location, movement) { - this.link.setDestinationLocation(location); + this.blueprint.scrollDelta([-movement[0], -movement[1]]); } endDrag() { - this.#listenedPins.forEach(pin => { - pin.removeEventListener("mouseenter", this.#mouseenterHandler); - pin.removeEventListener("mouseleave", this.#mouseleaveHandler); - }); - if (this.enteredPin && !this.blueprint.getLinks().find( - link => - link.getSourcePin() == this.target && link.getDestinationPin() == this.enteredPin - || link.getSourcePin() == this.enteredPin && link.getDestinationPin() == this.target - )) { - this.link.setDestinationPin(this.enteredPin); - this.link.setLinkMessage(null); - this.blueprint.addGraphElement(this.link); - } else { - this.link.remove(); - } - this.link = null; + this.blueprint.template.applyEndDragScrolling(this.blueprint); } } -class PinElement extends IElement { +class MouseTracking extends IPointing { - static tagName = "ueb-pin" + /** @type {IPointing} */ + #mouseTracker = null - constructor(entity) { - super(entity, new PinTemplate()); - /** @type {import("../entity/PinEntity").default} */ - this.entity; - /** @type {PinTemplate} */ - this.template; - /** @type {HTMLElement} */ - this.clickableElement = null; + /** @type {(e: MouseEvent) => void} */ + #mousemoveHandler + + /** @type {(e: CustomEvent) => void} */ + #trackingMouseStolenHandler + + /** @type {(e: CustomEvent) => void} */ + #trackingMouseGaveBackHandler + + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true; + super(target, blueprint, options); + + let self = this; + + this.#mousemoveHandler = e => { + self.blueprint.entity.mousePosition = self.locationFromEvent(e); + }; + + this.#trackingMouseStolenHandler = e => { + if (!self.#mouseTracker) { + e.preventDefault(); + this.#mouseTracker = e.detail.tracker; + self.unlistenMouseMove(); + } + }; + + this.#trackingMouseGaveBackHandler = e => { + if (self.#mouseTracker == e.detail.tracker) { + e.preventDefault(); + self.#mouseTracker = null; + self.listenMouseMove(); + } + }; } - createInputObjects() { - return [ - new MouseCreateLink(this.clickableElement, this.blueprint, { - moveEverywhere: true, - looseTarget: true - }), - ] + listenMouseMove() { + this.target.addEventListener("mousemove", this.#mousemoveHandler); } - /** - * - * @returns {String} - */ - getPinDisplayName() { - return this.entity.PinName + unlistenMouseMove() { + this.target.removeEventListener("mousemove", this.#mousemoveHandler); } - getAttributes() { - return PinEntity.attributes + listenEvents() { + this.listenMouseMove(); + this.blueprint.addEventListener(Configuration.trackingMouseEventName.begin, this.#trackingMouseStolenHandler); + this.blueprint.addEventListener(Configuration.trackingMouseEventName.end, this.#trackingMouseGaveBackHandler); } - isInput() { - return this.entity.isInput() - } - - isOutput() { - return this.entity.isOutput() - } - - isConnected() { - return this.entity.isConnected() - } - - getType() { - return this.entity.getType() - } - - getClickableElement() { - return this.clickableElement - } - - /** - * Returns The exact location where the link originates from or arrives at. - * @returns {Number[]} The location array - */ - getLinkLocation() { - return this.template.getLinkLocation(this) - } - - getNodeElement() { - return this.closest("ueb-node") - } -} - -customElements.define(PinElement.tagName, PinElement); - -/** - * @typedef {import("../element/ISelectableDraggableElement").default} ISelectableDraggableElement - */ -class SelectableDraggableTemplate extends ITemplate { - - /** - * Returns the html elements rendered from this template. - * @param {ISelectableDraggableElement} element Element of the graph - */ - applyLocation(element) { - element.style.setProperty("--ueb-position-x", sanitizeText(element.location[0])); - element.style.setProperty("--ueb-position-y", sanitizeText(element.location[1])); - } - - /** - * Returns the html elements rendered from this template. - * @param {ISelectableDraggableElement} element Element of the graph - */ - applySelected(element) { - if (element.selected) { - element.classList.add("ueb-selected"); - } else { - element.classList.remove("ueb-selected"); - } - } -} - -/** - * @typedef {import("../element/NodeElement").default} NodeElement - */ -class NodeTemplate extends SelectableDraggableTemplate { - - /** - * Computes the html content of the target element. - * @param {NodeElement} node Graph node element - * @returns The result html - */ - render(node) { - return html` -
-
-
- - - ${sanitizeText(node.entity.getNodeDisplayName())} - -
-
-
-
-
-
-
- ` - } - - /** - * Applies the style to the element. - * @param {NodeElement} node Element of the graph - */ - apply(node) { - super.apply(node); - if (node.selected) { - node.classList.add("ueb-selected"); - } - node.style.setProperty("--ueb-position-x", sanitizeText(node.location[0])); - node.style.setProperty("--ueb-position-y", sanitizeText(node.location[1])); - /** @type {HTMLElement} */ - let inputContainer = node.querySelector(".ueb-node-inputs"); - /** @type {HTMLElement} */ - let outputContainer = node.querySelector(".ueb-node-outputs"); - let pins = node.getPinEntities(); - pins.filter(v => v.isInput()).forEach(v => inputContainer.appendChild(new PinElement(v))); - pins.filter(v => v.isOutput()).forEach(v => outputContainer.appendChild(new PinElement(v))); + unlistenEvents() { + this.unlistenMouseMove(); + this.blueprint.removeEventListener(Configuration.trackingMouseEventName.begin, this.#trackingMouseStolenHandler); + this.blueprint.removeEventListener(Configuration.trackingMouseEventName.end, this.#trackingMouseGaveBackHandler); } } @@ -2440,6 +2314,379 @@ class ISelectableDraggableElement extends IElement { } } +/** + * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement + */ +class LinkMessageTemplate extends ITemplate { + + /** + * Computes the html content of the target element. + * @param {LinkMessageElement} linkMessage attached to link destination + * @returns The result html + */ + render(linkMessage) { + return html` + + + ` + } + + /** + * Applies the style to the element. + * @param {LinkMessageElement} linkMessage element + */ + apply(linkMessage) { + super.apply(linkMessage); + linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); + linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( + linkMessage.linkElement.getSourcePin(), + linkMessage.linkElement.getDestinationPin() + ); + } + +} + +/** + * @typedef {import("./PinElement").default} PinElement + * @typedef {import("./LinkElement").default} LinkElement + * @typedef {(sourcePin: PinElement, sourcePin: 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 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); + +/** + * @typedef {import("../../element/LinkElement").default} LinkElement + * @typedef {import("../../element/PinElement").default} PinElement + */ +class MouseCreateLink extends IMouseClickDrag { + + /** @type {NodeListOf} */ + #listenedPins + + /** @type {(e: MouseEvent) => void} */ + #mouseenterHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseleaveHandler + + constructor(target, blueprint, options) { + super(target, blueprint, options); + /** @type {PinElement} */ + this.target; + /** @type {LinkElement} */ + this.link; + /** @type {PinElement} */ + this.enteredPin; + + let self = this; + this.#mouseenterHandler = e => { + if (!self.enteredPin) { + self.enteredPin = e.target; + } + }; + this.#mouseleaveHandler = e => { + if (self.enteredPin == e.target) { + self.enteredPin = null; + } + }; + } + + startDrag() { + this.link = new LinkElement(this.target, null); + this.link.setLinkMessage(LinkMessageElement.placeNode()); + this.blueprint.nodesContainerElement.prepend(this.link); + this.#listenedPins = this.blueprint.querySelectorAll(this.target.constructor.tagName); + this.#listenedPins.forEach(pin => { + if (pin != this.target) { + pin.getClickableElement().addEventListener("mouseenter", this.#mouseenterHandler); + pin.getClickableElement().addEventListener("mouseleave", this.#mouseleaveHandler); + } + }); + this.link.startDragging(); + } + + dragTo(location, movement) { + this.link.setDestinationLocation(location); + } + + endDrag() { + this.#listenedPins.forEach(pin => { + pin.removeEventListener("mouseenter", this.#mouseenterHandler); + pin.removeEventListener("mouseleave", this.#mouseleaveHandler); + }); + if (this.enteredPin && !this.blueprint.getLinks().find( + link => + link.getSourcePin() == this.target && link.getDestinationPin() == this.enteredPin + || link.getSourcePin() == this.enteredPin && link.getDestinationPin() == this.target + )) { + this.blueprint.addGraphElement(this.link); + this.link.setDestinationPin(this.enteredPin); + this.link.setLinkMessage(null); + this.link.finishDragging(); + } else { + this.link.finishDragging(); + this.link.remove(); + } + this.link = null; + } +} + +/** + * @typedef {import("../element/PinElement").default} PinElement + */ +class PinTemplate extends ITemplate { + + /** + * Computes the html content of the pin. + * @param {PinElement} pin html element + * @returns The result html + */ + render(pin) { + if (pin.isInput()) { + return html` + + ${sanitizeText(pin.getPinDisplayName())} + ` + } else { + return html` + ${sanitizeText(pin.getPinDisplayName())} + + ` + } + } + + /** + * Applies the style to the element. + * @param {PinElement} pin element of the graph + */ + apply(pin) { + super.apply(pin); + pin.classList.add( + "ueb-node-" + (pin.isInput() ? "input" : pin.isOutput() ? "output" : "hidden"), "ueb-node-value-" + sanitizeText(pin.getType())); + pin.clickableElement = pin; + } + + /** + * + * @param {PinElement} pin + * @returns + */ + getLinkLocation(pin) { + const rect = pin.querySelector(".ueb-node-value-icon").getBoundingClientRect(); + return pin.blueprint.compensateTranslation(Utility.convertLocation( + [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2], + pin.blueprint.gridElement)) + } +} + +class PinElement extends IElement { + + static tagName = "ueb-pin" + + /** @type {HTMLElement} */ + clickableElement + + /** @type {String} */ + #color + + constructor(entity) { + super(entity, new PinTemplate()); + /** @type {import("../entity/PinEntity").default} */ + this.entity; + /** @type {PinTemplate} */ + this.template; + } + + connectedCallback() { + super.connectedCallback(); + this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-node-value-color"); + } + + createInputObjects() { + return [ + new MouseCreateLink(this.clickableElement, this.blueprint, { + moveEverywhere: true, + looseTarget: true + }), + ] + } + + /** + * + * @returns {String} + */ + getPinDisplayName() { + return this.entity.PinName + } + + getAttributes() { + return PinEntity.attributes + } + + isInput() { + return this.entity.isInput() + } + + isOutput() { + return this.entity.isOutput() + } + + isConnected() { + return this.entity.isConnected() + } + + 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) + } + + getNodeElement() { + return this.closest("ueb-node") + } +} + +customElements.define(PinElement.tagName, PinElement); + +/** + * @typedef {import("../element/ISelectableDraggableElement").default} ISelectableDraggableElement + */ +class SelectableDraggableTemplate extends ITemplate { + + /** + * Returns the html elements rendered from this template. + * @param {ISelectableDraggableElement} element Element of the graph + */ + applyLocation(element) { + element.style.setProperty("--ueb-position-x", sanitizeText(element.location[0])); + element.style.setProperty("--ueb-position-y", sanitizeText(element.location[1])); + } + + /** + * Returns the html elements rendered from this template. + * @param {ISelectableDraggableElement} element Element of the graph + */ + applySelected(element) { + if (element.selected) { + element.classList.add("ueb-selected"); + } else { + element.classList.remove("ueb-selected"); + } + } +} + +/** + * @typedef {import("../element/NodeElement").default} NodeElement + */ +class NodeTemplate extends SelectableDraggableTemplate { + + /** + * Computes the html content of the target element. + * @param {NodeElement} node Graph node element + * @returns The result html + */ + render(node) { + return html` +
+
+
+ + + ${sanitizeText(node.entity.getNodeDisplayName())} + +
+
+
+
+
+
+
+ ` + } + + /** + * Applies the style to the element. + * @param {NodeElement} node Element of the graph + */ + apply(node) { + super.apply(node); + if (node.selected) { + node.classList.add("ueb-selected"); + } + node.style.setProperty("--ueb-position-x", sanitizeText(node.location[0])); + node.style.setProperty("--ueb-position-y", sanitizeText(node.location[1])); + /** @type {HTMLElement} */ + let inputContainer = node.querySelector(".ueb-node-inputs"); + /** @type {HTMLElement} */ + let outputContainer = node.querySelector(".ueb-node-outputs"); + let pins = node.getPinEntities(); + pins.filter(v => v.isInput()).forEach(v => inputContainer.appendChild(new PinElement(v))); + pins.filter(v => v.isOutput()).forEach(v => outputContainer.appendChild(new PinElement(v))); + } +} + class NodeElement extends ISelectableDraggableElement { static tagName = "ueb-node" @@ -2497,203 +2744,6 @@ class NodeElement extends ISelectableDraggableElement { customElements.define(NodeElement.tagName, NodeElement); -let P = Parsimmon; - -class KeyGrammar { - - // Creates a grammar where each alternative is the string from ModifierKey mapped to a number for bit or use - ModifierKey = r => P.alt(...Configuration.ModifierKeys.map((v, i) => P.string(v).map(_ => 1 << i))) - Key = r => P.alt(...Object.keys(Configuration.Keys).map(v => P.string(v))).map(v => Configuration.Keys[v]) - KeyboardShortcut = r => P.alt( - P.seqMap( - P.seqMap(r.ModifierKey, P.optWhitespace, P.string(Configuration.keysSeparator), (v, _, __) => v) - .atLeast(1) - .map(v => v.reduce((acc, cur) => acc | cur)), - P.optWhitespace, - r.Key, - (modifierKeysFlag, _, key) => ({ - key: key, - ctrlKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Ctrl"))), - shiftKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Shift"))), - altKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Alt"))), - metaKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Meta"))) - }) - ), - r.Key.map(v => ({ key: v })) - ) - .trim(P.optWhitespace) -} - -class IKeyboardShortcut extends IContext { - - static keyGrammar = P.createLanguage(new KeyGrammar()) - - constructor(target, blueprint, options = {}) { - options.wantsFocusCallback = true; - super(target, blueprint, options); - - /** @type {String[]} */ - this.key = this.options.key; - this.ctrlKey = options.ctrlKey ?? false; - this.shiftKey = options.shiftKey ?? false; - this.altKey = options.altKey ?? false; - this.metaKey = options.metaKey ?? false; - - let self = this; - this.keyDownHandler = e => { - if ( - e.code == self.key - && e.ctrlKey === self.ctrlKey - && e.shiftKey === self.shiftKey - && e.altKey === self.altKey - && e.metaKey === self.metaKey - ) { - self.fire(); - e.preventDefault(); - return true - } - return false - }; - } - - /** - * - * @param {String} keyString - * @returns {Object} - */ - static keyOptionsParse(options, keyString) { - options = { - ...options, - ...IKeyboardShortcut.keyGrammar.KeyboardShortcut.parse(keyString).value - }; - return options - } - - listenEvents() { - document.addEventListener("keydown", this.keyDownHandler); - } - - unlistenEvents() { - document.removeEventListener("keydown", this.keyDownHandler); - } - - fire() { - } -} - -class KeyvoardCanc extends IKeyboardShortcut { - - /** - * - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {OBject} options - */ - constructor(target, blueprint, options = {}) { - options = IKeyboardShortcut.keyOptionsParse(options, Configuration.deleteNodesKeyboardKey); - super(target, blueprint, options); - } - - fire() { - this.blueprint.removeGraphElement(...this.blueprint.getNodes(true)); - } -} - -class KeyboardSelectAll extends IKeyboardShortcut { - - /** - * - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options = IKeyboardShortcut.keyOptionsParse(options, Configuration.selectAllKeyboardKey); - super(target, blueprint, options); - } - - fire() { - this.blueprint.selectAll(); - } -} - -class MouseScrollGraph extends IMouseClickDrag { - - startDrag() { - this.blueprint.template.applyStartDragScrolling(this.blueprint); - } - - dragTo(location, movement) { - this.blueprint.scrollDelta([-movement[0], -movement[1]]); - } - - endDrag() { - this.blueprint.template.applyEndDragScrolling(this.blueprint); - } -} - -class MouseTracking extends IPointing { - - /** @type {IPointing} */ - #mouseTracker = null - - /** @type {(e: MouseEvent) => void} */ - #mousemoveHandler - - /** @type {(e: CustomEvent) => void} */ - #trackingMouseStolenHandler - - /** @type {(e: CustomEvent) => void} */ - #trackingMouseGaveBackHandler - - constructor(target, blueprint, options = {}) { - options.wantsFocusCallback = true; - super(target, blueprint, options); - - let self = this; - - this.#mousemoveHandler = e => { - self.blueprint.entity.mousePosition = self.locationFromEvent(e); - }; - - this.#trackingMouseStolenHandler = e => { - if (!self.#mouseTracker) { - e.preventDefault(); - this.#mouseTracker = e.detail.tracker; - self.unlistenMouseMove(); - } - }; - - this.#trackingMouseGaveBackHandler = e => { - if (self.#mouseTracker == e.detail.tracker) { - e.preventDefault(); - self.#mouseTracker = null; - self.listenMouseMove(); - } - }; - } - - listenMouseMove() { - this.target.addEventListener("mousemove", this.#mousemoveHandler); - } - - unlistenMouseMove() { - this.target.removeEventListener("mousemove", this.#mousemoveHandler); - } - - listenEvents() { - this.listenMouseMove(); - this.blueprint.addEventListener(Configuration.trackingMouseEventName.begin, this.#trackingMouseStolenHandler); - this.blueprint.addEventListener(Configuration.trackingMouseEventName.end, this.#trackingMouseGaveBackHandler); - } - - unlistenEvents() { - this.unlistenMouseMove(); - this.blueprint.removeEventListener(Configuration.trackingMouseEventName.begin, this.#trackingMouseStolenHandler); - this.blueprint.removeEventListener(Configuration.trackingMouseEventName.end, this.#trackingMouseGaveBackHandler); - } -} - class Paste extends IContext { #pasteHandle diff --git a/js/element/LinkElement.js b/js/element/LinkElement.js index f3995c4..b3f18f3 100644 --- a/js/element/LinkElement.js +++ b/js/element/LinkElement.js @@ -175,6 +175,14 @@ export default class LinkElement extends IElement { this.linkMessageElement = null } } + + startDragging() { + this.template.applyStartDragging(this) + } + + finishDragging() { + this.template.applyFinishDragging(this) + } } customElements.define(LinkElement.tagName, LinkElement) diff --git a/js/element/PinElement.js b/js/element/PinElement.js index 13cc437..2951034 100644 --- a/js/element/PinElement.js +++ b/js/element/PinElement.js @@ -6,14 +6,23 @@ export default class PinElement extends IElement { static tagName = "ueb-pin" + /** @type {HTMLElement} */ + clickableElement + + /** @type {String} */ + #color + constructor(entity) { super(entity, new PinTemplate()) /** @type {import("../entity/PinEntity").default} */ this.entity /** @type {PinTemplate} */ this.template - /** @type {HTMLElement} */ - this.clickableElement = null + } + + connectedCallback() { + super.connectedCallback() + this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-node-value-color") } createInputObjects() { @@ -57,6 +66,10 @@ export default class PinElement extends IElement { return this.clickableElement } + getColor() { + return this.#color + } + /** * Returns The exact location where the link originates from or arrives at. * @returns {Number[]} The location array diff --git a/js/input/mouse/MouseCreateLink.js b/js/input/mouse/MouseCreateLink.js index 7cc2090..eed2707 100755 --- a/js/input/mouse/MouseCreateLink.js +++ b/js/input/mouse/MouseCreateLink.js @@ -42,7 +42,7 @@ export default class MouseCreateLink extends IMouseClickDrag { startDrag() { this.link = new LinkElement(this.target, null) this.link.setLinkMessage(LinkMessageElement.placeNode()) - this.blueprint.nodesContainerElement.insertBefore(this.link, this.blueprint.selectorElement.nextElementSibling) + this.blueprint.nodesContainerElement.prepend(this.link) this.#listenedPins = this.blueprint.querySelectorAll(this.target.constructor.tagName) this.#listenedPins.forEach(pin => { if (pin != this.target) { @@ -50,6 +50,7 @@ export default class MouseCreateLink extends IMouseClickDrag { pin.getClickableElement().addEventListener("mouseleave", this.#mouseleaveHandler) } }) + this.link.startDragging() } dragTo(location, movement) { @@ -66,10 +67,12 @@ export default class MouseCreateLink extends IMouseClickDrag { link.getSourcePin() == this.target && link.getDestinationPin() == this.enteredPin || link.getSourcePin() == this.enteredPin && link.getDestinationPin() == this.target )) { + this.blueprint.addGraphElement(this.link) this.link.setDestinationPin(this.enteredPin) this.link.setLinkMessage(null) - this.blueprint.addGraphElement(this.link) + this.link.finishDragging() } else { + this.link.finishDragging() this.link.remove() } this.link = null diff --git a/js/template/LinkMessageTemplate.js b/js/template/LinkMessageTemplate.js index e0406dc..3b74f82 100644 --- a/js/template/LinkMessageTemplate.js +++ b/js/template/LinkMessageTemplate.js @@ -1,6 +1,7 @@ import html from "./html" import ITemplate from "./ITemplate" import LinkElement from "../element/LinkElement" +import sanitizeText from "./sanitizeText" /** * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement @@ -14,7 +15,7 @@ export default class LinkMessageTemplate extends ITemplate { */ render(linkMessage) { return html` - + ` } diff --git a/js/template/LinkTemplate.js b/js/template/LinkTemplate.js index 527ba2b..a8c5515 100755 --- a/js/template/LinkTemplate.js +++ b/js/template/LinkTemplate.js @@ -23,9 +23,13 @@ export default class LinkTemplate extends ITemplate { * @returns The result html */ render(link) { + const uniqueId = crypto.randomUUID() return html` - + + + + ` } @@ -35,17 +39,39 @@ export default class LinkTemplate extends ITemplate { * @param {LinkElement} link Element of the graph */ apply(link) { - super.apply(link) - link.classList.add("ueb-positioned") - link.pathElement = link.querySelector("path") if (link.linkMessageElement) { link.appendChild(link.linkMessageElement) } + super.apply(link) + link.classList.add("ueb-positioned") + link.pathElement = link.querySelector("path") + } + + /** + * + * @param {LinkElement} link element + */ + applyStartDragging(link) { + link.blueprint.dataset.creatingLink = true + const referencePin = link.getSourcePin() ?? link.getDestinationPin() + if (referencePin) { + link.style.setProperty("--ueb-node-value-color", referencePin.getColor()) + } + link.classList.add("ueb-link-dragging") + } + + /** + * + * @param {LinkElement} link element + */ + applyFinishDragging(link) { + link.blueprint.dataset.creatingLink = false + link.classList.remove("ueb-link-dragging") } /** * Applies the style relative to the source pin location. - * @param {LinkElement} link Link element + * @param {LinkElement} link element */ applySourceLocation(link) { link.style.setProperty("--ueb-from-input", link.originatesFromInput ? "0" : "1") @@ -88,8 +114,8 @@ export default class LinkTemplate extends ITemplate { const q = p[1] - a / p[0] return x => a / x + q } - const controlPoint = [500, 140] - c2 = Math.min(c2, getMaxC2(c2Decreasing, controlPoint)(width)) + const controlPointC2 = [500, 140] + c2 = Math.min(c2, getMaxC2(c2Decreasing, controlPointC2)(width)) const d = Configuration.linkRightSVGPath(start, c1, c2) // TODO move to CSS when Firefox will support property d link.pathElement.setAttribute("d", d) diff --git a/scss/ueblueprint-style.scss b/scss/ueblueprint-style.scss index 6f33a88..461c4bf 100644 --- a/scss/ueblueprint-style.scss +++ b/scss/ueblueprint-style.scss @@ -446,22 +446,13 @@ ueb-link svg { } ueb-link svg path { - /* flag stating whether or not the width of the link is below threshold or not */ - --ueb-width-below-threshold: clamp( - /* min */ - 0, - /* input */ - calc(var(--ueb-width-threshold) - (var(--ueb-to-x) - var(--ueb-from-x))), - /* max */ - 1); - transform : skewX(calc(var(--ueb-width-below-threshold) * var(--ueb-link-skew) * -1rad)); - transform-origin: 0 0; - stroke-width : 2; - transition : stroke-width 0.5s; + stroke: var(--ueb-node-value-color); } -ueb-link svg path:hover { +ueb-link.ueb-link-dragging svg path, +ueb-link svg g:hover path { stroke-width: 5; + transition : stroke-width 0.8s; } ueb-link-message { @@ -475,4 +466,5 @@ ueb-link-message { background : linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%); color : var(--ueb-node-value-dim-color); white-space : nowrap; + z-index : 1000000; } \ No newline at end of file