1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 08:33:31 +01:00

Allow trace graph to scroll independently of the step-details tab (#29906)

* Allow trace graph to scroll independently

* Apply independent trace graph scrolling to script trace

Port the independent column scrolling and sticky nav overlay from
ha-automation-trace to ha-script-trace, and add scroll-to-active-node
behavior to hat-script-graph.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Re-encapsulate nav buttons inside hat-script-graph

Move the up/down navigation buttons back into hat-script-graph where they
belong, restoring _previousTrackedNode/_nextTrackedNode as private. Wrap
the graph content in a div.graph-scroll so it scrolls independently while
the button column stays fixed — the same flex-row layout the parent was
using, now self-contained inside the shadow DOM.

This removes the duplicated nav overlay markup and disabled-state logic
from ha-automation-trace and ha-script-trace, and removes the unnecessary
public surface on HatScriptGraph.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Make not-scrollable more specific.

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
This commit is contained in:
Johan Henkens
2026-03-11 06:08:10 -07:00
committed by GitHub
parent 0844d9c2c0
commit 8bcccb5792
4 changed files with 121 additions and 32 deletions

View File

@@ -597,28 +597,30 @@ export class HatScriptGraph extends LitElement {
: undefined;
try {
return html`
<div class="parent graph-container">
${triggerNodes
? html`<hat-graph-branch start .short=${triggerNodes.length < 2}>
${triggerNodes}
</hat-graph-branch>`
: ""}
${conditionKey in this.trace.config
? html`${ensureArray(this.trace.config[conditionKey])?.map(
(condition, i) => this._renderCondition(condition, i)
)}`
: ""}
${actionKey in this.trace.config
? html`${ensureArray(this.trace.config[actionKey]).map(
(action, i) => this._renderActionNode(action, `action/${i}`)
)}`
: ""}
${"sequence" in this.trace.config
? html`${ensureArray<Action>(this.trace.config.sequence).map(
(action, i) =>
this._renderActionNode(action, `sequence/${i}`, i === 0)
)}`
: ""}
<div class="graph-scroll ha-scrollbar">
<div class="parent graph-container">
${triggerNodes
? html`<hat-graph-branch start .short=${triggerNodes.length < 2}>
${triggerNodes}
</hat-graph-branch>`
: ""}
${conditionKey in this.trace.config
? html`${ensureArray(this.trace.config[conditionKey])?.map(
(condition, i) => this._renderCondition(condition, i)
)}`
: ""}
${actionKey in this.trace.config
? html`${ensureArray(this.trace.config[actionKey]).map(
(action, i) => this._renderActionNode(action, `action/${i}`)
)}`
: ""}
${"sequence" in this.trace.config
? html`${ensureArray<Action>(this.trace.config.sequence).map(
(action, i) =>
this._renderActionNode(action, `sequence/${i}`, i === 0)
)}`
: ""}
</div>
</div>
<div class="actions">
<ha-icon-button
@@ -659,6 +661,20 @@ export class HatScriptGraph extends LitElement {
protected updated(changedProps: PropertyValues<this>) {
super.updated(changedProps);
if (!changedProps.has("trace") && !changedProps.has("selected")) {
return;
}
// Scroll to active node when selection changes
if (changedProps.has("selected")) {
const activeNode = this.renderRoot.querySelector(
"hat-graph-node[active], hat-graph-branch[active]"
) as HTMLElement;
if (activeNode) {
activeNode.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
}
if (!changedProps.has("trace")) {
return;
}
@@ -717,6 +733,8 @@ export class HatScriptGraph extends LitElement {
return css`
:host {
display: flex;
flex-direction: row;
overflow: hidden;
--stroke-clr: var(--stroke-color, var(--secondary-text-color));
--active-clr: var(--active-color, var(--primary-color));
--track-clr: var(--track-color, var(--accent-color));
@@ -734,6 +752,11 @@ export class HatScriptGraph extends LitElement {
--hat-graph-node-size: ${NODE_SIZE}px;
--hat-graph-branch-height: ${BRANCH_HEIGHT}px;
}
.graph-scroll {
flex: 1;
overflow: auto;
min-width: 0;
}
.graph-container {
display: flex;
flex-direction: column;

View File

@@ -23,6 +23,8 @@ class HassSubpage extends LitElement {
@property({ type: Boolean, reflect: true }) public narrow = false;
@property({ type: Boolean }) public scrollable = true;
// @ts-ignore
@restoreScroll(".content") private _savedScrollPos?: number;
@@ -57,7 +59,14 @@ class HassSubpage extends LitElement {
<slot name="toolbar-icon"></slot>
</div>
</div>
<div class="content ha-scrollbar" @scroll=${this._saveScrollPos}>
<div
class=${classMap({
content: true,
"ha-scrollbar": this.scrollable,
"not-scrollable": !this.scrollable,
})}
@scroll=${this._saveScrollPos}
>
<slot></slot>
</div>
<div id="fab">
@@ -163,6 +172,12 @@ class HassSubpage extends LitElement {
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.content.not-scrollable {
overflow: hidden;
display: flex;
flex-direction: column;
}
:host([narrow]) .content {
width: calc(
100% - var(--safe-area-inset-left, 0px) - var(

View File

@@ -101,7 +101,12 @@ export class HaAutomationTrace extends LitElement {
return html`
${devButtons}
<hass-subpage .hass=${this.hass} .narrow=${this.narrow} .header=${title}>
<hass-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.header=${title}
.scrollable=${this.narrow}
>
${!this.narrow && stateObj?.attributes.id
? html`
<ha-button
@@ -558,15 +563,19 @@ export class HaAutomationTrace extends LitElement {
}
.main {
min-height: calc(100% - var(--header-height));
flex: 1;
min-height: 0;
display: flex;
overflow: hidden;
background-color: var(--card-background-color);
direction: ltr;
}
:host([narrow]) .main {
flex: none;
height: auto;
flex-direction: column;
overflow: visible;
}
.container {
@@ -575,19 +584,34 @@ export class HaAutomationTrace extends LitElement {
.graph {
border-right: 1px solid var(--divider-color);
overflow-x: auto;
max-width: 50%;
padding-bottom: 16px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
hat-script-graph {
flex: 1;
min-width: 0;
min-height: 0;
}
:host([narrow]) .graph {
max-width: 100%;
justify-content: center;
display: flex;
overflow: visible;
height: auto;
}
:host([narrow]) hat-script-graph {
overflow: visible;
flex: none;
}
.info {
flex: 1;
overflow-y: auto;
background-color: var(--card-background-color);
}
:host([narrow]) .info {
overflow: visible;
}
.trace-link {
text-decoration: none;
}

View File

@@ -103,7 +103,12 @@ export class HaScriptTrace extends LitElement {
return html`
${devButtons}
<hass-subpage .hass=${this.hass} .narrow=${this.narrow} .header=${title}>
<hass-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.header=${title}
.scrollable=${this.narrow}
>
${!this.narrow && this.scriptId
? html`
<ha-button
@@ -565,14 +570,18 @@ export class HaScriptTrace extends LitElement {
}
.main {
min-height: calc(100% - var(--header-height));
flex: 1;
min-height: 0;
display: flex;
overflow: hidden;
background-color: var(--card-background-color);
}
:host([narrow]) .main {
flex: none;
height: auto;
flex-direction: column;
overflow: visible;
}
.container {
@@ -581,16 +590,34 @@ export class HaScriptTrace extends LitElement {
.graph {
border-right: 1px solid var(--divider-color);
overflow-x: auto;
max-width: 50%;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
hat-script-graph {
flex: 1;
min-width: 0;
min-height: 0;
}
:host([narrow]) .graph {
max-width: 100%;
overflow: visible;
height: auto;
}
:host([narrow]) hat-script-graph {
overflow: visible;
flex: none;
}
.info {
flex: 1;
overflow-y: auto;
background-color: var(--card-background-color);
}
:host([narrow]) .info {
overflow: visible;
}
.trace-link {
text-decoration: none;
}