2020-12-28 14:31:21 +00:00
|
|
|
<template>
|
2020-12-29 18:56:20 +00:00
|
|
|
<div class="sidebar hideOnPrint">
|
|
|
|
<div class="toolbar">
|
|
|
|
<a :class="{ active: tab === 'settings' }" @click="tab = 'settings'"><font-awesome-icon icon="sliders-h" size="2x" fixed-width /> Settings</a>
|
|
|
|
<a :class="{ active: tab === 'wood' }" @click="tab = 'wood'"><font-awesome-icon icon="tree" size="2x" fixed-width /> Wood</a>
|
|
|
|
<a :class="{ active: tab === 'layers' }" @click="tab = 'layers'"><font-awesome-icon icon="layer-group" size="2x" fixed-width /> Layers</a>
|
|
|
|
<a :class="{ active: tab === 'saveLoad' }" @click="tab = 'saveLoad'"><font-awesome-icon icon="save" size="2x" fixed-width /> Save / load</a>
|
|
|
|
<a :class="{ active: tab === 'print' }" @click="tab = 'print'"><font-awesome-icon icon="print" size="2x" fixed-width /> Print</a>
|
|
|
|
<a :class="{ active: tab === 'about' }" @click="tab = 'about'"><font-awesome-icon icon="info-circle" size="2x" fixed-width /> About</a>
|
2020-12-28 15:11:12 +00:00
|
|
|
</div>
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
<div class="tabs">
|
|
|
|
<div class="tab" v-show="tab === 'settings'">
|
|
|
|
<Settings />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="tab" v-show="tab === 'layers'">
|
|
|
|
<Layers />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="tab" v-show="tab === 'wood'">
|
|
|
|
<Wood />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="tab" v-show="tab === 'print'">
|
|
|
|
<p>
|
|
|
|
Use your browser's built-in print functionality (for example, Ctrl+P on Windows) or click the button below to get a printable version of your board and cutting list.
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<input type="checkbox" v-model="printEndGrain" id="printEndGrain" />
|
|
|
|
<label for="printEndGrain"> End grain preview</label>
|
|
|
|
</div>
|
2020-12-30 22:00:03 +00:00
|
|
|
<div>
|
|
|
|
<input type="checkbox" v-model="printEdgeGrain" id="printEdgeGrain" />
|
|
|
|
<label for="printEdgeGrain"> Edge grain preview</label>
|
|
|
|
</div>
|
2020-12-29 18:56:20 +00:00
|
|
|
<div>
|
|
|
|
<input type="checkbox" v-model="printCuttingList" id="printCuttingList" />
|
|
|
|
<label for="printCuttingList"> Cutting list and bill of materials</label>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<br />
|
|
|
|
<button @click="print">Print</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="tab" v-show="tab === 'saveLoad'">
|
|
|
|
<p>
|
|
|
|
Below you can download the current settings or load them again from a file. You can also bookmark the current page or copy the URL from the address bar instead, as it is automatically updated whenever you change anything.
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="text" v-model="saveFilename" />
|
|
|
|
<button @click="save">Save</button>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="file" ref="loadFile" accept=".json" />
|
|
|
|
<button @click="load">Load</button>
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="tab" v-show="tab === 'about'">
|
|
|
|
<p>
|
|
|
|
Created by Mark van Renswoude. Open-source and available under the Unlicense to the public domain on <a href="https://github.com/MvRens/CuttingBoard" target="_blank">Github</a>, where feedback is welcome under Issues.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
Heavily inspired by <a href="http://www.lastalias.com/cbdesigner/">CBdesigner</a>.
|
|
|
|
</p>
|
|
|
|
</div>
|
2020-12-28 14:31:21 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
<div class="content">
|
|
|
|
<div :class="{ hideOnPrint: !printEndGrain }">
|
|
|
|
<h1>End grain</h1>
|
|
|
|
<EndGrainPreview :scale="1" />
|
|
|
|
</div>
|
2020-12-28 14:31:21 +00:00
|
|
|
|
2020-12-30 22:00:03 +00:00
|
|
|
<div :class="{ hideOnPrint: !printEdgeGrain }">
|
|
|
|
<h1>Edge grain</h1>
|
|
|
|
<template v-for="(board, boardIndex) in boards">
|
|
|
|
<h2 v-if="boards.length > 1">Board {{ boardIndex + 1}}</h2>
|
|
|
|
<EdgeGrainPreview :board="board" :scale="1" />
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
<div :class="{ hideOnPrint: !printCuttingList }">
|
|
|
|
<h1>Cutting list</h1>
|
|
|
|
<CuttingList />
|
|
|
|
</div>
|
2020-12-28 14:31:21 +00:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import Settings from './components/Settings.vue'
|
|
|
|
import Layers from './components/Layers.vue'
|
|
|
|
import Wood from './components/Wood.vue'
|
|
|
|
import EndGrainPreview from './components/EndGrainPreview.vue'
|
|
|
|
import EdgeGrainPreview from './components/EdgeGrainPreview.vue'
|
2020-12-29 18:56:20 +00:00
|
|
|
import CuttingList from './components/CuttingList.vue'
|
2020-12-28 14:31:21 +00:00
|
|
|
|
2020-12-28 15:11:12 +00:00
|
|
|
import { saveAs } from 'file-saver';
|
2020-12-28 21:05:27 +00:00
|
|
|
import { bytesToBase64, base64ToBytes } from './lib/base64';
|
2020-12-28 15:11:12 +00:00
|
|
|
|
2020-12-28 14:31:21 +00:00
|
|
|
export default {
|
|
|
|
name: 'App',
|
|
|
|
components: {
|
|
|
|
EndGrainPreview,
|
|
|
|
EdgeGrainPreview,
|
|
|
|
Settings,
|
|
|
|
Layers,
|
2020-12-29 18:56:20 +00:00
|
|
|
Wood,
|
|
|
|
CuttingList
|
2020-12-28 15:11:12 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
data()
|
|
|
|
{
|
|
|
|
return {
|
2020-12-29 18:56:20 +00:00
|
|
|
tab: 'settings',
|
|
|
|
saveFilename: 'My cutting board',
|
|
|
|
|
|
|
|
printEdgeGrain: true,
|
|
|
|
printEndGrain: true,
|
|
|
|
printCuttingList: true
|
2020-12-28 15:11:12 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-12-28 21:05:27 +00:00
|
|
|
created()
|
|
|
|
{
|
|
|
|
const self = this;
|
|
|
|
const checkHash = () =>
|
|
|
|
{
|
|
|
|
if (!location.hash)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const hash = location.hash.substring(1);
|
|
|
|
try
|
|
|
|
{
|
|
|
|
const decoded = base64ToBytes(hash);
|
|
|
|
if (decoded)
|
|
|
|
self.$store.commit('loadMsgPack', decoded);
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
checkHash();
|
|
|
|
|
|
|
|
window.addEventListener('hashchange', () =>
|
|
|
|
{
|
|
|
|
checkHash();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
computed: {
|
2020-12-30 22:00:03 +00:00
|
|
|
boards() { return this.$store.state.boards; },
|
|
|
|
|
2020-12-28 21:05:27 +00:00
|
|
|
hash()
|
|
|
|
{
|
|
|
|
return bytesToBase64(this.$store.getters.saveMsgPack);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-12-28 15:11:12 +00:00
|
|
|
methods: {
|
|
|
|
save()
|
|
|
|
{
|
|
|
|
const state = this.$store.getters.save;
|
|
|
|
const blob = new Blob([state], { type: 'text/plain; charset=utf-8' });
|
|
|
|
|
|
|
|
saveAs(blob, this.saveFilename + '.json');
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
load()
|
|
|
|
{
|
2020-12-28 20:20:51 +00:00
|
|
|
const loadFile = this.$refs.loadFile.files[0];
|
2020-12-28 15:11:12 +00:00
|
|
|
if (!loadFile)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.saveFilename = loadFile.name.toLowerCase().endsWith('.json')
|
|
|
|
? loadFile.name.substring(0, loadFile.name.length - 5)
|
|
|
|
: loadFile.name;
|
|
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.addEventListener('load', (event) =>
|
|
|
|
{
|
|
|
|
this.$store.commit('load', event.target.result);
|
|
|
|
});
|
|
|
|
reader.readAsBinaryString(loadFile);
|
2020-12-29 18:56:20 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
print()
|
|
|
|
{
|
|
|
|
window.print();
|
2020-12-28 15:11:12 +00:00
|
|
|
}
|
2020-12-28 21:05:27 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
hash: (newValue) =>
|
|
|
|
{
|
|
|
|
history.replaceState({}, '', '#' + newValue);
|
|
|
|
}
|
2020-12-28 14:31:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss">
|
2020-12-29 18:56:20 +00:00
|
|
|
html, body
|
|
|
|
{
|
|
|
|
background-color: #444444;
|
|
|
|
color: white;
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
overflow: none;
|
|
|
|
|
|
|
|
@media print
|
|
|
|
{
|
|
|
|
background-color: white;
|
|
|
|
color: black;
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#app
|
|
|
|
{
|
2020-12-28 14:31:21 +00:00
|
|
|
font-family: 'Verdana', 'Arial', sans-serif;
|
|
|
|
font-size: 10pt;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
flex-direction: horizontal;
|
2020-12-29 18:56:20 +00:00
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:00:03 +00:00
|
|
|
h2
|
|
|
|
{
|
|
|
|
color: #808080;
|
|
|
|
font-size: 80%;
|
|
|
|
}
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
a
|
|
|
|
{
|
|
|
|
color: #99ccff;
|
|
|
|
}
|
|
|
|
|
|
|
|
input, select
|
|
|
|
{
|
|
|
|
background-color: #303030;
|
|
|
|
color: white;
|
|
|
|
border: solid 1px #606060;
|
|
|
|
padding-top: .3em;
|
|
|
|
padding-bottom: .3em;
|
|
|
|
|
|
|
|
&:focus
|
|
|
|
{
|
|
|
|
outline: solid 1px #808080;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
input[type='number']
|
|
|
|
{
|
|
|
|
text-align: right;
|
|
|
|
}
|
|
|
|
|
|
|
|
button
|
|
|
|
{
|
|
|
|
background-color: #404040;
|
|
|
|
color: white;
|
|
|
|
border: solid 1px #606060;
|
|
|
|
padding-top: .3em;
|
|
|
|
padding-bottom: .3em;
|
|
|
|
|
2020-12-30 22:00:03 +00:00
|
|
|
&:hover:not([disabled])
|
2020-12-29 18:56:20 +00:00
|
|
|
{
|
|
|
|
background-color: #808080;
|
|
|
|
}
|
2020-12-28 14:31:21 +00:00
|
|
|
}
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
.hideOnPrint
|
|
|
|
{
|
|
|
|
@media print
|
|
|
|
{
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
2020-12-28 14:31:21 +00:00
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
<style lang="scss" scoped>
|
|
|
|
.sidebar
|
2020-12-28 14:31:21 +00:00
|
|
|
{
|
2020-12-29 18:56:20 +00:00
|
|
|
background-color: #383838;
|
|
|
|
color: white;
|
|
|
|
width: 35em;
|
|
|
|
flex-shrink: 0;
|
|
|
|
box-shadow: 0 0 3em #101010;
|
|
|
|
|
|
|
|
.toolbar
|
|
|
|
{
|
|
|
|
background-color: #333333;
|
|
|
|
box-shadow: -.2em 0 .5em black;
|
|
|
|
margin-bottom: .5em;
|
|
|
|
|
|
|
|
a
|
|
|
|
{
|
|
|
|
color: white;
|
|
|
|
display: inline-block;
|
|
|
|
padding: .5em;
|
|
|
|
padding-left: 1em;
|
|
|
|
padding-right: 1em;
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
&.active
|
|
|
|
{
|
|
|
|
background-color: #0066cc;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:hover:not(.active)
|
|
|
|
{
|
|
|
|
background-color: #004d99;
|
|
|
|
}
|
|
|
|
|
|
|
|
> svg
|
|
|
|
{
|
|
|
|
display: block;
|
|
|
|
margin-left: auto;
|
|
|
|
margin-right: auto;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab
|
|
|
|
{
|
|
|
|
padding: 1em;
|
|
|
|
}
|
2020-12-28 14:31:21 +00:00
|
|
|
}
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
.settings
|
2020-12-28 14:31:21 +00:00
|
|
|
{
|
|
|
|
margin-right: 1em;
|
|
|
|
|
|
|
|
.block
|
|
|
|
{
|
|
|
|
margin-bottom: 2em;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-29 18:56:20 +00:00
|
|
|
.content
|
2020-12-28 14:31:21 +00:00
|
|
|
{
|
2020-12-29 18:56:20 +00:00
|
|
|
flex-grow: 1;
|
|
|
|
padding: 2em;
|
|
|
|
padding-left: 3em;
|
|
|
|
overflow: auto;
|
|
|
|
|
|
|
|
@media print
|
|
|
|
{
|
|
|
|
background-color: white;
|
|
|
|
color: black;
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
|
|
|
h1
|
|
|
|
{
|
|
|
|
margin-top: 0;
|
|
|
|
margin-bottom: 0;
|
|
|
|
font-size: 150%;
|
|
|
|
}
|
|
|
|
|
2020-12-28 14:31:21 +00:00
|
|
|
.preview
|
|
|
|
{
|
2020-12-29 18:56:20 +00:00
|
|
|
margin-bottom: 2em;
|
2020-12-28 14:31:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-28 21:07:22 +00:00
|
|
|
.loadSave, .about
|
2020-12-28 14:31:21 +00:00
|
|
|
{
|
|
|
|
width: 30em;
|
|
|
|
}
|
|
|
|
</style>
|