Skip to content

Commit

Permalink
Replace FormInterceptor with FormSubmitObserver
Browse files Browse the repository at this point in the history
Unify `<form>` element submission event listening by replacing all
`FormInterceptor` call sites and `FormInterceptorDelegate` implementers,
replacing them with the `FormSubmitObserver` pattern.
  • Loading branch information
seanpdoyle committed Nov 11, 2021
1 parent 274d369 commit b87e1e1
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 62 deletions.
34 changes: 0 additions & 34 deletions src/core/frames/form_interceptor.ts

This file was deleted.

19 changes: 10 additions & 9 deletions src/core/frames/frame_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ import { FetchMethod, FetchRequest, FetchRequestDelegate, FetchRequestHeaders }
import { FetchResponse } from "../../http/fetch_response"
import { AppearanceObserver, AppearanceObserverDelegate } from "../../observers/appearance_observer"
import { getAttribute, parseHTMLDocument } from "../../util"
import { FormSubmitObserver, FormSubmitObserverDelegate } from "../../observers/form_submit_observer"
import { FormSubmission, FormSubmissionDelegate } from "../drive/form_submission"
import { Snapshot } from "../snapshot"
import { ViewDelegate } from "../view"
import { getAction, expandURL, urlsAreEqual, locationIsVisitable, Locatable } from "../url"
import { FormInterceptor, FormInterceptorDelegate } from "./form_interceptor"
import { FrameView } from "./frame_view"
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor"
import { FrameRenderer } from "./frame_renderer"
import { session } from "../index"
import { isAction } from "../types"

export class FrameController implements AppearanceObserverDelegate, FetchRequestDelegate, FormInterceptorDelegate, FormSubmissionDelegate, FrameElementDelegate, LinkInterceptorDelegate, ViewDelegate<Snapshot<FrameElement>> {
export class FrameController implements AppearanceObserverDelegate, FetchRequestDelegate, FormSubmitObserverDelegate, FormSubmissionDelegate, FrameElementDelegate, LinkInterceptorDelegate, ViewDelegate<Snapshot<FrameElement>> {
readonly element: FrameElement
readonly view: FrameView
readonly appearanceObserver: AppearanceObserver
readonly linkInterceptor: LinkInterceptor
readonly formInterceptor: FormInterceptor
readonly formSubmitObserver: FormSubmitObserver
currentURL?: string | null
formSubmission?: FormSubmission
private currentFetchRequest: FetchRequest | null = null
Expand All @@ -33,7 +33,7 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest
this.view = new FrameView(this, this.element)
this.appearanceObserver = new AppearanceObserver(this, this.element)
this.linkInterceptor = new LinkInterceptor(this, this.element)
this.formInterceptor = new FormInterceptor(this, this.element)
this.formSubmitObserver = new FormSubmitObserver(this, this.element)
}

connect() {
Expand All @@ -44,7 +44,7 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest
this.appearanceObserver.start()
}
this.linkInterceptor.start()
this.formInterceptor.start()
this.formSubmitObserver.start()
this.sourceURLChanged()
}
}
Expand All @@ -54,7 +54,7 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest
this.connected = false
this.appearanceObserver.stop()
this.linkInterceptor.stop()
this.formInterceptor.stop()
this.formSubmitObserver.stop()
}
}

Expand Down Expand Up @@ -142,11 +142,12 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest

// Form interceptor delegate

