Skip to content

Commit

Permalink
Replace LinkInterceptor with LinkClickObserver
Browse files Browse the repository at this point in the history
Follow-up to hotwired#382

---

In the same style as [hotwired#382][], replace instances of
`LinkInterceptor` with `LinkClickObserver`, making the necessary
interface changes in classes that used to extend
`LinkInterceptorDelegate`.

Conditional logic that was once covered in the `LinkInterceptor` is
moved into the predicate methods of the delegates (for example, the
`FrameController.willFollowLinkToLocation` and
`FrameRedirector.willFollowLinkToLocation`).

Since the recently introduced [FormLinkInterceptor][] was named after
the `LinkInterceptor`, this commit also renames that class (and its call
sites) to match the `LinkClickObserver`-suffix, along with the
`LinkInterceptorDelegate` method structure.

[hotwired#382]: hotwired#382
[FormLinkInterceptor]: hotwired@f8a94c5
  • Loading branch information
seanpdoyle committed Jul 19, 2022
1 parent 5a2029a commit 719137f
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 162 deletions.
40 changes: 20 additions & 20 deletions src/core/frames/frame_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { ViewDelegate, ViewRenderOptions } from "../view"
import { getAction, expandURL, urlsAreEqual, locationIsVisitable } from "../url"
import { FormSubmitObserver, FormSubmitObserverDelegate } from "../../observers/form_submit_observer"
import { FrameView } from "./frame_view"
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor"
import { FormLinkInterceptor, FormLinkInterceptorDelegate } from "../../observers/form_link_interceptor"
import { LinkClickObserver, LinkClickObserverDelegate } from "../../observers/link_click_observer"
import { FormLinkClickObserver, FormLinkClickObserverDelegate } from "../../observers/form_link_click_observer"
import { FrameRenderer } from "./frame_renderer"
import { session } from "../index"
import { isAction } from "../types"
Expand All @@ -28,15 +28,15 @@ export class FrameController
FormSubmitObserverDelegate,
FormSubmissionDelegate,
FrameElementDelegate,
FormLinkInterceptorDelegate,
LinkInterceptorDelegate,
FormLinkClickObserverDelegate,
LinkClickObserverDelegate,
ViewDelegate<FrameElement, Snapshot<FrameElement>>
{
readonly element: FrameElement
readonly view: FrameView
readonly appearanceObserver: AppearanceObserver
readonly formLinkInterceptor: FormLinkInterceptor
readonly linkInterceptor: LinkInterceptor
readonly formLinkClickObserver: FormLinkClickObserver
readonly linkClickObserver: LinkClickObserver
readonly formSubmitObserver: FormSubmitObserver
formSubmission?: FormSubmission
fetchResponseLoaded = (_fetchResponse: FetchResponse) => {}
Expand All @@ -51,8 +51,8 @@ export class FrameController
this.element = element
this.view = new FrameView(this, this.element)
this.appearanceObserver = new AppearanceObserver(this, this.element)
this.formLinkInterceptor = new FormLinkInterceptor(this, this.element)
this.linkInterceptor = new LinkInterceptor(this, this.element)
this.formLinkClickObserver = new FormLinkClickObserver(this, this.element)
this.linkClickObserver = new LinkClickObserver(this, this.element)
this.formSubmitObserver = new FormSubmitObserver(this, this.element)
}

Expand All @@ -64,8 +64,8 @@ export class FrameController
} else {
this.loadSourceURL()
}
this.formLinkInterceptor.start()
this.linkInterceptor.start()
this.formLinkClickObserver.start()
this.linkClickObserver.start()
this.formSubmitObserver.start()
}
}
Expand All @@ -74,8 +74,8 @@ export class FrameController
if (this.connected) {
this.connected = false
this.appearanceObserver.stop()
this.formLinkInterceptor.stop()
this.linkInterceptor.stop()
this.formLinkClickObserver.stop()
this.linkClickObserver.stop()
this.formSubmitObserver.stop()
}
}
Expand Down Expand Up @@ -161,28 +161,28 @@ export class FrameController
this.loadSourceURL()
}

// Form link interceptor delegate
// Form link click observer delegate

