finished seats of seatplan editor

This commit is contained in:
2023-05-29 11:22:40 +02:00
parent 84567f14d4
commit 7de7546371
5 changed files with 274 additions and 41 deletions

View File

@@ -10,33 +10,51 @@
<template> <template>
<div id="properties"> <div id="properties">
<h2>Properties</h2> <h2>Properties</h2>
<table> <table v-if="active">
<tr> <tr>
<td>Position X:</td> <td>Position X:</td>
<td><div v-if="!isEditing.x" @dblclick="activateEditing( 'x' )">{{ posSize.x }}px</div> <td>
<input v-else type="number" min="20" v-model="internal.x" @focusout="resubmit( 'x' )"></td> <input type="number" min="20" v-model="internal[ active ].x" @focusout="resubmit()">
</td>
</tr> </tr>
<tr> <tr>
<td>Position Y:</td> <td>Position Y:</td>
<td><div v-if="!isEditing.y" @dblclick="activateEditing( 'y' )">{{ posSize.y }}px</div> <td>
<input v-else type="number" min="20" v-model="internal.y" @focusout="resubmit( 'y' )"></td> <input type="number" min="20" v-model="internal[ active ].y" @focusout="resubmit()">
</td>
</tr> </tr>
<tr> <tr>
<td>Width:</td> <td>Width:</td>
<td><div v-if="!isEditing.w" @dblclick="activateEditing( 'w' )">{{ posSize.w }}px</div> <td>
<input v-else type="number" min="20" v-model="internal.w" @focusout="resubmit( 'w' )"></td> <input type="number" min="20" v-model="internal[ active ].w" @focusout="resubmit()">
</td>
</tr> </tr>
<tr> <tr>
<td>Height:</td> <td>Height:</td>
<td><div v-if="!isEditing.h" @dblclick="activateEditing( 'h' )">{{ posSize.h }}px</div> <td>
<input v-else type="number" min="20" v-model="internal.w" @focusout="resubmit( 'h' )"></td> <input type="number" min="20" v-model="internal[ active ].h" @focusout="resubmit()">
</td>
</tr> </tr>
<tr> <tr>
<td>Origin:</td> <td>Origin:</td>
<td><div v-if="!isEditing.origin" @dblclick="activateEditing( 'origin' )">{{ posSize.origin }}</div> <td>
<input v-else type="number" min="20" v-model="internal.origin" @focusout="resubmit( 'origin' )"></td> <input type="number" min="1" max="4" v-model="internal[ active ].origin" @focusout="resubmit()">
</td>
</tr>
<tr>
<td>Shape:</td>
<td><select min="20" v-model="internal[ active ].shape" @change="resubmit()">
<option value="rectangular">Rectangular</option>
<option value="trapezoid">Trapezoid</option>
<option value="circular">Circular</option>
</select>
</td>
</tr> </tr>
</table> </table>
<div v-else class="no-select">
<b>No Object selected</b><br>
Please select one to view details here.
</div>
</div> </div>
</template> </template>
@@ -44,38 +62,44 @@
export default { export default {
name: 'propertiesSeatplan', name: 'propertiesSeatplan',
props: { props: {
posSize: { draggables: {
type: Object, type: Object,
"default": { 'x': 100, 'y': 100, 'w': 200, 'h': 100, 'origin': 1 } "default": {}
}, },
scaleFactor: { scaleFactor: {
type: Number, type: Number,
"default": 1, "default": 1,
}, },
active: {
type: Number,
"default": 1,
},
}, },
data () { data () {
return { return {
isEditing: { 'w':false }, internal: {},
internal: { 'x': 100, 'y': 100, 'w': 200, 'h': 100 },
} }
}, },
methods: { methods: {
activateEditing ( option ) { loadInternal () {
this.isEditing[ option ] = true; for ( let value in this.draggables ) {
for ( let value in this.posSize ) { this.internal[ value ] = this.draggables[ value ];
this.internal[ value ] = this.posSize[ value ];
} }
}, },
resubmit ( option ) { resubmit () {
console.log( 'ok' ); this.$emit( 'updated', this.internal );
this.isEditing[ option ] = false;
this.$emit( 'updated', this.internal )
} }
}, },
watch: { watch: {
posSize() { draggables ( value ) {
console.log( 'posSize changed' ); this.loadInternal();
},
active ( value ) {
this.loadInternal();
} }
},
created () {
this.loadInternal();
} }
} }
</script> </script>

