diff --git a/frontend/src/components/utils/observable.ts b/frontend/src/components/utils/observable.ts index 6f73401a..21c0d6d0 100644 --- a/frontend/src/components/utils/observable.ts +++ b/frontend/src/components/utils/observable.ts @@ -20,10 +20,15 @@ export class Observable extends LitElement { @property({ type: Object }) options?: IntersectionObserverInit; - private readonly observable = new ObservableController(this); + private observable?: ObservableController; + + connectedCallback(): void { + super.connectedCallback(); + this.observable = new ObservableController(this, this.options); + } firstUpdated() { - this.observable.observe(this); + this.observable?.observe(this); } render() { diff --git a/frontend/src/features/crawl-workflows/workflow-editor.ts b/frontend/src/features/crawl-workflows/workflow-editor.ts index c7c6f84b..f0ea0d1a 100644 --- a/frontend/src/features/crawl-workflows/workflow-editor.ts +++ b/frontend/src/features/crawl-workflows/workflow-editor.ts @@ -1,6 +1,7 @@ import { consume } from "@lit/context"; import { localized, msg, str } from "@lit/localize"; import type { + SlBlurEvent, SlCheckbox, SlDetails, SlHideEvent, @@ -15,6 +16,7 @@ import Fuse from "fuse.js"; import { mergeDeep } from "immutable"; import type { LanguageCode } from "iso-639-1"; import { + css, html, nothing, type LitElement, @@ -220,6 +222,21 @@ type CrawlConfigResponse = { @customElement("btrix-workflow-editor") @localized() export class WorkflowEditor extends BtrixElement { + static styles = css` + :host { + @keyframes sticky-footer { + from { + transform: translateY(100%); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } + } + } + `; + @consume({ context: proxiesContext, subscribe: true }) private readonly proxies?: ProxiesContext; @@ -258,9 +275,17 @@ export class WorkflowEditor extends BtrixElement { @state() private serverError?: TemplateResult | string; + @state() + private stickyFooter: "animate" | boolean = false; + @state() private isCrawlRunning: boolean | null = this.configId ? null : false; + @state() + private showKeyboardShortcuts = false; + + private saveAndRun = false; + // For observing panel sections position in viewport private readonly observable = new ObservableController(this, { // Add some padding to account for stickied elements @@ -390,6 +415,10 @@ export class WorkflowEditor extends BtrixElement { } } + private animateStickyFooter() { + this.stickyFooter = "animate"; + } + async firstUpdated() { // Observe form sections to get scroll position this.panels?.forEach((panel) => { @@ -399,6 +428,11 @@ export class WorkflowEditor extends BtrixElement { if (this.progressState?.activeTab !== STEPS[0]) { void this.scrollToActivePanel(); } + + // Always show footer when editing or duplicating workflow + if (this.configId || this.hasRequiredFields()) { + this.stickyFooter = true; + } } private initializeEditor() { @@ -433,7 +467,22 @@ export class WorkflowEditor extends BtrixElement { sticky: true, stickyTopClassname: tw`lg:top-16`, })} - ${this.renderFooter()} + { + const [entry] = e.detail.entries; + + if (entry.isIntersecting) { + this.stickyFooter = true; + } + }} + > + ${this.renderFooter()} + `; } @@ -454,7 +503,7 @@ export class WorkflowEditor extends BtrixElement { return html`