Option to highlight the current board / layer

This commit is contained in:
Mark van Renswoude 2020-12-31 09:22:37 +01:00
parent aca82c477f
commit b5c9bf0099
12 changed files with 144 additions and 26 deletions

View File

@ -1,14 +1,9 @@
ToDo
====
Bugs
----
- Double scrollbar, all the way (sidebar needs it's own scrollbar)
Should have
----
- Automatically switch to Custom direction when clicking a strip (preserving the directions currently on screen otherwise)
- Highlight strips for the current board, highlight layer for the focused/hovered layer
- Automatically switch to Custom direction when clicking a strip (preserving the directions currently on screen otherwise)?
- Show list of end grain board numbers and reversed in a "Glue list" for printing
- Support for fractional inches (see, not all europeans look down on freedom units!)
- Reset to default
@ -19,6 +14,5 @@ Nice to have
- Show remaining material
- Theme selection for the preview background
- Render width and height of the boards in the previews (simplified version implemented, moved to Nice to have)
- Support for mixing multiple boards in the end grain version
- 3D effect for previews emulating thickness / crosscut width
- Actual wood textures

View File

@ -1 +0,0 @@
.settings[data-v-7067c7c7]{display:inline-grid;grid-template-columns:auto auto;grid-column-gap:1em;grid-row-gap:.25em}.settings h2[data-v-7067c7c7]{margin-top:1em;margin-bottom:.25em;grid-column:1/3}.layers[data-v-01187e40]{display:grid;grid-template-columns:-webkit-min-content auto 5em -webkit-min-content;grid-template-columns:min-content auto 5em min-content;grid-column-gap:1em}.layers .hint[data-v-01187e40]{color:grey;text-align:center;grid-column:1/5;margin-bottom:1em}.layers .index[data-v-01187e40]{cursor:-webkit-grab;cursor:grab}.layers .index.dropTargetAbove[data-v-01187e40]{border-top:1px solid #fff;cursor:-webkit-grabbing;cursor:grabbing}.layers .index.dropTargetBelow[data-v-01187e40]{border-bottom:1px solid #fff;cursor:-webkit-grabbing;cursor:grabbing}.layers .add[data-v-01187e40]{grid-column:2/5;padding-top:1em}.layers .widthwarning[data-v-01187e40]{font-size:80%;color:#ff0;grid-column:2/5;padding-top:.5em}.layers .header[data-v-01187e40]{font-weight:700;margin-bottom:.25em}.board[data-v-01187e40]{display:flex;margin-bottom:2em}.board .name[data-v-01187e40]{flex-grow:1;padding:.5em}.boardsettings[data-v-01187e40]{display:inline-grid;grid-template-columns:-webkit-max-content 5em;grid-template-columns:max-content 5em;grid-column-gap:1em;margin-bottom:2em}h2[data-v-01187e40]{margin-top:2em}.wood[data-v-15338ab0]{display:grid;grid-template-columns:auto -webkit-min-content -webkit-min-content;grid-template-columns:auto min-content min-content;grid-column-gap:1em}.wood .add[data-v-15338ab0]{grid-column:1/4;padding-top:1em}.wood .header[data-v-15338ab0]{font-weight:700;margin-bottom:.25em}.wood button[data-v-15338ab0]{height:100%}.dimensions[data-v-61e0967d]{margin-bottom:.5em}.draghint[data-v-61e0967d]{margin-bottom:2em}svg[data-v-61e0967d]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media screen{svg[data-v-61e0967d]{box-shadow:0 0 3em #000}}@media print{svg[data-v-61e0967d]{max-width:100%}}svg.dragging[data-v-61e0967d]{cursor:-webkit-grabbing;cursor:grabbing}.dimensions[data-v-a5fd011a]{margin-bottom:.5em}@media screen{svg[data-v-a5fd011a]{box-shadow:0 0 3em #000}}@media print{svg[data-v-a5fd011a]{max-width:100%}}h2[data-v-30665a8d]{font-size:110%}.list[data-v-30665a8d]{border-collapse:collapse;margin-top:1em;margin-bottom:3em}.list td[data-v-30665a8d],.list th[data-v-30665a8d]{padding:.25em;padding-left:1em;padding-right:1em}.list .dimension[data-v-30665a8d]{text-align:right}.list tr.board td[data-v-30665a8d]{font-style:italic}.list tr:nth-child(2n):not(.board) td[data-v-30665a8d]{background-color:#555}@media print{.list tr:nth-child(2n):not(.board) td[data-v-30665a8d]{background-color:#f0f0f0}}body,html{background-color:#444;color:#fff;margin:0;padding:0;width:100%;height:100%;overflow:none}@media print{body,html{background-color:#fff;color:#000;overflow:visible}}#app{font-family:Verdana,Arial,sans-serif;font-size:10pt;display:flex;flex-direction:horizontal;width:100%;height:100%}h2{color:grey;font-size:80%}a{color:#9cf}input,select{background-color:#303030;color:#fff;border:1px solid #606060;padding-top:.3em;padding-bottom:.3em}input:focus,select:focus{outline:1px solid grey}input[type=number]{text-align:right}button{background-color:#404040;color:#fff;border:1px solid #606060;padding-top:.3em;padding-bottom:.3em}button:hover:not([disabled]){background-color:grey}@media print{.hideOnPrint{display:none}}.sidebar[data-v-5bc914fd]{background-color:#383838;color:#fff;width:35em;flex-shrink:0;box-shadow:0 0 3em #101010}.sidebar .toolbar[data-v-5bc914fd]{background-color:#333;box-shadow:-.2em 0 .5em #000;margin-bottom:.5em}.sidebar .toolbar a[data-v-5bc914fd]{color:#fff;display:inline-block;padding:.5em;padding-left:1em;padding-right:1em;cursor:pointer}.sidebar .toolbar a.active[data-v-5bc914fd]{background-color:#06c}.sidebar .toolbar a[data-v-5bc914fd]:hover:not(.active){background-color:#004d99}.sidebar .toolbar a>svg[data-v-5bc914fd]{display:block;margin-left:auto;margin-right:auto}.sidebar .tab[data-v-5bc914fd]{padding:1em}.settings[data-v-5bc914fd]{margin-right:1em}.settings .block[data-v-5bc914fd]{margin-bottom:2em}.content[data-v-5bc914fd]{flex-grow:1;padding:2em;padding-left:3em;overflow:auto}@media print{.content[data-v-5bc914fd]{background-color:#fff;color:#000;overflow:visible}}.content h1[data-v-5bc914fd]{margin-top:0;margin-bottom:0;font-size:150%}.content .preview[data-v-5bc914fd]{margin-bottom:2em}.about[data-v-5bc914fd],.loadSave[data-v-5bc914fd]{width:30em}

View File

@ -0,0 +1 @@
.settings[data-v-7067c7c7]{display:inline-grid;grid-template-columns:auto auto;grid-column-gap:1em;grid-row-gap:.25em}.settings h2[data-v-7067c7c7]{margin-top:1em;margin-bottom:.25em;grid-column:1/3}.layers[data-v-342492d6]{display:grid;grid-template-columns:-webkit-min-content auto 5em -webkit-min-content;grid-template-columns:min-content auto 5em min-content;grid-column-gap:1em}.layers .hint[data-v-342492d6]{color:grey;text-align:center;grid-column:1/5;margin-bottom:1em}.layers .index[data-v-342492d6]{cursor:-webkit-grab;cursor:grab}.layers .index.dropTargetAbove[data-v-342492d6]{border-top:1px solid #fff;cursor:-webkit-grabbing;cursor:grabbing}.layers .index.dropTargetBelow[data-v-342492d6]{border-bottom:1px solid #fff;cursor:-webkit-grabbing;cursor:grabbing}.layers .add[data-v-342492d6]{grid-column:2/5;padding-top:1em}.layers .widthwarning[data-v-342492d6]{font-size:80%;color:#ff0;grid-column:2/5;padding-top:.5em}.layers .header[data-v-342492d6]{font-weight:700;margin-bottom:.25em}.board[data-v-342492d6]{display:flex;margin-bottom:2em}.board .name[data-v-342492d6]{flex-grow:1;padding:.5em}.boardsettings[data-v-342492d6]{display:inline-grid;grid-template-columns:-webkit-max-content 5em;grid-template-columns:max-content 5em;grid-column-gap:1em;margin-bottom:2em}h2[data-v-342492d6]{margin-top:2em}.wood[data-v-15338ab0]{display:grid;grid-template-columns:auto -webkit-min-content -webkit-min-content;grid-template-columns:auto min-content min-content;grid-column-gap:1em}.wood .add[data-v-15338ab0]{grid-column:1/4;padding-top:1em}.wood .header[data-v-15338ab0]{font-weight:700;margin-bottom:.25em}.wood button[data-v-15338ab0]{height:100%}.dimensions[data-v-a8f1d07c]{margin-bottom:.5em}.draghint[data-v-a8f1d07c]{margin-bottom:2em}svg[data-v-a8f1d07c]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media screen{svg[data-v-a8f1d07c]{box-shadow:0 0 3em #000}}@media print{svg[data-v-a8f1d07c]{max-width:100%}}svg.dragging[data-v-a8f1d07c]{cursor:-webkit-grabbing;cursor:grabbing}svg.highlightBoard .boardLayer[data-v-a8f1d07c]:not(.highlightedBoard),svg.highlightLayer .layer[data-v-a8f1d07c]:not(.highlightedLayer){opacity:.5}.dimensions[data-v-a5fd011a]{margin-bottom:.5em}@media screen{svg[data-v-a5fd011a]{box-shadow:0 0 3em #000}}@media print{svg[data-v-a5fd011a]{max-width:100%}}h2[data-v-30665a8d]{font-size:110%}.list[data-v-30665a8d]{border-collapse:collapse;margin-top:1em;margin-bottom:3em}.list td[data-v-30665a8d],.list th[data-v-30665a8d]{padding:.25em;padding-left:1em;padding-right:1em}.list .dimension[data-v-30665a8d]{text-align:right}.list tr.board td[data-v-30665a8d]{font-style:italic}.list tr:nth-child(2n):not(.board) td[data-v-30665a8d]{background-color:#555}@media print{.list tr:nth-child(2n):not(.board) td[data-v-30665a8d]{background-color:#f0f0f0}}body,html{background-color:#444;color:#fff;margin:0;padding:0;width:100%;height:100%;overflow:none}@media print{body,html{background-color:#fff;color:#000;overflow:visible}}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-thumb{background-color:#606060}#app{font-family:Verdana,Arial,sans-serif;font-size:10pt;display:flex;flex-direction:horizontal;width:100%;height:100%}h2{color:grey;font-size:80%}a{color:#9cf}input,select{background-color:#303030;color:#fff;border:1px solid #606060;padding-top:.3em;padding-bottom:.3em}input:focus,select:focus{outline:1px solid grey}input[type=number]{text-align:right}button{background-color:#404040;color:#fff;border:1px solid #606060;padding-top:.3em;padding-bottom:.3em}button:hover:not([disabled]){background-color:grey}@media print{.hideOnPrint{display:none}}.sidebar[data-v-0a5c6a00]{background-color:#383838;color:#fff;width:35em;flex-shrink:0;box-shadow:0 0 3em #101010;display:flex;flex-direction:column}.sidebar .toolbar[data-v-0a5c6a00]{background-color:#333;box-shadow:-.2em 0 .5em #000;margin-bottom:.5em}.sidebar .toolbar a[data-v-0a5c6a00]{color:#fff;display:inline-block;padding:.5em;padding-left:1em;padding-right:1em;cursor:pointer}.sidebar .toolbar a.active[data-v-0a5c6a00]{background-color:#06c}.sidebar .toolbar a[data-v-0a5c6a00]:hover:not(.active){background-color:#004d99}.sidebar .toolbar a>svg[data-v-0a5c6a00]{display:block;margin-left:auto;margin-right:auto}.sidebar .tabs[data-v-0a5c6a00]{overflow:auto}.sidebar .tab[data-v-0a5c6a00]{padding:1em}.settings[data-v-0a5c6a00]{margin-right:1em}.settings .block[data-v-0a5c6a00]{margin-bottom:2em}.content[data-v-0a5c6a00]{flex-grow:1;padding:2em;padding-left:3em;overflow:auto}@media print{.content[data-v-0a5c6a00]{background-color:#fff;color:#000;overflow:visible}}.content h1[data-v-0a5c6a00]{margin-top:0;margin-bottom:0;font-size:150%}.content .preview[data-v-0a5c6a00]{margin-bottom:2em}.about[data-v-0a5c6a00],.loadSave[data-v-0a5c6a00]{width:30em}

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>CuttingBoard</title><link href="css/app.1164dda2.css" rel="preload" as="style"><link href="js/app.a1173833.js" rel="preload" as="script"><link href="js/chunk-vendors.37dada05.js" rel="preload" as="script"><link href="css/app.1164dda2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but CuttingBoard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.37dada05.js"></script><script src="js/app.a1173833.js"></script></body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>CuttingBoard</title><link href="css/app.2653d42c.css" rel="preload" as="style"><link href="js/app.62fd703d.js" rel="preload" as="script"><link href="js/chunk-vendors.37dada05.js" rel="preload" as="script"><link href="css/app.2653d42c.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but CuttingBoard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.37dada05.js"></script><script src="js/app.62fd703d.js"></script></body></html>

2
docs/js/app.62fd703d.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -227,6 +227,18 @@ html, body
}
}
::-webkit-scrollbar
{
width: 10px;
}
::-webkit-scrollbar-track {
}
::-webkit-scrollbar-thumb {
background-color: #606060;
}
#app
{
font-family: 'Verdana', 'Arial', sans-serif;
@ -300,6 +312,8 @@ button
width: 35em;
flex-shrink: 0;
box-shadow: 0 0 3em #101010;
display: flex;
flex-direction: column;
.toolbar
{
@ -335,6 +349,11 @@ button
}
}
.tabs
{
overflow: auto;
}
.tab
{
padding: 1em;

View File

@ -8,16 +8,18 @@
:width="viewportWidth"
:height="viewportHeight"
:viewBox="viewBox"
:class="{ dragging: dropTarget !== null }">
:class="{ dragging: dropTarget !== null, highlightBoard: settings.highlightBoard && volatile.highlightedBoard !== null, highlightLayer: settings.highlightLayer && volatile.highlightedBoard !== null && volatile.highlightedLayer !== null }">
<defs>
<g v-for="(board, boardIndex) in boards" :id="'strip' + boardIndex">
<g v-for="(board, boardIndex) in boards" :id="'strip' + boardIndex" class="boardLayer" :class="{ highlightedBoard: boardIndex === volatile.highlightedBoard }">
<rect
v-for="(layer, index) in board.layers"
:width="toPixels(board.thickness)"
:height="toPixels(layer.width)"
x="0"
:y="getBoardLayerOffset(board, index)"
:style="getBoardLayerStyle(board, index)" />
:style="getBoardLayerStyle(board, index)"
class="layer"
:class="{ highlightedLayer: boardIndex === volatile.highlightedBoard && index == volatile.highlightedLayer }" />
</g>
<g id="dropTarget">
<line x1="0" y1="0" x2="0" :y2="boardPixelHeight" style="stroke: white; stroke-width: 2" />
@ -60,6 +62,7 @@ export default {
computed: {
volatile() { return this.$store.state.volatile; },
settings() { return this.$store.state.settings; },
boards() { return this.$store.state.boards; },
wood() { return this.$store.state.wood; },
@ -322,5 +325,22 @@ svg
{
cursor: grabbing;
}
&.highlightBoard
{
.boardLayer:not(.highlightedBoard)
{
opacity: 0.5;
}
}
&.highlightLayer
{
.layer:not(.highlightedLayer)
{
opacity: 0.5;
}
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="board">
<div class="board" @mouseenter="highlightBoard" @mouseleave="removeHighlightBoard">
<button @click="previousBoard" :disabled="boardIndex == 0">&lt;</button>
<div class="name">Board {{ boardIndex + 1 }} of {{ boards.length }}</div>
<button @click="removeBoard" v-if="boards.length > 1">Remove</button>
@ -27,13 +27,13 @@
<span class="header">&nbsp;</span>
<template v-for="(layer, index) in currentBoard.layers">
<div class="index" :class="{ dropTargetAbove: dropTarget === index, dropTargetBelow: dropTarget === currentBoard.layers.length && index === currentBoard.layers.length - 1 }" :ref="'layer' + index" @mousedown.prevent="startDrag(index)">{{ index + 1 }}</div>
<select v-model="layer.wood" class="wood">
<div class="index" :class="{ dropTargetAbove: dropTarget === index, dropTargetBelow: dropTarget === currentBoard.layers.length && index === currentBoard.layers.length - 1 }" :ref="'layer' + index" @mousedown.prevent="startDrag(index)" @mouseenter="highlightLayer(index)" @mouseleave="removeHighlightLayer(index)">{{ index + 1 }}</div>
<select v-model="layer.wood" class="wood" @mouseenter="highlightLayer(index)" @mouseleave="removeHighlightLayer(index)">
<option v-for="(item, index) in wood" :value="index">{{ item.name }}</option>
</select>
<input type="number" class="width" :value="layer.width" @input="layer.width = parseFloatDef($event.target.value)" />
<input type="number" class="width" :value="layer.width" @input="layer.width = parseFloatDef($event.target.value)" @mouseenter="highlightLayer(index)" @mouseleave="removeHighlightLayer(index)"/>
<div class="remove">
<div class="remove" @mouseenter="highlightLayer(index)" @mouseleave="removeHighlightLayer(index)">
<button @click="removeLayer(index)">X</button>
</div>
</template>
@ -49,8 +49,20 @@
<div>
<h2>Preview settings</h2>
<input id="borders" type="checkbox" :checked="settings.borders" @change="$store.commit('updateSettings', { borders: $event.target.checked })" />
<label for="borders"> Show borders</label>
<div>
<input id="borders" type="checkbox" :checked="settings.borders" @change="$store.commit('updateSettings', { borders: $event.target.checked })" />
<label for="borders"> Show borders</label>
</div>
<div>
<input id="highlightBoard" type="checkbox" :checked="settings.highlightBoard" @change="$store.commit('updateSettings', { highlightBoard: $event.target.checked })" />
<label for="highlightBoard"> Highlight current board in end grain preview</label>
</div>
<div>
<input id="highlightLayer" type="checkbox" :checked="settings.highlightLayer" @change="$store.commit('updateSettings', { highlightLayer: $event.target.checked })" />
<label for="highlightLayer"> Highlight current layer in end grain preview</label>
</div>
</div>
<div>
@ -85,12 +97,14 @@ export default {
return {
boardIndex: 0,
dragIndex: null,
dropTarget: null
dropTarget: null,
updateHighlightedBoard: false
}
},
computed: {
volatile() { return this.$store.state.volatile; },
settings() { return this.$store.state.settings; },
wood() { return this.$store.state.wood; },
boards() { return this.$store.state.boards; },
@ -301,6 +315,43 @@ export default {
return;
this.$store.commit('updateSettings', { direction: direction });
},
highlightBoard()
{
this.updateHighlightedBoard = true;
this.$store.commit('updateVolatile', { highlightedBoard: this.boardIndex });
},
removeHighlightBoard()
{
this.updateHighlightedBoard = false;
this.$store.commit('updateVolatile', { highlightedBoard: null });
},
highlightLayer(index)
{
this.$store.commit('updateVolatile', { highlightedBoard: this.boardIndex, highlightedLayer: index });
},
removeHighlightLayer(index)
{
if (this.volatile.highlightedLayer === index)
{
this.$store.commit('updateVolatile', { highlightedBoard: null, highlightedLayer: null });
}
}
},
watch: {
boardIndex(newValue)
{
if (this.updateHighlightedBoard)
this.$store.commit('updateVolatile', { highlightedBoard: newValue });
}
}
}

View File

@ -13,7 +13,9 @@ function DefaultSettings()
borders: false,
bladeKerf: 0.35,
crosscutWidth: 3,
direction: DirectionEnum.alternate
direction: DirectionEnum.alternate,
highlightBoard: true,
highlightLayer: true
};
}
@ -70,6 +72,14 @@ function DefaultEndGrain()
export default createStore({
state: {
// These are not stored persistently
volatile: {
highlightedBoard: null,
highlightedLayer: null
},
// When adding any persistent settings, remember to update the
// serializeState and deserializeState functions below
settings: DefaultSettings(),
wood: DefaultWood(),
boards: DefaultBoards(),
@ -140,6 +150,12 @@ export default createStore({
},
updateVolatile(state, payload)
{
mergeObject(payload, state.volatile);
},
updateSettings(state, payload)
{
const oldUnits = state.settings.units;
@ -417,7 +433,9 @@ const SettingsNameMapJSON = {
borders: 'borders',
bladeKerf: 'bladeKerf',
crosscutWidth: 'crosscutWidth',
direction: 'direction'
direction: 'direction',
highlightBoard: 'highlightBoard',
highlightLayer: 'highlightLayer'
},
wood: {
@ -452,7 +470,9 @@ const SettingsNameMapMsgPack = {
borders: 'b',
bladeKerf: 'k',
crosscutWidth: 'c',
direction: 'd'
direction: 'd',
highlightBoard: 'h',
highlightLayer: 'l'
},
wood: {
@ -507,6 +527,8 @@ function serializeSettings(settings, map)
result[map.settings.bladeKerf] = settings.bladeKerf;
result[map.settings.crosscutWidth] = settings.crosscutWidth;
result[map.settings.direction] = settings.direction;
result[map.settings.highlightBoard] = settings.highlightBoard;
result[map.settings.highlightLayer] = settings.highlightLayer;
return result;
}
@ -593,6 +615,16 @@ function deserializeState(parsedPayload)
result.boards[0].thickness = parseFloatDef(parsedPayload.settings.boardThickness);
}
if (parsedPayload.hasOwnProperty(map.settings.self))
{
if (!parsedPayload[map.settings.self].hasOwnProperty(map.settings.highlightBoard))
result.settings.highlightBoard = true;
if (!parsedPayload[map.settings.self].hasOwnProperty(map.settings.highlightLayer))
result.settings.highlightLayer = true;
}
if (result.endGrain.length === 0)
updateEndGrain(result.endGrain, result.settings, result.boards);
@ -613,6 +645,8 @@ function deserializeSettings(parsedPayload, map)
bladeKerf: parseFloatDef(settings[map.settings.bladeKerf]),
crosscutWidth: parseFloatDef(settings[map.settings.crosscutWidth]),
direction: DirectionEnum.isValid(settings[map.settings.direction]) ? settings[map.settings.direction] : DirectionEnum.uniform,
highlightBoard: settings[map.settings.highlightBoard] === true,
highlightLayer: settings[map.settings.highlightLayer] === true
};
}