PageSourceSearch

https://railsblocks.com/assets/controllers/confirmation_controller…45a6e47571543645bada92ec0ec54d9044c77b3f.js

js railsblocks.com collected 2026-09-25 20:23:02 UTC 5,516 bytes, 205 lines download raw bytes

1import { Controller } from "@hotwired/stimulus";
2
3export default class extends Controller {
4  static targets = ["input", "confirmButton", "checkbox"];
5  static values = {
6    confirmText: { type: String, default: "" },
7    caseSensitive: { type: Boolean, default: false },
8  };
9
10  connect() {
11    this.confirmed = false;
12    this.confirmTextArray = this.parseConfirmText();
13    this.updateConfirmButton();
14
15    // Add form submission prevention
16    const form = this.element.closest("form");
17    if (form) {
18      this.boundHandleSubmit = this.handleSubmit.bind(this);
19      form.addEventListener("submit", this.boundHandleSubmit);
20    }
21
22    // Add input listeners
23    if (this.hasInputTarget) {
24      this.inputTarget.addEventListener("input", () => this.checkConfirmation());
25      this.inputTarget.addEventListener("keydown", (event) => {
26        if (event.key === "Enter") {
27          if (this.confirmed) {
28            event.preventDefault();
29            this.confirm();
30          } else {
31            // Prevent form submission when confirmation is not valid
32            event.preventDefault();
33          }
34        }
35      });
36    }
37
38    // Add checkbox listeners
39    if (this.hasCheckboxTarget) {
40      this.checkboxTargets.forEach((checkbox) => {
41        checkbox.addEventListener("change", () => this.checkConfirmation());
42      });
43    }
44  }
45
46  disconnect() {
47    // Remove form submission listener
48    const form = this.element.closest("form");
49    if (form && this.boundHandleSubmit) {
50      form.removeEventListener("submit", this.boundHandleSubmit);
51    }
52
53    if (this.hasInputTarget) {
54      this.inputTarget.removeEventListener("input", () => this.checkConfirmation());
55    }
56
57    if (this.hasCheckboxTarget) {
58      this.checkboxTargets.forEach((checkbox) => {
59        checkbox.removeEventListener("change", () => this.checkConfirmation());
60      });
61    }
62  }
63
64  parseConfirmText() {
65    if (!this.confirmTextValue) return [];
66
67    // Simple comma-separated approach for arrays
68    if (this.confirmTextValue.includes(",")) {
69      return this.confirmTextValue
70        .split(",")
71        .map((item) => item.trim())
72        .filter((item) => item.length > 0);
73    }
74
75    return [this.confirmTextValue];
76  }
77
78  checkConfirmation() {
79    // Check text confirmation
80    let textConfirmed = false;
81    if (this.hasInputTarget && this.confirmTextArray.length > 0) {
82      const inputValue = this.caseSensitiveValue ? this.inputTarget.value : this.inputTarget.value.toLowerCase();
83
84      if (this.confirmTextArray.length === 1) {
85        const confirmValue = this.caseSensitiveValue
86          ? this.confirmTextArray[0]
87          : this.confirmTextArray[0].toLowerCase();
88        textConfirmed = inputValue === confirmValue;
89      } else {
90        textConfirmed = this.confirmTextArray.some((text) => {
91          const confirmValue = this.caseSensitiveValue ? text : text.toLowerCase();
92          return inputValue === confirmValue;
93        });
94      }
95    } else {
96      textConfirmed = this.hasInputTarget ? this.inputTarget.value.trim().length > 0 : true;
97    }
98
99    // Check checkbox confirmation
100    let checkboxConfirmed = true;
101    if (this.hasCheckboxTarget && this.checkboxTargets.length > 0) {
102      checkboxConfirmed = this.checkboxTargets.every((checkbox) => checkbox.checked);
103    }
104
105    this.confirmed = textConfirmed && checkboxConfirmed;
106    this.updateConfirmButton();
107  }
108
109  updateConfirmButton() {
110    if (this.hasConfirmButtonTarget) {
111      this.confirmButtonTarget.disabled = !this.confirmed;
112
113      if (this.confirmed) {
114        this.confirmButtonTarget.classList.remove("opacity-50", "cursor-not-allowed");
115      } else {
116        this.confirmButtonTarget.classList.add("opacity-50", "cursor-not-allowed");
117      }
118    }
119  }
120
121  confirm() {
122    if (!this.confirmed) return false;
123
124    const beforeConfirmEvent = new CustomEvent("confirmation:before", {
125      detail: {
126        value: this.getConfirmationValue(),
127        controller: this,
128      },
129      cancelable: true,
130    });
131
132    if (!this.element.dispatchEvent(beforeConfirmEvent)) {
133      return false;
134    }
135
136    this.performConfirmation();
137
138    this.element.dispatchEvent(
139      new CustomEvent("confirmation:confirmed", {
140        detail: {
141          value: this.getConfirmationValue(),
142          controller: this,
143        },
144      }),
145    );
146
147    return true;
148  }
149
150  handleSubmit(event) {
151    // Only allow form submission if confirmation is valid
152    if (!this.confirmed) {
153      event.preventDefault();
154      event.stopPropagation();
155      return false;
156    }
157  }
158
159  cancel() {
160    this.reset();
161    this.element.dispatchEvent(
162      new CustomEvent("confirmation:cancelled", {
163        detail: { controller: this },
164      }),
165    );
166  }
167
168  reset() {
169    this.confirmed = false;
170
171    if (this.hasInputTarget) {
172      this.inputTarget.value = "";
173    }
174
175    if (this.hasCheckboxTarget) {
176      this.checkboxTargets.forEach((checkbox) => {
177        checkbox.checked = false;
178      });
179    }
180
181    this.updateConfirmButton();
182  }
183
184  performConfirmation() {
185    const form = this.element.closest("form");
186    if (form) {
187      form.submit();
188    }
189  }
190
191  getConfirmationValue() {
192    const textValue = this.hasInputTarget ? this.inputTarget.value : "";
193    const checkboxValues = this.hasCheckboxTarget
194      ? this.checkboxTargets.map((checkbox) => ({
195          id: checkbox.id,
196          checked: checkbox.checked,
197        }))
198      : [];
199
200    return {
201      text: textValue,
202      checkboxes: checkboxValues,
203    };
204  }
205};

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.