Files
Automaaval/dist/zacatraz/_internal/panel/models/column.ts
T
2026-03-14 21:48:05 +00:00

141 lines
3.7 KiB
TypeScript

import {ModelEvent} from "@bokehjs/core/bokeh_events"
import {div} from "@bokehjs/core/dom"
import type * as p from "@bokehjs/core/properties"
import type {EventCallback} from "@bokehjs/model"
import {Column as BkColumn, ColumnView as BkColumnView} from "@bokehjs/models/layouts/column"
export class ScrollButtonClick extends ModelEvent {
static {
this.prototype.event_name = "scroll_button_click"
}
}
export class ColumnView extends BkColumnView {
declare model: Column
_updating: boolean = false
scroll_down_button_el: HTMLElement
override connect_signals(): void {
super.connect_signals()
const {children, scroll_position, scroll_button_threshold} = this.model.properties
this.on_change(children, () => this.trigger_auto_scroll())
this.on_change(scroll_position, () => this.scroll_to_position())
this.on_change(scroll_button_threshold, () => this.toggle_scroll_button())
}
get distance_from_latest(): number {
return this.el.scrollHeight - this.el.scrollTop - this.el.clientHeight
}
scroll_to_position(): void {
if (this._updating) {
return
}
requestAnimationFrame(() => {
this.el.scrollTo({top: this.model.scroll_position, behavior: "instant"})
})
}
scroll_to_latest(): void {
// Waits for the child to be rendered before scrolling
requestAnimationFrame(() => {
this.model.scroll_position = Math.round(this.el.scrollHeight)
})
}
trigger_auto_scroll(): void {
const limit = this.model.auto_scroll_limit
if (limit == 0) {
return
}
const within_limit = this.distance_from_latest <= limit
if (!within_limit) {
return
}
this.scroll_to_latest()
}
record_scroll_position(): void {
this._updating = true
this.model.scroll_position = Math.round(this.el.scrollTop)
this._updating = false
}
toggle_scroll_button(): void {
const threshold = this.model.scroll_button_threshold
if (!this.scroll_down_button_el || threshold === 0) {
return
}
const exceeds_threshold = this.distance_from_latest >= threshold
this.scroll_down_button_el.classList.toggle("visible", exceeds_threshold)
}
override render(): void {
super.render()
this.scroll_down_button_el = div({class: "scroll-button"})
this.shadow_el.appendChild(this.scroll_down_button_el)
this.el.addEventListener("scroll", () => {
this.record_scroll_position()
this.toggle_scroll_button()
})
this.scroll_down_button_el.addEventListener("click", () => {
this.scroll_to_latest()
this.model.trigger_event(new ScrollButtonClick())
})
}
override after_render(): void {
super.after_render()
requestAnimationFrame(() => {
if (this.model.scroll_position) {
this.scroll_to_position()
}
if (this.model.view_latest) {
this.scroll_to_latest()
}
this.toggle_scroll_button()
})
}
}
export namespace Column {
export type Attrs = p.AttrsOf<Props>
export type Props = BkColumn.Props & {
scroll_position: p.Property<number>
auto_scroll_limit: p.Property<number>
scroll_button_threshold: p.Property<number>
view_latest: p.Property<boolean>
}
}
export interface Column extends Column.Attrs { }
export class Column extends BkColumn {
declare properties: Column.Props
constructor(attrs?: Partial<Column.Attrs>) {
super(attrs)
}
static override __module__ = "panel.models.layout"
static {
this.prototype.default_view = ColumnView
this.define<Column.Props>(({Int, Bool}) => ({
scroll_position: [Int, 0],
auto_scroll_limit: [Int, 0],
scroll_button_threshold: [Int, 0],
view_latest: [Bool, false],
}))
}
on_click(callback: EventCallback<ScrollButtonClick>): void {
this.on_event(ScrollButtonClick, callback)
}
}