Vue - Dynamic Dialogs

Vue also bound to SVG elements

by Ben Clayton

HTML

<script type="text/x-template" id="mynode-svg-template">
		<g>
			<circle @click="circleClick" :cx="circleX()" :cy="circleY()" :r="circleRadius()" :stroke="circleStroke()" :fill="circleFill()" stroke-width="1" />
			<text :x="textX()" :y="textY()" fill="black">{{ name() }}</text>
		</g>
	</script>
	
	<!-- Property template -->
	<script type="text/x-template" id="mynode-dialog-template">
		<div class='dialog'>
			<div class="closebtn">X</div>
			<div class="proplist">
				<table>
					<tr>
						<td class='label'><label>Name:</label></td>
						<td class='value'><input v-model='name'></td>
					</tr>
					<tr>
						<td class='label'><label>Size:</label></td>
						<td class='value'><input type='range' v-model='size' min='0' max='100'>{{ size }}</td>
					</tr>
					<tr>
						<td class='label'><label>X:</label></td>
						<td class='value'><input type='range' v-model='x' min='100' max='400'>{{ x }}</td>
					</tr>
					<tr>
						<td class='label'><label>Y:</label></td>
						<td class='value'><input type='range' v-model='y' min='50' max='250'>{{ y }}</td>
					</tr>
					
					<tr>
						<td class='label'><label>Color:</label></td>
						<td class='value'>
							<my-color-selector  :param="$data.getPropByRef('color')"></my-color-selector>
						</td>
					</tr>
				</table>
			</div>
		</div>
	</script>
	
	

	<script type="text/x-template" id="my-color-selector-template">
		<div>
			<select v-model='param.ref[param.prop]'><option v-for='selcolor in selcolors'>{{ selcolor }}</option></select>
		</div>
	</script>

	<!-- SVG Drawing area for Nodes -->
	<div class="svgarea">
		<!-- SVG Elements -->
		<svg width="500" height="300">
			<my-node v-for="node in nodes" :node="node.getSVGViewData()" :click="node.click"></my-node>
		</svg>
	</div>
	
	
	<!-- Property Dialog area -->
	<div class="property-dialogs">
		<div><button @click="addDialog">Add Dialog for {{objarr[dialogs.length % objarr.length].name}}</button></div>
		<div v-for="dialog in dialogs"...

CSS

.dialogouter {
			display: inline-block;
			margin: 20px;
		}
		
		.dialog {
			width: 240px;
			height: 300px;
			display: inline-block;
			border: 1px solid gray;
			
		}
		.dialog .proplist {		
			padding: 10px;
		}
		.dialog table td.label {}
		
		.dialog table td.value {
			min-width: 180px;
		}
		.dialog .closebtn {
			float: right;
			border:1px solid gray;
		}

JavaScript

class myclass{
			constructor( name, color, size, x, y ) {
				this.name = name || "mycircle";
				this.size = size || 20;
				this.color = color || "gray";
				this.x = x || 260;
				this.y = y || 100; 
			}
			
			getPropByRef( nm ){ // used to pass 'color' by ref to color selector component
				return { ref : this, prop: nm };
			}

			getSVGViewData(){
				return { ref:this, x:this.x, y:this.y, size:this.size, name:this.name, color:this.color, click:this.click };
			}

			propertyTemplate(){
				return "#mynode-dialog-template";
			}
			
			// The click is not called with the context of the class
			//  but the parameter 'cls' points at this class.
			click( cls ){
				// 'pd' is a global pointer to the view handling the dialogs array.
				pd.singleDialog(cls);
			}
		
		}

		var objarray = [
				new myclass("C1","red",10,100,100),
				new myclass("C2","blue",20,160,100),
				new myclass("C3")
		];

		// Vue component for driving svg object
		Vue.component('my-node', {
			props: ['node','click'],
			template: '#mynode-svg-template',
			methods: {
				circleClick: function(event){
					this.click( this.node.ref );
				},
				circleFill: function() {
					return this.node.color;
				},
				circleRadius: function(){
					return this.node.size;
				},
				circleX: function(){
					return this.node.x;
				},
				circleY: function(){
					return this.node.y;
				},
				circleStroke: function(){
					return this.node.stroke || "black";
				},
				textX: function(){
					return this.node.x - this.node.size - ( 7 * this.node.name.length ) - 10;
				}, 
				textY: function(){
					return this.node.y;
				},
				name: function(){
					return this.node.name;
				}
			}
		});



		// SVG Area Handler
		vm1 = new Vue({
			el: '.svgarea',
			data: {
				nodes: objarray
			}
		});


		//=============================================================================

		// Dynamic Vue component, which is a bit of magic config, hard to...