View File

@@ -9,13 +9,15 @@
<template> <template>
<div id="window"> <div id="window">
<properties class="properties" v-model:posSize="selectedObject" @updated="handleUpdate" :scale-factor="scaleFactor"></properties> <properties class="properties" v-model:draggables="draggables" @updated="handleUpdate" :scale-factor="scaleFactor" :active="active"></properties>
<div class="parent"> <div class="parent">
<div class="content-parent"> <div class="content-parent">
<Vue3DraggableResizable v-for="draggable in draggables" :initW="draggable.w" :initH="draggable.h" v-model:x="draggable.x" v-model:y="draggable.y" v-model:w="draggable.w" v-model:h="draggable.h" <Vue3DraggableResizable v-for="draggable in draggables" :initW="draggable.w" :initH="draggable.h" v-model:x="draggable.x" v-model:y="draggable.y" v-model:w="draggable.w" v-model:h="draggable.h"
v-model:active="draggable.active" :draggable="draggable.draggable" :resizable="draggable.resizable" :parent="true" @activated="activateComponent( draggable.id );" v-model:active="draggable.active" :draggable="draggable.draggable" :resizable="draggable.resizable" :parent="true" @activated="activateComponent( draggable.id );"
@drag-end="saveHistory();" @resize-end="saveHistory();" @contextmenu="( e ) => { e.preventDefault(); }" class="draggable-box"> @drag-end="saveHistory();" @resize-end="saveHistory();" @contextmenu="( e ) => { e.preventDefault(); }" class="draggable-box">
<circularSeatplanComponent :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin"></circularSeatplanComponent> <circularSeatplanComponent v-if="draggable.shape == 'circular' && draggable.kind == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin"></circularSeatplanComponent>
<trapezoidSeatplanComponent v-if="draggable.shape == 'trapezoid' && draggable.kind == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin"></trapezoidSeatplanComponent>
<rectangularSeatplanComponent v-if="draggable.shape == 'rectangular' && draggable.kind == 'seat'" :scale-factor="scaleFactor" :w="draggable.w" :h="draggable.h" :origin="draggable.origin"></rectangularSeatplanComponent>
</Vue3DraggableResizable> </Vue3DraggableResizable>
</div> </div>
</div> </div>
@@ -34,6 +36,8 @@
import Vue3DraggableResizable from 'vue3-draggable-resizable'; import Vue3DraggableResizable from 'vue3-draggable-resizable';
import properties from '@/components/seatplan/editor/properties.vue'; import properties from '@/components/seatplan/editor/properties.vue';
import circularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/circular.vue'; import circularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/circular.vue';
import rectangularSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/rectangular.vue';
import trapezoidSeatplanComponent from '@/components/seatplan/seatplanComponents/seats/trapezoid.vue';
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css'; import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';
export default { export default {
@@ -42,13 +46,14 @@
Vue3DraggableResizable, Vue3DraggableResizable,
properties, properties,
circularSeatplanComponent, circularSeatplanComponent,
rectangularSeatplanComponent,
trapezoidSeatplanComponent,
}, },
data() { data() {
return { return {
active: 0, active: 0,
draggables: { 1: { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': 1, 'origin': 1, 'categories': { 1: 0 } } }, draggables: { 1: { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': 1, 'origin': 1, 'categories': { 1: 0 }, 'shape':'rectangular', 'kind': 'seat' } },
available: { 'redo': false, 'undo': false }, available: { 'redo': false, 'undo': false },
selectedObject: {},
scaleFactor: 1, scaleFactor: 1,
sizePoll: null, sizePoll: null,
prevSize: { 'h': window.innerHeight, 'w': window.innerWidth }, prevSize: { 'h': window.innerHeight, 'w': window.innerWidth },
@@ -139,7 +144,7 @@
for ( let element in this.draggables ) { for ( let element in this.draggables ) {
if ( this.draggables[ element ].active ) { if ( this.draggables[ element ].active ) {
this.activateComponent( element ); this.draggables[ element ].active = false;
} }
} }
}, },
@@ -174,9 +179,7 @@
return returnArray; return returnArray;
}, },
activateComponent ( id ) { activateComponent ( id ) {
console.log( id );
this.active = id; this.active = id;
this.selectedObject = this.draggables[ this.active ];
}, },
saveHistory () { saveHistory () {
let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {}; let history = sessionStorage.getItem( 'seatplan-history' ) ? JSON.parse( sessionStorage.getItem( 'seatplan-history' ) ) : {};
@@ -228,7 +231,7 @@
sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) ); sessionStorage.setItem( 'seatplan', JSON.stringify( this.scaleDown( this.draggables ) ) );
}, },
addNewElement () { addNewElement () {
this.draggables[ Object.keys( this.draggables ).length + 1 ] = { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': Object.keys( this.draggables ).length + 1, 'origin': 1, 'categories': { 1: 0 } }; this.draggables[ Object.keys( this.draggables ).length + 1 ] = { 'x': 100, 'y':100, 'h': 100, 'w': 250, 'active': false, 'draggable': true, 'resizable': true, 'id': Object.keys( this.draggables ).length + 1, 'origin': 1, 'categories': { 1: 0 }, 'shape':'rectangular', 'kind': 'seat' };
this.saveHistory(); this.saveHistory();
}, },
deleteSelected () { deleteSelected () {
@@ -239,7 +242,7 @@
} }
}, },
handleUpdate ( value ) { handleUpdate ( value ) {
this.draggables[ this.active ] = value; this.draggables = value;
this.selectedObject = value; this.selectedObject = value;
this.saveHistory(); this.saveHistory();
} }
@@ -250,7 +253,7 @@
}, },
unmounted() { unmounted() {
clearInterval( this.sizePoll ); clearInterval( this.sizePoll );
} },
} }
</script> </script>
@@ -269,7 +272,6 @@
} }
.draggable-box { .draggable-box {
border: black 1px solid;
cursor: all-scroll; cursor: all-scroll;
} }
@@ -290,6 +292,6 @@
.content-parent { .content-parent {
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
height: 3000px; height: 400%;
} }
</style> </style>

