diff --git a/panel/models/button.ts b/panel/models/button.ts index 88bac84e38..9021e22d38 100644 --- a/panel/models/button.ts +++ b/panel/models/button.ts @@ -55,7 +55,7 @@ export class ButtonView extends BkButtonView { visible, }) } - let timer: number + let timer: ReturnType | undefined this.el.addEventListener("mouseenter", () => { timer = setTimeout(() => toggle(true), this.model.tooltip_delay) }) diff --git a/panel/models/checkbox_button_group.ts b/panel/models/checkbox_button_group.ts index bc7e88bb2b..0c8eeef6aa 100644 --- a/panel/models/checkbox_button_group.ts +++ b/panel/models/checkbox_button_group.ts @@ -58,7 +58,7 @@ export class CheckboxButtonGroupView extends bkCheckboxButtonGroupView { visible, }) } - let timer: number + let timer: ReturnType | undefined this.el.addEventListener("mouseenter", () => { timer = setTimeout(() => toggle(true), this.model.tooltip_delay) }) diff --git a/panel/models/html.ts b/panel/models/html.ts index d64a133181..53d7bc1fb1 100644 --- a/panel/models/html.ts +++ b/panel/models/html.ts @@ -73,7 +73,7 @@ export function run_scripts(node: Element): void { } function throttle(func: Function, limit: number): any { - let lastFunc: number + let lastFunc: ReturnType | undefined let lastRan: number return function(...args: any) { @@ -85,7 +85,6 @@ function throttle(func: Function, limit: number): any { lastRan = Date.now() } else { clearTimeout(lastFunc) - lastFunc = setTimeout(function() { if ((Date.now() - lastRan) >= limit) { func.apply(context, args) diff --git a/panel/models/icon.ts b/panel/models/icon.ts index e5467f3fb0..45af58693f 100644 --- a/panel/models/icon.ts +++ b/panel/models/icon.ts @@ -90,7 +90,7 @@ export class ClickableIconView extends ControlView { visible, }) } - let timer: number + let timer: ReturnType | undefined this.el.addEventListener("mouseenter", () => { timer = setTimeout(() => toggle_tooltip(true), this.model.tooltip_delay) }) diff --git a/panel/models/radio_button_group.ts b/panel/models/radio_button_group.ts index 59811eefb3..c18c184466 100644 --- a/panel/models/radio_button_group.ts +++ b/panel/models/radio_button_group.ts @@ -58,7 +58,7 @@ export class RadioButtonGroupView extends bkRadioButtonGroupView { visible, }) } - let timer: number + let timer: ReturnType | undefined this.el.addEventListener("mouseenter", () => { timer = setTimeout(() => toggle(true), this.model.tooltip_delay) }) diff --git a/panel/models/tabulator.ts b/panel/models/tabulator.ts index 97485cf6f5..bfdc4c2b63 100644 --- a/panel/models/tabulator.ts +++ b/panel/models/tabulator.ts @@ -359,6 +359,7 @@ export class DataTabulatorView extends HTMLBoxView { _debounced_redraw: any = null _restore_scroll: boolean | "horizontal" | "vertical" = false _updating_scroll: boolean = false + _is_scrolling: boolean = false override connect_signals(): void { super.connect_signals() @@ -514,7 +515,9 @@ export class DataTabulatorView extends HTMLBoxView { override after_resize(): void { super.after_resize() - this._debounced_redraw() + if (!this._is_scrolling) { + this._debounced_redraw() + } } _resize_redraw(): void { @@ -591,12 +594,8 @@ export class DataTabulatorView extends HTMLBoxView { return `${cell.getColumn().getField()}: ${cell.getValue()}` }) this.tabulator.on("scrollVertical", debounce(() => { - this.record_scroll() this.setStyles() }, 50, false)) - this.tabulator.on("scrollHorizontal", debounce(() => { - this.record_scroll() - }, 50, false)) // Sync state with model this.tabulator.on("rowSelectionChanged", (data: any, rows: any, selected: any, deselected: any) => this.rowSelectionChanged(data, rows, selected, deselected)) @@ -649,10 +648,23 @@ export class DataTabulatorView extends HTMLBoxView { this.renderChildren() this.setStyles() + // Track scrolling position and active scroll + const holder = this.shadow_el.querySelector(".tabulator-tableholder") + let scroll_timeout: ReturnType | undefined + if (holder) { + holder.addEventListener("scroll", () => { + this.record_scroll() + this._is_scrolling = true + clearTimeout(scroll_timeout) + scroll_timeout = setTimeout(() => { + this._is_scrolling = false + }, 200) + }) + } + if (this.model.pagination) { if (this.model.page_size == null) { const table = this.shadow_el.querySelector(".tabulator-table") - const holder = this.shadow_el.querySelector(".tabulator-tableholder") if (table != null && holder != null) { const table_height = holder.clientHeight let height = 0 @@ -740,7 +752,6 @@ export class DataTabulatorView extends HTMLBoxView { paginationSize: this.model.page_size || 20, paginationInitialPage: 1, groupBy: this.groupBy, - rowFormatter: (row: any) => this._render_row(row), frozenRows: (row: any) => { return (this.model.frozen_rows.length > 0) ? this.model.frozen_rows.includes(row._row.data._index) : false }, @@ -765,10 +776,17 @@ export class DataTabulatorView extends HTMLBoxView { } } + get_child(idx: number): LayoutDOM | null { + if (this.model.children instanceof Map) { + return this.model.children.get(idx) || null + } + return null + } + override get child_models(): LayoutDOM[] { const children: LayoutDOM[] = [] for (const idx of this.model.expanded) { - const child = this.model.children.get(idx) + const child = this.get_child(idx) if (child != null) { children.push(child) } @@ -790,11 +808,8 @@ export class DataTabulatorView extends HTMLBoxView { } } for (const index of this.model.expanded) { - if (!this.model.children.has(index)) { - continue - } + const model = this.get_child(index) const row = lookup.get(index) - const model = this.model.children.get(index) const view = model == null ? null : this._child_views.get(model) if (view != null) { const render = (new_children as UIElementView[]).includes(view) @@ -811,10 +826,10 @@ export class DataTabulatorView extends HTMLBoxView { _render_row(row: any, resize: boolean = true, render: boolean = true): void { const index = row._row?.data._index - if (!this.model.expanded.includes(index) || this.model.children.get(index) == null) { + if (!this.model.expanded.includes(index)) { return } - const model = this.model.children.get(index) + const model = this.get_child(index) const view = model == null ? null : this._child_views.get(model) if (view == null) { return @@ -823,7 +838,13 @@ export class DataTabulatorView extends HTMLBoxView { const style = getComputedStyle(this.tabulator.element.children[1].children[0]) const bg = style.backgroundColor const neg_margin = rowEl.style.paddingLeft ? `-${rowEl.style.paddingLeft}` : "0" - const viewEl = div({style: {background_color: bg, margin_left: neg_margin, max_width: "100%", overflow_x: "hidden"}}) + const prev_child = rowEl.children[rowEl.children.length-1] + let viewEl + if (prev_child != null && prev_child.className == "row-content") { + viewEl = prev_child + } else { + viewEl = div({class: "row-content", style: {background_color: bg, margin_left: neg_margin, max_width: "100%", overflow_x: "hidden"}}) + } viewEl.appendChild(view.el) rowEl.appendChild(viewEl) if (!view.has_finished() && render) { @@ -851,7 +872,7 @@ export class DataTabulatorView extends HTMLBoxView { } else { const exp_index = expanded.indexOf(index) const removed = expanded.splice(exp_index, 1)[0] - const model = this.model.children.get(removed) + const model = this.get_child(removed) if (model != null) { const view = this._child_views.get(model) if (view !== undefined && view.el != null) { @@ -865,7 +886,7 @@ export class DataTabulatorView extends HTMLBoxView { } let ready = true for (const idx of this.model.expanded) { - if (this.model.children.get(idx) == null) { + if (this.get_child(idx) == null) { ready = false break }