Merge branch 'design-updates' into 'master'

Add dark mode

See merge request packmate/packmate-frontend!4
This commit is contained in:
Sergey 2023-05-01 21:21:47 +00:00
commit 1107eb78e3
9 changed files with 3609 additions and 291 deletions

3703
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
"dependencies": {
"axios": "^1.4.0",
"bootstrap": "^4.6.2",
"bootstrap-darkmode": "^0.9.1",
"bootstrap-vue": "^2.23.1",
"core-js": "^3.26.1",
"idb": "^5.0.8",
@ -30,6 +31,8 @@
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^8.0.0",
"sass-loader": "^10.4.1",
"vue-template-compiler": "^2.7.14"
},
"eslintConfig": {

View File

@ -307,3 +307,7 @@
padding-top: 55px; /* Space for fixed navbar */
}
</style>
<style lang="scss">
@import "~bootstrap-darkmode/scss/darktheme";
</style>

View File

@ -1,20 +1,20 @@
<template>
<div class="packet" :class="{'packet-incoming': packet.incoming}">
<div :class="{'packet-incoming': packet.incoming, 'packet-outgoing': !packet.incoming}">
<div>#{{ packet.id }} at {{ dateToText(packet.timestamp) }}, {{ printPacketFlags(packet) }}
<button @click.prevent="copyRaw" class="btn btn-link">Copy HEX</button>
<button @click.prevent="copyText" class="btn btn-link">Copy text</button>
<button @click.prevent="copyPythonBytes" class="btn btn-link">Copy as Python bytes</button>
</div>
<p v-if="!this.$store.state.hexdumpMode"
class="pt-2 pb-2 mb-3 border-bottom"
class="pt-2 pb-2 mb-3"
v-html="stringdata"/>
<p v-else
class="pt-2 pb-2 mb-3 border-bottom"
class="pt-2 pb-2 mb-3"
v-html="hexdata"/>
</div>
</template>
<!--suppress JSUnresolvedVariable -->
<!--suppress JSUnresolvedVariable, JSDeprecatedSymbols -->
<script>
export default {
name: 'Packet',
@ -176,21 +176,19 @@
</script>
<style scoped>
.packet {
background: rgba(224, 246, 255, 0.4);
box-shadow: 0 0 5px 5px rgba(224, 246, 255, 0.4);
.packet-outgoing {
background: var(--response-packet-color);
box-shadow: 0 0 5px 5px var(--response-packet-color);
}
/*noinspection CssUnusedSymbol*/
.packet-incoming {
background: rgba(251, 233, 231, 0.4);
box-shadow: 0 0 5px 5px rgba(251, 233, 231, 0.4);
background: var(--request-packet-color);
box-shadow: 0 0 5px 5px var(--request-packet-color);
}
p {
font-family: "Ubuntu Mono", "Lucida Console", monospace;
font-size: 100%;
color: black;
margin-bottom: 10px;
padding-bottom: 5px;
word-break: break-word;

View File

@ -1,5 +1,6 @@
<template>
<nav class="col-sm-3 d-none d-sm-block bg-light sidebar">
<nav class="col-sm-3 d-none d-sm-block sidebar"
:class="this.$store.state.theme === 'light' ? 'bg-light' : 'bg-dark'">
<div class="m-2">
<button type="button" class="btn btn-sm"
:title="this.$store.state.pause ? 'Continue' : 'Pause new streams'"

View File

@ -127,12 +127,15 @@
color: #333;
}
[data-theme=dark] .nav-link {
color: #f8f9fa;
}
/*noinspection CssUnusedSymbol*/
.nav-link.active {
color: #007BFF;
}
/*noinspection CssUnusedSymbol*/
.highlight {
background: rgba(220, 53, 69, 0.3);
box-shadow: 0 0 1px 1px rgba(255, 119, 182, 0.3);
@ -140,7 +143,10 @@
.stream-item {
transition: all .5s;
border: 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 24px 3px -25px #4b4e51;
}
[data-theme=dark] .stream-item {
box-shadow: 0 24px 3px -25px #84898E;
}
</style>

View File

@ -0,0 +1,127 @@
<template>
<div>
<input
@change="toggleTheme"
id="checkbox"
type="checkbox"
class="switch-checkbox"
/>
<label for="checkbox" class="switch-label">
<span class="checkbox-emoji">🌙</span>
<span class="checkbox-emoji">☀️</span>
<div
class="switch-toggle"
:class="{ 'switch-toggle-checked': chosenTheme === 'dark' }"
></div>
</label>
</div>
</template>
<script>
export default {
mounted() {
this.initTheme();
},
computed: {
chosenTheme: {
get() {
return this.$store.state.theme || this.detectTheme();
},
set(theme) {
this.$store.commit('setTheme', theme);
this.displayTheme(theme);
},
},
},
methods: {
toggleTheme() {
const activeTheme = this.chosenTheme;
if (activeTheme === "light") {
this.chosenTheme = 'dark';
} else {
this.chosenTheme = 'light';
}
console.debug('Toggling theme from ', activeTheme, ' to ', this.chosenTheme);
},
initTheme() {
this.displayTheme(this.chosenTheme);
},
detectTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
},
displayTheme(theme) {
document.body.setAttribute('data-theme', theme);
},
},
};
</script>
<style scoped>
.switch-checkbox {
display: none;
}
.switch-label {
align-items: center;
background: var(--button-text-primary-color);
border: calc(var(--button-element-size) * 0.025) solid var(--button-accent-color);
border-radius: var(--button-element-size);
cursor: pointer;
display: flex;
font-size: calc(var(--button-element-size) * 0.3);
height: calc(var(--button-element-size) * 0.35);
position: relative;
padding: calc(var(--button-element-size) * 0.1);
transition: background 0.5s ease;
justify-content: space-between;
width: var(--button-element-size);
z-index: 1;
box-sizing: content-box;
}
.switch-toggle {
position: absolute;
background-color: var(--button-background-color-primary);
border-radius: 50%;
top: calc(var(--button-element-size) * 0.07);
left: calc(var(--button-element-size) * 0.07);
height: calc(var(--button-element-size) * 0.4);
width: calc(var(--button-element-size) * 0.4);
transform: translateX(0);
transition: transform 0.3s ease, background-color 0.5s ease;
}
.switch-toggle-checked {
transform: translateX(calc(var(--button-element-size) * 0.6)) !important;
}
.checkbox-emoji {
color: transparent;
text-shadow: 0 0 0 var(--button-background-color-primary);
}
>>> {
--button-element-size: 3rem;
--button-background-color-primary: #ebebeb;
--button-background-color-secondary: #fafafa;
--button-accent-color: #cacaca;
--button-text-primary-color: #222;
}
[data-theme=dark] * {
--button-background-color-primary: #1e1e1e;
--button-background-color-secondary: #2d2d30;
--button-accent-color: #3f3f3f;
--button-text-primary-color: #ddd;
}
</style>

View File

@ -8,6 +8,8 @@ Vue.use(Vuex);
// noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols
export default new Vuex.Store({
state: {
theme: null,
hexdumpBlockSize: 16,
hexdumpLineNumberBase: 10,
pageSize: 25,
@ -29,6 +31,7 @@ export default new Vuex.Store({
currentServicesStreamsCount: {},
},
mutations: {
setTheme: (s, p) => s.theme = p,
setHexdumpBlockSize: (s, p) => s.hexdumpBlockSize = p,
setHexdumpLineNumberBase: (s, p) => s.hexdumpLineNumberBase = p,
setPageSize: (s, p) => s.pageSize = p,
@ -64,6 +67,7 @@ export default new Vuex.Store({
console.debug('Got mutation:', mutation);
const mName = mutation?.type;
const process = mName !== 'toggleDisplayFavoritesOnly'
&& mName !== 'setTheme'
&& mName !== 'setServiceModalName'
&& mName !== 'setServiceModalId'
&& mName !== 'togglePause'

View File

@ -1,18 +1,22 @@
<template>
<div>
<p>
<span style="background: rgba(224, 246, 255, 0.4); box-shadow: 0 0 5px 5px rgba(224, 246, 255, 0.4);">Response</span>,
<span style="background: rgba(251, 233, 231, 0.4); box-shadow: 0 0 5px 5px rgba(251, 233, 231, 0.4);">request</span>.
<span style="background: var(--request-packet-color); box-shadow: 0 0 3px 3px var(--request-packet-color);">Request</span>,
<span style="background: var(--response-packet-color); box-shadow: 0 0 3px 3px var(--response-packet-color);">response</span>.
<ThemeButton class="d-inline float-right" style="margin-top: -0.25rem" />
</p>
<Packet v-for="packet in packets"
:key="packet.id"
:packet="packet"/>
<infinite-loading @infinite="infiniteLoadingHandler" ref="infiniteLoader"/>
<infinite-loading @infinite="infiniteLoadingHandler" ref="infiniteLoader">
<span slot="no-results"></span>
</infinite-loading>
</div>
</template>
<script>
import Packet from '../components/Packet';
import ThemeButton from '@/components/ThemeButton.vue';
export default {
name: 'ContentStream',
@ -77,7 +81,21 @@
},
},
components: {
ThemeButton,
Packet,
},
};
</script>
<style>
:root {
--response-packet-color: rgb(231, 248, 253);
--request-packet-color: rgb(254, 234, 231);
}
:root [data-theme=dark] {
color-scheme: dark;
--response-packet-color: rgb(43, 83, 120);
--request-packet-color: rgb(52, 58, 64);
}
</style>