Floorplan configurator

(c) PvH

by PhilQ

HTML

<div id="blockmodules">
	<ul id="blockmodule_categories">
	</ul>
</div>

<div id="plan">
	<div id="plan_head">
		<div class="plan_head"></div>
	</div>
	<div id="plan_blocks">
		<div class="plan_block badkamer1"></div>
		<div class="plan_block eetkamer2"></div>
		<div class="plan_block badkamer3"></div>
	</div>
	<div id="plan_tail">
		<div class="plan_tail"></div>
	</div>
</div>

<div id="drag_module"></div>

SCSS

*, *::before, *::after {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

body {
	background: #eee;
	font-family: sans-serif;
}


.hide {
	display: none !important;
}


#plan {
	position: fixed;
	z-index: 1;
	left: 0px;
	right: 0px;
	top: 35vh;
	height: 30vh;
	background: #fff;
	display: inline-flex;
	justify-content: center;
	
	#plan_head {
		display: inline-flex;
	}
	.plan_head {
		width: 3vh;
		background: #ccc;
	}
	
	#plan_blocks {
		display: inline-flex;
	}
	.plan_block {
		width: 20vh; // or wider
	}
	
	#plan_tail {
		display: inline-flex;
	}
	.plan_tail {
		width: 3vh;
		background: #ccc;
	}
	
	.plan_head, .plan_block, .plan_tail {
		height: 100%;
		position: relative;
		
		&::before {
			content: '';
			display: none;
			position: absolute;
			top: 0px;
			height: 100%;
			width: 40px; // Same as drag_inbetween_size
			background: rgba(255,255,0, 0.3);
		}
		
		&.overwrite::before {
			width: 100%;
			left: 0px;
			display: inline-block;
			z-index: 1;
		}
		&.insertleft::before {
			left: 0px;
			transform: translate(-50%, 0);
			display: inline-block;
			z-index: 1;
		}
		&.insertright::before {
			right: 0px;
			transform: translate(50%, 0);
			display: inline-block;
			z-index: 1;
		}
	}
}


#drag_module {
	position: fixed;
	z-index: 10;
	top: 0px;
	left: 0px;
	width: 15vh;
	height: 15vh;
	transform: translate(-50%, -50%);
	pointer-events: none;
	background: yellow; //TODO: remove
	display: none;
	
	&.show {
		display: inline-block;
	}
}


#blockmodules {
	position: fixed;
	z-index: 2;
	bottom: 0px;
	left: 0px;
	right: 0px;
	height: 15vh;
}

#blockmodule_categories {
	display: flex;
	justify-content: center;
	list-style: none;
	
	> li {
		display: inline-flex;
		width: 15vh;
		height: 15vh;
		text-align: center;
		align-items: center;
		border: 1px solid #ccc;
		
		> h3 {
			text-align: center;
		}
		
		> ul {
			position: absolute;
			bottom: 100%;
			width: 100vw;
			left: 0vw;
			list-style: none;
			display: none;
			justify-content:...

JavaScript

var config = {
	head: 0,
	tail: 0,
	blocks: [ null, null, null ],
};

var blockmodules = {
	heads: [
		{ name: 'Standaard', id: 'kop1' },
	],
	tails: [
		{ name: 'Standaard', id: 'staart1' },
	],
	categories: [],
};

blockmodules.categories['badkamers'] = {
	name: 'Badkamers',
	items: [
		{ name: 'Badkamer 1', id: 'badkamer1' },
		{ name: 'Badkamer 2', id: 'badkamer2' },
		{ name: 'Badkamer 3', id: 'badkamer3' },
	],
};

blockmodules.categories['eetkamers'] = {
	name: 'Eetkamers',
	items: [
		{ name: 'Eetkamer 1', id: 'eetkamer1' },
		{ name: 'Eetkamer 2', id: 'eetkamer2' },
		{ name: 'Eetkamer 3', id: 'eetkamer3' },
	],
};

var is_module_dragging = false,
	drag_target_class = '',
	$drag_module = null,
	$drag_target = null,
	drag_target_bbox = { left: 0, top: 0, right: 0, bottom: 0 },
	drag_inbetween_size = 20,
	mouse_x = 0,
	mouse_y = 0,
	resize_timer = null;

$( document ).ready(function() {
	var $blockmodule_categories = $('#blockmodule_categories');
	$drag_module = $('#drag_module');

	for (var k in blockmodules.categories) {
		var v = blockmodules.categories[ k ];
		var $oLi = $('<li />');
		var $iUl = $('<ul />');
		v.items.forEach(function(vv, ii, aa) {
			var $iLi = $('<li />');
			$iLi
				.append('<b>' + vv.name + '</b>')
				.append('<div id="' + vv.id + '"></div>')
				.data('targetclass', 'block')
				.data('moduleclass', vv.id )
				.appendTo( $iUl )
				.on('mousedown touchstart', function( ev ) {
					ev.preventDefault();
					var $this = $( this );
					
					mouse_x = ev.pageX;
					mouse_y = ev.pageY;
					drag_target_class = 'plan_' + $this.data('targetclass');

					//TODO: clone element + set initial mouse coords

					$( window )
						.on('mouseup.moduledrag touchend.moduledrag', function( ev2 ) {
							$( window ).off('.moduledrag');
							if ( ! is_module_dragging ) {
								console.log('Clicked:', $this);
							} else {
								ev2.preventDefault();
								ev2.stopImmediatePropagation();
								is_module_dragging =...