Skip to content

Commit

Permalink
feat: draggable dashboard cards
Browse files Browse the repository at this point in the history
  • Loading branch information
cadriel committed Dec 26, 2020
1 parent 25aa5b9 commit 5286e8e
Show file tree
Hide file tree
Showing 23 changed files with 504 additions and 168 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"vue-prism-editor": "^1.2.2",
"vue-property-decorator": "^9.1.2",
"vue-router": "^3.2.0",
"vuedraggable": "^2.24.3",
"vuetify": "^2.3.21",
"vuex": "^3.6.0"
},
Expand Down
33 changes: 33 additions & 0 deletions src/components/AppDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@

<v-divider></v-divider>
<system-printers-widget @click="this.close"></system-printers-widget>

<v-divider></v-divider>
<v-list
dense
subheader
two-line
flat
v-model="layoutMode">
<v-list-item-group
multiple
>
<v-list-item @click.prevent="layoutMode = !layoutMode">
<v-list-item-action>
<v-checkbox :input-value="layoutMode"></v-checkbox>
</v-list-item-action>

<v-list-item-content>
<v-list-item-title>Adjust layout</v-list-item-title>
<v-list-item-subtitle>Adjust dashboard layout</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>

</v-navigation-drawer>
</template>

Expand All @@ -69,6 +93,15 @@ export default class AppDrawer extends Mixins(UtilsMixin) {
@Prop({ type: Boolean, default: false })
value!: boolean
get layoutMode () {
return this.$store.state.config.layoutMode
}
set layoutMode (val: boolean) {
this.$store.commit('config/setLayoutMode', val)
this.close()
}
get instanceName () {
return this.$store.state.config.fileConfig.general.instanceName
}
Expand Down
92 changes: 74 additions & 18 deletions src/components/cards/CollapsableCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,42 @@
color="tertiary"
:rounded="rounded"
:loading="isLoading">
<v-card-title class="card-title quaternary py-1">

<div v-if="hasTabbedTitleSlot()" :class="{ 'draggable': isInLayout }">
<slot
name="tabbed-title"
v-bind:attrs="{
inLayout: isInLayout,
enabled,
value: isCollapsed,
isCollapsed
}"
v-bind:on="{
'input': onCollapseChange,
'layout-enabled': onLayoutEnabled
}">
</slot>
</div>

<v-card-title
v-if="!hasTabbedTitleSlot()"
class="card-title quaternary py-1"
:class="{ 'draggable': isInLayout }"
>
<slot name="title">
<v-icon left>{{ icon }}</v-icon>
<span class="font-weight-light">{{ title }}</span>
</slot>
<v-spacer />

<!-- Menu Buttons, desktop + -->
<div class="d-none d-lg-flex">
<div class="d-none d-lg-flex" v-if="!isInLayout">
<slot name="menu"></slot>
</div>

<!-- Menu, mobile / tablet -->
<v-menu
v-if="hasMenuSlot && !hideMenu"
v-if="hasMenuSlot && !hideMenu && !isInLayout"
left>
<template v-slot:activator="{ on, attrs }">
<v-btn
Expand All @@ -37,20 +58,28 @@

<!-- Collapse Control -->
<slot name="collapse-button">
<btn-collapse v-model="isCollapsed"></btn-collapse>
<btn-collapse
:value="isCollapsed"
@input="isCollapsed = $event"
:enabled="enabled"
:inLayout="isInLayout"
@layout-enabled="$emit('enabled', $event)"
>
</btn-collapse>
</slot>
</v-card-title>

<v-divider></v-divider>

