PageSourceSearch

https://freshjots.com/assets/controllers/blog_form_controller-655abd10.js

js freshjots.com collected 2026-10-04 22:56:21 UTC 2,541 bytes, 68 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// Form-wide ergonomics for the blog post editor:
4// 1. Warn before leaving when there are unsaved changes.
5// 2. On page load with a validation error block present, smooth-scroll the
6//    first errored field into view.
7// 3. Derived post-title readout — reflects the first Title section's current
8//    Trix content so the admin can see what the saved title will be.
9export default class extends Controller {
10  static targets = ["errorBlock", "titleReadout"]
11
12  connect() {
13    this.dirty = false
14    this.boundMarkDirty = () => { this.dirty = true }
15    this.boundTrixChange = this.handleTrixChange.bind(this)
16    this.boundBeforeUnload = this.handleBeforeUnload.bind(this)
17    this.boundSubmit = () => { this.dirty = false }
18
19    this.element.addEventListener("input", this.boundMarkDirty)
20    this.element.addEventListener("change", this.boundMarkDirty)
21    this.element.addEventListener("submit", this.boundSubmit)
22    document.addEventListener("trix-change", this.boundTrixChange)
23    window.addEventListener("beforeunload", this.boundBeforeUnload)
24
25    this.#scrollToFirstError()
26    this.#updateTitleReadout()
27  }
28
29  disconnect() {
30    this.element.removeEventListener("input", this.boundMarkDirty)
31    this.element.removeEventListener("change", this.boundMarkDirty)
32    this.element.removeEventListener("submit", this.boundSubmit)
33    document.removeEventListener("trix-change", this.boundTrixChange)
34    window.removeEventListener("beforeunload", this.boundBeforeUnload)
35  }
36
37  handleBeforeUnload(event) {
38    if (!this.dirty) return
39    event.preventDefault()
40    event.returnValue = ""
41  }
42
43  handleTrixChange() {
44    this.dirty = true
45    this.#updateTitleReadout()
46  }
47
48  #scrollToFirstError() {
49    if (!this.hasErrorBlockTarget) return
50    const errored = this.element.querySelector(".input-field-error, trix-editor.ring-red-300")
51    errored?.scrollIntoView({ behavior: "smooth", block: "center" })
52  }
53
54  #updateTitleReadout() {
55    if (!this.hasTitleReadoutTarget) return
56    const sections = this.element.querySelectorAll("[data-section]")
57    let text = ""
58    for (const section of sections) {
59      const kindInput = section.querySelector("input[name*='[kind]']")
60      if (kindInput && kindInput.value === "title") {
61        const editor = section.querySelector("trix-editor")
62        text = (editor && editor.innerText ? editor.innerText : "").trim()
63        if (text) break
64      }
65    }
66    this.titleReadoutTarget.textContent = text ? `📄 ${text}` : ""
67  }
68}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.