shouldInterceptFormSubmission(element: HTMLFormElement, submitter?: HTMLElement) {
return this.shouldInterceptNavigation(element, submitter)
willSubmitForm(element: HTMLFormElement, submitter?: HTMLElement) {
return element.closest("turbo-frame") == this.element &&
this.shouldInterceptNavigation(element, submitter)
}

formSubmissionIntercepted(element: HTMLFormElement, submitter?: HTMLElement) {
formSubmitted(element: HTMLFormElement, submitter?: HTMLElement) {
if (this.formSubmission) {
this.formSubmission.stop()
}
Expand Down
22 changes: 12 additions & 10 deletions src/core/frames/frame_redirector.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { FormInterceptor, FormInterceptorDelegate } from "./form_interceptor"
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"

export class FrameRedirector implements LinkInterceptorDelegate, FormInterceptorDelegate {
export class FrameRedirector implements LinkInterceptorDelegate, FormSubmitObserverDelegate {
readonly element: Element
readonly linkInterceptor: LinkInterceptor
readonly formInterceptor: FormInterceptor
readonly formSubmitObserver: FormSubmitObserver

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

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

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

shouldInterceptLinkClick(element: Element, url: string) {
Expand All @@ -35,15 +35,17 @@ export class FrameRedirector implements LinkInterceptorDelegate, FormInterceptor
}
}

shouldInterceptFormSubmission(element: HTMLFormElement, submitter?: HTMLElement) {
return this.shouldSubmit(element, submitter)
willSubmitForm(element: HTMLFormElement, submitter?: HTMLElement) {
return element.closest("turbo-frame") == null &&
this.shouldSubmit(element, submitter) &&
this.shouldRedirect(element, submitter)
}

formSubmissionIntercepted(element: HTMLFormElement, submitter?: HTMLElement) {
formSubmitted(element: HTMLFormElement, submitter?: HTMLElement) {
const frame = this.findFrameElement(element, submitter)
if (frame) {
frame.removeAttribute("reloadable")
frame.delegate.formSubmissionIntercepted(element, submitter)
frame.delegate.formSubmitted(element, submitter)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin
readonly pageObserver = new PageObserver(this)
readonly cacheObserver = new CacheObserver()
readonly linkClickObserver = new LinkClickObserver(this)
readonly formSubmitObserver = new FormSubmitObserver(this)
readonly formSubmitObserver = new FormSubmitObserver(this, document)
readonly scrollObserver = new ScrollObserver(this)
readonly streamObserver = new StreamObserver(this)

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

export enum FrameLoadingStyle { eager = "eager", lazy = "lazy" }

export interface FrameElementDelegate {
export interface FrameElementDelegate extends LinkInterceptorDelegate, FormSubmitObserverDelegate {
connect(): void
disconnect(): void
loadingStyleChanged(): void
sourceURLChanged(): void
disabledChanged(): void
formSubmissionIntercepted(element: HTMLFormElement, submitter?: HTMLElement): void
linkClickIntercepted(element: Element, url: string): void
loadResponse(response: FetchResponse): void
isLoading: boolean
}
Expand Down
12 changes: 7 additions & 5 deletions src/observers/form_submit_observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,31 @@ export interface FormSubmitObserverDelegate {

export class FormSubmitObserver {
readonly delegate: FormSubmitObserverDelegate
readonly eventTarget: EventTarget
started = false

constructor(delegate: FormSubmitObserverDelegate) {
constructor(delegate: FormSubmitObserverDelegate, eventTarget: EventTarget) {
this.delegate = delegate
this.eventTarget = eventTarget
}

start() {
if (!this.started) {
addEventListener("submit", this.submitCaptured, true)
this.eventTarget.addEventListener("submit", this.submitCaptured, true)
this.started = true
}
}

stop() {
if (this.started) {
removeEventListener("submit", this.submitCaptured, true)
this.eventTarget.removeEventListener("submit", this.submitCaptured, true)
this.started = false
}
}

submitCaptured = () => {
removeEventListener("submit", this.submitBubbled, false)
addEventListener("submit", this.submitBubbled, false)
this.eventTarget.removeEventListener("submit", this.submitBubbled, false)
this.eventTarget.addEventListener("submit", this.submitBubbled, false)
}

submitBubbled = <EventListener>((event: SubmitEvent) => {
Expand Down

0 comments on commit b87e1e1

Please sign in to comment.