Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ declare module 'vue' {
AppObservedColumn: typeof import('./src/components/layout/AppObservedColumn.vue')['default']
AppQrCode: typeof import('./src/components/ui/AppQrCode.vue')['default']
AppSaveConfigAndRestartBtn: typeof import('./src/components/layout/AppSaveConfigAndRestartBtn.vue')['default']
AppScrollableContainer: typeof import('./src/components/ui/AppScrollableContainer.vue')['default']
AppSetting: typeof import('./src/components/ui/AppSetting.vue')['default']
AppSettingsNav: typeof import('./src/components/layout/AppSettingsNav.vue')['default']
AppTextField: typeof import('./src/components/ui/AppTextField.vue')['default']
Expand Down
36 changes: 0 additions & 36 deletions package-lock.json

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

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
"vue-meta": "^2.4.0",
"vue-property-decorator": "^9.1.2",
"vue-router": "^3.6.5",
"vue-virtual-scroller": "^1.1.2",
"vue2-touch-events": "^3.2.3",
"vuetify": "^2.7.2",
"vuetify-confirm": "^2.0.6",
Expand Down
88 changes: 88 additions & 0 deletions src/components/ui/AppScrollableContainer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<template>
<div
ref="box"
class="app-scrollable-container"
@scroll="onScroll"
>
<slot />
</div>
</template>

<script lang="ts">
import { Component, Prop, Ref, Vue, Watch } from 'vue-property-decorator'

@Component
export default class AppScrollableContainer extends Vue {
@Prop({ type: Boolean })
readonly reversed?: boolean

@Ref('box')
readonly box!: HTMLElement

scrollingAwayFromLatest = false

@Watch('reversed')
onReversed () {
this.scrollToLatest(true)
}

@Watch('scrollingAwayFromLatest')
onScrollingAwayFromLatest (value: boolean) {
this.$emit('update:scrolling-away-from-latest', value)
}

mounted () {
this.scrollToLatest()
}

updated () {
this.scrollToLatest()
}

onScroll () {
this.scrollingAwayFromLatest = (
this.reversed
? this.getScrollTop()
: this.getScrollBottom()
) > 1
}

scrollToLatest (force?: boolean) {
if (
!force &&
this.scrollingAwayFromLatest
) {
return
}

this.$nextTick(() => {
this.setScrollTop(
this.reversed
? 0
: this.box.scrollHeight
)
this.scrollingAwayFromLatest = false
})
}

getScrollTop () {
return this.box.scrollTop
}

setScrollTop (value: number) {
this.box.scrollTop = value
}

getScrollBottom () {
const { scrollTop, scrollHeight, clientHeight } = this.box

return scrollHeight - clientHeight - scrollTop
}
}
</script>
<style lang="scss" scoped>
.app-scrollable-container {
overflow-y: auto;
overflow-x: hidden;
}
</style>
131 changes: 25 additions & 106 deletions src/components/widgets/console/Console.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,26 @@
@send="sendCommand"
/>
<v-card
ref="console-wrapper"
flat
class="console-wrapper"
>
<DynamicScroller
ref="scroller"
:items="flipLayout ? [...items].reverse() : items"
:min-item-size="24"
<app-scrollable-container
ref="consoleScroller"
class="console-scroller"
:class="{
'console-scroller-fullscreen': fullscreen
}"
key-field="id"
:buffer="600"
@resize="scrollToLatest()"
:reversed="flipLayout"
@update:scrolling-away-from-latest="scrollingPausedModel = $event"
>
<template #default="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[
item.message,
]"
:data-index="index"
>
<console-item
:key="item.id"
:value="item"
class="console-item"
@click="handleEntryClick"
/>
</DynamicScrollerItem>
</template>
</DynamicScroller>
<console-item
v-for="item in orderedItems"
:key="item.id"
:value="item"
class="console-item"
@click="handleEntryClick"
/>
Comment thread
pedrolamas marked this conversation as resolved.
</app-scrollable-container>
</v-card>
<console-command
v-if="!readonly && !flipLayout"
Expand All @@ -54,14 +40,14 @@
</template>