shouldInterceptFormLinkClick(link: Element): boolean {
willSubmitFormLinkClickToLocation(link: Element): boolean {
return this.shouldInterceptNavigation(link)
}

formLinkClickIntercepted(link: Element, form: HTMLFormElement): void {
submittedFormLinkToLocation(link: Element, _location: URL, form: HTMLFormElement): void {
const frame = this.findFrameElement(link)
if (frame) form.setAttribute("data-turbo-frame", frame.id)
}

// Link interceptor delegate
// Link click observer delegate

shouldInterceptLinkClick(element: Element, _url: string) {
willFollowLinkToLocation(element: Element) {
return this.shouldInterceptNavigation(element)
}

linkClickIntercepted(element: Element, url: string) {
this.navigateFrame(element, url)
followedLinkToLocation(element: Element, location: URL) {
this.navigateFrame(element, location.href)
}

// Form interceptor delegate
// Form submit observer delegate

willSubmitForm(element: HTMLFormElement, submitter?: HTMLElement) {
return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter)
Expand Down
18 changes: 9 additions & 9 deletions src/core/frames/frame_redirector.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
import { FormSubmitObserver, FormSubmitObserverDelegate } from "../../observers/form_submit_observer"
import { FrameElement } from "../../elements/frame_element"
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor"
import { expandURL, getAction, locationIsVisitable } from "../url"
import { LinkClickObserver, LinkClickObserverDelegate } from "../../observers/link_click_observer"

export class FrameRedirector implements LinkInterceptorDelegate, FormSubmitObserverDelegate {
export class FrameRedirector implements LinkClickObserverDelegate, FormSubmitObserverDelegate {
readonly element: Element
readonly linkInterceptor: LinkInterceptor
readonly linkClickObserver: LinkClickObserver
readonly formSubmitObserver: FormSubmitObserver

constructor(element: Element) {
this.element = element
this.linkInterceptor = new LinkInterceptor(this, element)
this.linkClickObserver = new LinkClickObserver(this, element)
this.formSubmitObserver = new FormSubmitObserver(this, element)
}

start() {
this.linkInterceptor.start()
this.linkClickObserver.start()
this.formSubmitObserver.start()
}

stop() {
this.linkInterceptor.stop()
this.linkClickObserver.stop()
this.formSubmitObserver.stop()
}

shouldInterceptLinkClick(element: Element, _url: string) {
willFollowLinkToLocation(element: Element) {
return this.shouldRedirect(element)
}

linkClickIntercepted(element: Element, url: string) {
followedLinkToLocation(element: Element, url: URL) {
const frame = this.findFrameElement(element)
if (frame) {
frame.delegate.linkClickIntercepted(element, url)
frame.delegate.followedLinkToLocation(element, url)
}
}

Expand Down
57 changes: 0 additions & 57 deletions src/core/frames/link_interceptor.ts

This file was deleted.

22 changes: 11 additions & 11 deletions src/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormSubmitObserver, FormSubmitObserverDelegate } from "../observers/for
import { FrameRedirector } from "./frames/frame_redirector"
import { History, HistoryDelegate } from "./drive/history"
import { LinkClickObserver, LinkClickObserverDelegate } from "../observers/link_click_observer"
import { FormLinkInterceptor, FormLinkInterceptorDelegate } from "../observers/form_link_interceptor"
import { FormLinkClickObserver, FormLinkClickObserverDelegate } from "../observers/form_link_click_observer"
import { getAction, expandURL, locationIsVisitable, Locatable } from "./url"
import { Navigator, NavigatorDelegate } from "./drive/navigator"
import { PageObserver, PageObserverDelegate } from "../observers/page_observer"
Expand Down Expand Up @@ -38,7 +38,7 @@ export class Session
implements
FormSubmitObserverDelegate,
HistoryDelegate,
FormLinkInterceptorDelegate,
FormLinkClickObserverDelegate,
LinkClickObserverDelegate,
NavigatorDelegate,
PageObserverDelegate,
Expand All @@ -53,11 +53,11 @@ export class Session

readonly pageObserver = new PageObserver(this)
readonly cacheObserver = new CacheObserver()
readonly linkClickObserver = new LinkClickObserver(this)
readonly linkClickObserver = new LinkClickObserver(this, document)
readonly formSubmitObserver = new FormSubmitObserver(this, document)
readonly scrollObserver = new ScrollObserver(this)
readonly streamObserver = new StreamObserver(this)
readonly formLinkInterceptor = new FormLinkInterceptor(this, document.documentElement)
readonly formLinkClickObserver = new FormLinkClickObserver(this, document)
readonly frameRedirector = new FrameRedirector(document.documentElement)

drive = true
Expand All @@ -70,12 +70,12 @@ export class Session
if (!this.started) {
this.pageObserver.start()
this.cacheObserver.start()
this.formLinkInterceptor.start()
this.frameRedirector.start()
this.formLinkClickObserver.start()
this.linkClickObserver.start()
this.formSubmitObserver.start()
this.scrollObserver.start()
this.streamObserver.start()
this.frameRedirector.start()
this.history.start()
this.preloader.start()
this.started = true
Expand All @@ -91,12 +91,12 @@ export class Session
if (this.started) {
this.pageObserver.stop()
this.cacheObserver.stop()
this.formLinkInterceptor.stop()
this.frameRedirector.stop()
this.formLinkClickObserver.stop()
this.linkClickObserver.stop()
this.formSubmitObserver.stop()
this.scrollObserver.stop()
this.streamObserver.stop()
this.frameRedirector.stop()
this.history.stop()
this.started = false
}
Expand Down Expand Up @@ -163,13 +163,13 @@ export class Session
this.history.updateRestorationData({ scrollPosition: position })
}

// Form link interceptor delegate
// Form link click observer delegate

shouldInterceptFormLinkClick(_link: Element): boolean {
willSubmitFormLinkClickToLocation(): boolean {
return true
}

formLinkClickIntercepted(_link: Element, _form: HTMLFormElement) {}
submittedFormLinkToLocation() {}

// Link click observer delegate

Expand Down
4 changes: 2 additions & 2 deletions src/elements/frame_element.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FetchResponse } from "../http/fetch_response"
import { Snapshot } from "../core/snapshot"
import { LinkInterceptorDelegate } from "../core/frames/link_interceptor"
import { LinkClickObserverDelegate } from "../observers/link_click_observer"
import { FormSubmitObserverDelegate } from "../observers/form_submit_observer"

export enum FrameLoadingStyle {
Expand All @@ -10,7 +10,7 @@ export enum FrameLoadingStyle {

export type FrameElementObservedAttribute = keyof FrameElement & ("disabled" | "complete" | "loading" | "src")

export interface FrameElementDelegate extends LinkInterceptorDelegate, FormSubmitObserverDelegate {
export interface FrameElementDelegate extends LinkClickObserverDelegate, FormSubmitObserverDelegate {
connect(): void
disconnect(): void
completeChanged(): void
Expand Down
56 changes: 56 additions & 0 deletions src/observers/form_link_click_observer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { LinkClickObserver, LinkClickObserverDelegate } from "./link_click_observer"

export type FormLinkClickObserverDelegate = {
willSubmitFormLinkClickToLocation(link: Element, location: URL, event: MouseEvent): boolean
submittedFormLinkToLocation(link: Element, location: URL, form: HTMLFormElement): void
}

export class FormLinkClickObserver implements LinkClickObserverDelegate {
readonly linkClickObserver: LinkClickObserver
readonly delegate: FormLinkClickObserverDelegate

constructor(delegate: FormLinkClickObserverDelegate, eventTarget: EventTarget) {
this.delegate = delegate
this.linkClickObserver = new LinkClickObserver(this, eventTarget)
}

start() {
this.linkClickObserver.start()
}

stop() {
this.linkClickObserver.stop()
}

willFollowLinkToLocation(link: Element, action: URL, originalEvent: MouseEvent): boolean {
return (
this.delegate.willSubmitFormLinkClickToLocation(link, action, originalEvent) &&
(link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream"))
)
}

followedLinkToLocation(link: Element, action: URL): void {
const form = document.createElement("form")
form.setAttribute("data-turbo", "true")
form.setAttribute("action", action.href)
form.setAttribute("hidden", "")

const method = link.getAttribute("data-turbo-method")
if (method) form.setAttribute("method", method)

const turboFrame = link.getAttribute("data-turbo-frame")
if (turboFrame) form.setAttribute("data-turbo-frame", turboFrame)

const turboConfirm = link.getAttribute("data-turbo-confirm")
if (turboConfirm) form.setAttribute("data-turbo-confirm", turboConfirm)

const turboStream = link.hasAttribute("data-turbo-stream")
if (turboStream) form.setAttribute("data-turbo-stream", "")

this.delegate.submittedFormLinkToLocation(link, action, form)

document.body.appendChild(form)
form.requestSubmit()
form.remove()
}
}
Loading

0 comments on commit 719137f

Please sign in to comment.