View File

@@ -10,9 +10,7 @@
<template> <template>
<div id="circularSeatplan"> <div id="circularSeatplan">
<div v-for="row in seats"> <div v-for="row in seats">
<div v-for="seat in row"> <span class="material-symbols-outlined seats" v-for="seat in row" :style="seat.style">living</span>
<span class="material-symbols-outlined seats" :style="seat.style">living</span>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -55,7 +53,7 @@ export default {
// w & h are normalised // w & h are normalised
let w = Math.round( this.w / this.scaleFactor ); let w = Math.round( this.w / this.scaleFactor );
let h = Math.round( this.h / this.scaleFactor ); let h = Math.round( this.h / this.scaleFactor );
const size = 37; const size = 33;
let count = Math.min( Math.floor( w / size ), Math.floor( h / size ) ); let count = Math.min( Math.floor( w / size ), Math.floor( h / size ) );
this.seats = {}; this.seats = {};
for ( let row = 0; row < count; row++ ) { for ( let row = 0; row < count; row++ ) {

View File

@@ -0,0 +1,101 @@
<!--
* myevent - properties.vue
*
* Created by Janis Hutz 05/12/2023, Licensed under the GPL V3 License
* https://janishutz.com, development@janishutz.com
*
*
-->
<template>
<div id="rectangularSeatplan">
<div v-for="row in seats" class="rows">
<span class="material-symbols-outlined seats" v-for="seat in row" :style="seat.style">living</span>
</div>
</div>
</template>
<style scoped>
.seats {
position: absolute;
}
</style>
<script>
export default {
name: 'rectangularSeatplanComponent',
props: {
h: {
type: Number,
"default": 100,
},
w: {
type: Number,
"default": 200,
},
scaleFactor: {
type: Number,
"default": 1,
},
origin: {
type: Number,
"default": 1,
},
},
data () {
return {
seats: {},
}
},
methods: {
calculateChairs () {
// Size of seat at scale 1 is 32px
// w & h are normalised
let w = Math.floor( this.w / this.scaleFactor );
let h = Math.floor( this.h / this.scaleFactor );
const size = 33;
this.seats = {};
for ( let row = 0; row < Math.floor( h / size ); row++ ) {
this.seats[ row ] = {};
for ( let n = 0; n < Math.floor( w / size ); n++ ) {
if ( this.origin === 1 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; bottom: ${ row * size * this.scaleFactor }px; left: ${ n * size * this.scaleFactor }px; rotate: ${ this.origin / 4 - 0.25 }turn;` };
} else if ( this.origin === 2 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; bottom: ${ row * size * this.scaleFactor }px; right: ${ n * size * this.scaleFactor }px; rotate: ${ this.origin / 4 - 0.25 }turn;` };
} else if ( this.origin === 3 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; top: ${ row * size * this.scaleFactor }px; right: ${ n * size * this.scaleFactor }px; rotate: ${ this.origin / 4 - 0.25 }turn;` };
} else if ( this.origin === 4 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; top: ${ row * size * this.scaleFactor }px; left: ${ n * size * this.scaleFactor }px; rotate: ${ this.origin / 4 - 0.25 }turn;` };
}
}
}
},
setScaleFactor () {
for ( let row in this.seats ) {
for ( let seat in this.seats[ row ] ) {
let styles = this.seats[ row ][ seat ].style.substring( this.seats[ row ][ seat ].style.indexOf( ';' ) + 1 );
this.seats[ row ][ seat ].style = `font-size: ${this.scaleFactor * 200}%;` + styles;
}
}
}
},
watch: {
scaleFactor() {
this.setScaleFactor();
},
h() {
this.calculateChairs();
},
w() {
this.calculateChairs();
},
origin() {
this.calculateChairs();
}
},
created() {
this.calculateChairs();
}
}
</script>

View File

@@ -0,0 +1,108 @@
<!--
* myevent - properties.vue
*
* Created by Janis Hutz 05/12/2023, Licensed under the GPL V3 License
* https://janishutz.com, development@janishutz.com
*
*
-->
<template>
<div id="trapezoidSeatplan">
<div v-for="row in seats">
<span class="material-symbols-outlined seats" v-for="seat in row" :style="seat.style">living</span>
</div>
</div>
</template>
<style scoped>
.seats {
position: absolute;
}
</style>
<script>
export default {
name: 'trapezoidSeatplanComponent',
props: {
h: {
type: Number,
"default": 100,
},
w: {
type: Number,
"default": 200,
},
scaleFactor: {
type: Number,
"default": 1,
},
origin: {
type: Number,
"default": 1,
},
},
data () {
return {
seats: {},
}
},
methods: {
calculateChairs () {
// Size of seat at scale 1 is 32px
// w & h are normalised
let w = Math.round( this.w / this.scaleFactor );
let h = Math.round( this.h / this.scaleFactor );
const size = 33;
let side = Math.min( w, h ) + 20;
let heightTriangle = Math.floor( Math.sqrt( side ** 2 - ( Math.sqrt( side ** 2 * 2 ) / 2 ) ) )
let sideOffset = size / Math.sqrt( 2 );
let count = Math.floor( heightTriangle / ( sideOffset * 2 ) );
const angle = Math.PI / 4;
this.seats = {};
for ( let row = 0; row < count; row++ ) {
let nn = 2 + ( row - 1 ) * 2;
this.seats[ row ] = {};
for ( let n = 0; n < nn; n++ ) {
let side = n * sideOffset;
if ( this.origin === 1 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; bottom: ${ ( side + 5 ) * this.scaleFactor }px; left: ${ ( row * sideOffset * 2 - side ) * this.scaleFactor }px; rotate: ${ angle }rad` };
} else if ( this.origin === 2 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; bottom: ${ ( side + 5 ) * this.scaleFactor }px; right: ${ ( row * size - side ) * this.scaleFactor }px; rotate: ${ Math.PI * 2 - angle }rad` };
} else if ( this.origin === 3 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; top: ${ ( side + 5 ) * this.scaleFactor }px; right: ${ ( row * size - side ) * this.scaleFactor }px; rotate: ${ angle + Math.PI }rad` };
} else if ( this.origin === 4 ) {
this.seats[ row ][ n ] = { 'style': `font-size: ${this.scaleFactor * 200}%; top: ${ ( side + 5 ) * this.scaleFactor }px; left: ${ ( row * size - side ) * this.scaleFactor }px; rotate: ${ Math.PI - angle }rad` };
}
}
}
},
setScaleFactor () {
for ( let row in this.seats ) {
for ( let seat in this.seats[ row ] ) {
let styles = this.seats[ row ][ seat ].style.substring( this.seats[ row ][ seat ].style.indexOf( ';' ) + 1 );
this.seats[ row ][ seat ].style = `font-size: ${this.scaleFactor * 200}%;` + styles;
}
}
}
},
watch: {
scaleFactor() {
this.setScaleFactor();
},
h() {
this.calculateChairs();
},
w() {
this.calculateChairs();
},
origin() {
this.calculateChairs();
}
},
created() {
this.calculateChairs();
}
}
</script>