<v-expand-transition v-if="!lazy">
<div
@transitionend="transitionEvent"
id="card-content"
v-if="!isCollapsed"
v-if="!isCollapsed && !isInLayout"
:class="_contentClasses"
:style="_contentStyles">
<v-card-subtitle class="tertiary py-2" v-if="subTitle || hasSubTitleSlot">
<slot name="subTitle">
<slot name="sub-title">
<span v-html="subTitle"></span>
</slot>
</v-card-subtitle>
Expand All @@ -65,7 +94,7 @@
<div
@transitionend="transitionEvent"
id="card-content"
v-show="!isCollapsed"
v-show="!isCollapsed && !isInLayout"
:class="_contentClasses"
:style="_contentStyles">
<v-card-subtitle class="tertiary py-2" v-if="subTitle || hasSubTitleSlot">
Expand All @@ -87,7 +116,7 @@ import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({})
export default class ToolheadCard extends Vue {
@Prop({ type: String })
@Prop({ type: String, required: true })
title!: string
@Prop({ type: String, required: false })
Expand All @@ -99,12 +128,21 @@ export default class ToolheadCard extends Vue {
@Prop({ type: Boolean, default: true })
lazy!: boolean // use v-show or v-if
@Prop({ type: String, required: true })
@Prop({ type: String, required: false })
icon!: string
@Prop({ type: Boolean, default: false })
loading!: boolean
@Prop({ type: Boolean, default: false })
draggable!: boolean
@Prop({ type: Boolean, default: false })
inLayout!: boolean
@Prop({ type: Boolean, default: true })
enabled!: boolean
@Prop({ type: Boolean, default: true })
collapsable!: boolean
Expand Down Expand Up @@ -153,43 +191,53 @@ export default class ToolheadCard extends Vue {
}
get id (): string {
return (this.cardKey) ? this.cardKey : this.title
if (this.cardKey) return this.cardKey
return this.title.replace(' ', '')
}
get isLoading (): boolean | string {
return (this.loading) ? 'primary' : false
}
get isCollapsed (): boolean {
const collapsed = (this.$store.state.config.localConfig[this.id] === undefined)
const collapsed = (this.$store.state.config.cardState[this.id] === undefined)
? this.collapsed
: this.$store.state.config.localConfig[this.id]
: this.$store.state.config.cardState[this.id]
return collapsed
}
set isCollapsed (val: boolean) {
this.$store.dispatch('config/saveLocal', { [this.id]: val })
this.$store.dispatch('config/saveCardState', { [this.id]: val })
}
get isInLayout (): boolean {
return (this.inLayout && this.draggable)
}
get hasDefaultSlot () {
return this.$slots.default || this.$scopedSlots.default
return !!this.$slots.default || !!this.$scopedSlots.default
}
get hasMenuSlot () {
return this.$slots.menu || this.$scopedSlots.menu
return !!this.$slots.menu || !!this.$scopedSlots.menu
}
get hasTitleSlot () {
return this.$slots.title || this.$scopedSlots.title
return !!this.$slots.title || !!this.$scopedSlots.title
}
get hasSubTitleSlot () {
return this.$slots.subTitle || this.$scopedSlots.subTitle
return !!this.$slots['sub-title'] || !!this.$scopedSlots['sub-title']
}
get hasCollapseButtonSlot () {
return this.$slots['collapse-button'] || this.$scopedSlots['collapse-button']
return !!this.$slots['collapse-button'] || !!this.$scopedSlots['collapse-button']
}
// Moved to a regular function because slots are not reactive.
hasTabbedTitleSlot () {
return !!this.$slots['tabbed-title'] || !!this.$scopedSlots['tabbed-title']
}
mounted () {
Expand All @@ -198,6 +246,14 @@ export default class ToolheadCard extends Vue {
}
}
onCollapseChange (e: boolean) {
this.isCollapsed = e
}
onLayoutEnabled (e: Event) {
this.$emit('enabled', e)
}
transitionEvent (e: TransitionEvent) {
if (
e.target &&
Expand Down
15 changes: 13 additions & 2 deletions src/components/cards/dashboard/CameraCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
title="Camera"
icon="$camera"
:lazy="false"
:collapsed="true">
:collapsed="true"
:draggable="true"
:inLayout="inLayout"
:enabled="enabled"
@enabled="$emit('enabled', $event)">

<img :src="cameraUrl" class="webcam" :style="cameraTransforms" v-if="streamType === 'mjpgstreamer'" />
<video :src="cameraUrl" autoplay class="webcam" :style="cameraTransforms" v-if="streamType === 'ipcamera'" />
</collapsable-card>
</template>

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import { Component, Mixins, Prop } from 'vue-property-decorator'
import PrintStatusWidget from '@/components/widgets/PrintStatusWidget.vue'
import UtilsMixin from '@/mixins/utils'
Expand All @@ -21,6 +25,9 @@ import UtilsMixin from '@/mixins/utils'
}
})
export default class CameraCard extends Mixins(UtilsMixin) {
@Prop({ type: Boolean, default: true })
enabled!: boolean
get streamType () {
return this.$store.state.config.fileConfig.camera.type
}
Expand All @@ -36,6 +43,10 @@ export default class CameraCard extends Mixins(UtilsMixin) {
transforms += (config && config.flipY) ? ' scaleY(-1)' : ''
return (transforms.trimLeft().length) ? { transform: transforms.trimLeft() } : {}
}
get inLayout (): boolean {
return (this.$store.state.config.layoutMode)
}
}
</script>

Expand Down
15 changes: 13 additions & 2 deletions src/components/cards/dashboard/ConsoleCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
cardClasses="mb-2 mb-sm-4 d-flex flex-column"
contentClasses="flex-grow-1 flow-shrink-0"
:height="450"
:collapsed="true">
:collapsed="true"
:draggable="true"
:inLayout="inLayout"
:enabled="enabled"
@enabled="$emit('enabled', $event)">

<console-widget
:items="items"
Expand All @@ -15,7 +19,7 @@
</template>

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import { Component, Mixins, Prop } from 'vue-property-decorator'
import PrintStatusWidget from '@/components/widgets/PrintStatusWidget.vue'
import UtilsMixin from '@/mixins/utils'
import ConsoleWidget from '@/components/widgets/ConsoleWidget.vue'
Expand All @@ -28,8 +32,15 @@ import { ConsoleEntry } from '@/store/socket/types'
}
})
export default class ConsoleCard extends Mixins(UtilsMixin) {
@Prop({ type: Boolean, default: true })
enabled!: boolean
get items (): ConsoleEntry[] {
return this.$store.state.socket.console
}
get inLayout (): boolean {
return (this.$store.state.config.layoutMode)
}
}
</script>
13 changes: 11 additions & 2 deletions src/components/cards/dashboard/PrinterLimitsCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
<collapsable-card
title="Printer Limits"
icon="$limits"
:collapsed="true">
:collapsed="true"
:draggable="true"
:inLayout="inLayout"
:enabled="enabled"
@enabled="$emit('enabled', $event)">
<printer-limits-widget></printer-limits-widget>
</collapsable-card>
</template>

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import { Component, Mixins, Prop } from 'vue-property-decorator'
import PrinterLimitsWidget from '@/components/widgets/PrinterLimitsWidget.vue'
import UtilsMixin from '@/mixins/utils'
Expand All @@ -18,6 +22,11 @@ import UtilsMixin from '@/mixins/utils'
}
})
export default class PrinterLimitsCard extends Mixins(UtilsMixin) {
@Prop({ type: Boolean, default: true })
enabled!: boolean
get inLayout (): boolean {
return (this.$store.state.config.layoutMode)
}
}
</script>
Loading

0 comments on commit 5286e8e

Please sign in to comment.