<script lang="ts">
import { Component, Prop, Mixins, Watch, Ref, PropSync } from 'vue-property-decorator'
import { Component, Prop, Mixins, Ref, PropSync } from 'vue-property-decorator'
import StateMixin from '@/mixins/state'
import ConsoleCommand from './ConsoleCommand.vue'
import ConsoleItem from './ConsoleItem.vue'
import { SocketActions } from '@/api/socketActions'
import type { DinamicScroller } from 'vue-virtual-scroller'
import type { ConsoleEntry } from '@/store/console/types'
import type { UpdateResponse } from '@/store/version/types'
import type AppScrollableContainer from '@/components/ui/AppScrollableContainer.vue'

@Component({
components: {
Expand All @@ -70,7 +56,7 @@ import type { UpdateResponse } from '@/store/version/types'
}
})
export default class Console extends Mixins(StateMixin) {
@Prop({ type: [Array<ConsoleEntry>, Array<UpdateResponse>], default: () => [] })
@Prop({ type: [Array], default: () => [] })
readonly items!: ConsoleEntry[] | UpdateResponse[]

@Prop({ type: Boolean })
Expand All @@ -82,10 +68,8 @@ export default class Console extends Mixins(StateMixin) {
@PropSync('scrollingPaused', { type: Boolean })
scrollingPausedModel?: boolean

@Ref('scroller')
readonly dynamicScroller!: DinamicScroller

_pauseScroll = false
@Ref('consoleScroller')
readonly consoleScrollerElement!: AppScrollableContainer

get currentCommand (): string {
return this.$typedState.console.consoleCommand
Expand All @@ -99,79 +83,18 @@ export default class Console extends Mixins(StateMixin) {
return this.$typedState.config.uiSettings.general.flipConsoleLayout
}

set flipLayout (_) {
this.scrollToLatest(true)
}

mounted () {
this.dynamicScroller.$el.addEventListener('scroll', this.onScroll)
}

beforeDestroy () {
this.dynamicScroller.$el.removeEventListener('scroll', this.onScroll)
}

@Watch('items', { immediate: true })
onItemsChange (_: unknown, oldItems: unknown[]) {
if (this.dynamicScroller) {
const el = this.dynamicScroller.$el

if (this.flipLayout && (this._pauseScroll || !this.$typedState.console.autoScroll)) {
const { scrollHeight, clientHeight } = el

if (scrollHeight > clientHeight) {
this.$nextTick(() => {
el.scrollTop += el.scrollHeight - scrollHeight
})
}
} else {
this.scrollToLatest(oldItems?.length === 0)
}
}
}

updateScrollingPaused () {
this.$nextTick(() => {
const { scrollTop, scrollHeight, clientHeight } = this.dynamicScroller.$el

const pauseScroll = this.flipLayout ? scrollTop > 1 : scrollHeight - scrollTop - clientHeight > 1

if (this._pauseScroll !== pauseScroll) {
this._pauseScroll = pauseScroll
this.scrollingPausedModel = pauseScroll
}
})
}

onScroll () {
this.updateScrollingPaused()
get orderedItems () {
return this.flipLayout
? [...this.items].reverse()
: this.items
}

scrollToLatest (force?: boolean) {
if (this._pauseScroll && !force) return

if (this.dynamicScroller) {
if (
this.$typedState.console.autoScroll ||
this.readonly ||
force
) {
if (this.flipLayout) {
this.dynamicScroller.scrollToItem(0)
} else {
this.dynamicScroller.scrollToBottom()
}
}

if (force) {
// The fixed/floating nature of the console may only change if the scroll is forced.
this.updateScrollingPaused()
}
}
scrollToLatest () {
this.consoleScrollerElement.scrollToLatest(true)
}

sendCommand (command?: string) {
if (command && command.length) {
if (command) {
// If clients detect M112 input from the console, we should invoke the emergency_stop endpoint
if (command.trim().toLowerCase() === 'm112') {
SocketActions.printerEmergencyStop()
Expand Down Expand Up @@ -202,6 +125,7 @@ export default class Console extends Mixins(StateMixin) {
.console-scroller {
height: 300px;
}

.console-scroller-fullscreen {
height: calc(100vh - 260px);
height: calc(100svh - 260px);
Expand All @@ -210,9 +134,4 @@ export default class Console extends Mixins(StateMixin) {
.v-input {
flex: 0 0 auto;
}

:deep(.vue-recycle-scroller__item-wrapper) {
overflow: revert;
}

</style>
Loading