PageSourceSearch

https://veefriends.com/_content/VeeFriends.Web.Www.Components/Flowbite/FlowbiteProvider.8qyc4alxi5.razor.js

js veefriends.com collected 2026-09-25 14:33:49 UTC 13,420 bytes, 464 lines download raw bytes

1const { initFlowbite, Dropdown } = await packageLoader.flowbite();
2
3/**
4 * Represents a Component function that initializes Flowbite.
5 *
6 * @param {Object} dotnetContext - The dotnet context object.
7 * @param {Object} itemRef - The item reference object.
8 * @throws {Error} Throws an error if initialization fails.
9 */
10export function Component(dotnetContext, itemRef) {
11  initFlowbite();
12  initializePhonePinCodeInput();
13  return {
14    reinit: function () {
15      // Re-initialize Flowbite to pick up new elements added to the DOM
16      try {
17        // Find all dropdown toggle buttons
18        const dropdownToggles = document.querySelectorAll(
19          "[data-dropdown-toggle]",
20        );
21
22        dropdownToggles.forEach((toggle) => {
23          const dropdownId = toggle.getAttribute("data-dropdown-toggle");
24          const targetEl = document.getElementById(dropdownId);
25
26          if (targetEl) {
27            // Get dropdown configuration from data attributes
28            const placement =
29              toggle.getAttribute("data-dropdown-placement") || "bottom";
30            const triggerType =
31              toggle.getAttribute("data-dropdown-trigger") || "click";
32            const delay =
33              parseInt(toggle.getAttribute("data-dropdown-delay")) || 0;
34
35            // Initialize or reinitialize the dropdown
36            // The override: true option replaces existing instances
37            new Dropdown(
38              targetEl,
39              toggle,
40              {
41                placement: placement,
42                triggerType: triggerType,
43                delay: delay,
44              },
45              { id: dropdownId, override: true },
46            );
47          } else {
48            console.warn(
49              `Target element not found for dropdown: ${dropdownId}`,
50            );
51          }
52        });
53
54        console.log(
55          `Flowbite dropdowns reinitialized: ${dropdownToggles.length} found`,
56        );
57      } catch (error) {
58        console.error("Error reinitializing Flowbite:", error);
59      }
60    },
61    hideAllDropdowns: function () {
62      // Hide all open dropdowns immediately
63      try {
64        const openDropdowns = document.querySelectorAll(
65          "[data-dropdown-toggle]",
66        );
67
68        openDropdowns.forEach((toggle) => {
69          const dropdownId = toggle.getAttribute("data-dropdown-toggle");
70          const targetEl = document.getElementById(dropdownId);
71
72          if (targetEl && !targetEl.classList.contains("hidden")) {
73            // Hide the dropdown by adding the hidden class
74            targetEl.classList.add("hidden");
75            console.log(`Closed dropdown: ${dropdownId}`);
76          }
77        });
78      } catch (error) {
79        console.error("Error hiding dropdowns:", error);
80      }
81    },
82  };
83}
84
85/**
86 * Initializes pin code input functionality with auto-advancing focus and validation
87 * Supports both existing and dynamically added elements
88 */
89const initializePhonePinCodeInput = function () {
90  try {
91    // Initialize existing elements
92    initializeExistingPinInputs();
93
94    // Set up event delegation for future elements
95    setupEventDelegation();
96
97    // Set up MutationObserver to watch for new elements
98    setupMutationObserver();
99  } catch (error) {
100    console.warn("Error initializing pin code input:", error);
101  }
102};
103
104/**
105 * Initialize existing pin input elements
106 */
107function initializeExistingPinInputs() {
108  const pinContainers = document.querySelectorAll("[data-pin-input]");
109  pinContainers.forEach((container) => {
110    initializePinContainer(container);
111  });
112}
113
114/**
115 * Initialize a specific pin container
116 * @param {Element} container - The pin input container element
117 */
118function initializePinContainer(container) {
119  // Skip if already initialized
120  if (container.hasAttribute("data-pin-initialized")) {
121    return;
122  }
123
124  const inputs = container.querySelectorAll("[data-pin-input-item]");
125  if (inputs.length === 0) return;
126
127  // Mark as initialized
128  container.setAttribute("data-pin-initialized", "true");
129
130  // Store reference to inputs on the container for easy access
131  container._pinInputs = inputs;
132}
133
134/**
135 * Set up event delegation for pin input functionality
136 */
137function setupEventDelegation() {
138  // Use event delegation on document body to catch all pin input events
139  document.body.addEventListener("input", (e) => {
140    if (e.target.matches("[data-pin-input-item]")) {
141      const container = e.target.closest("[data-pin-input]");
142      if (container) {
143        const inputs = container.querySelectorAll("[data-pin-input-item]");
144        const index = Array.from(inputs).indexOf(e.target);
145        handleInput(e, inputs, index);
146      }
147    }
148  });
149
150  document.body.addEventListener("keydown", (e) => {
151    if (e.target.matches("[data-pin-input-item]")) {
152      const container = e.target.closest("[data-pin-input]");
153      if (container) {
154        const inputs = container.querySelectorAll("[data-pin-input-item]");
155        const index = Array.from(inputs).indexOf(e.target);
156        handleKeydown(e, inputs, index);
157      }
158    }
159  });
160
161  document.body.addEventListener("paste", (e) => {
162    if (e.target.matches("[data-pin-input-item]")) {
163      const container = e.target.closest("[data-pin-input]");
164      if (container) {
165        const inputs = container.querySelectorAll("[data-pin-input-item]");
166        const index = Array.from(inputs).indexOf(e.target);
167        handlePaste(e, inputs, index);
168      }
169    }
170  });
171
172  document.body.addEventListener(
173    "focus",
174    (e) => {
175      if (e.target.matches("[data-pin-input-item]")) {
176        e.target.select(); // Select all text when focused
177      }
178    },
179    true,
180  ); // Use capture phase for focus events
181
182  document.body.addEventListener("keypress", (e) => {
183    if (e.target.matches("[data-pin-input-item]")) {
184      if (!/^[0-9a-zA-Z]$/.test(e.key) && !isNavigationKey(e.key)) {
185        e.preventDefault();
186      }
187    }
188  });
189}
190
191/**
192 * Set up MutationObserver to watch for dynamically added pin input containers
193 */
194function setupMutationObserver() {
195  const observer = new MutationObserver((mutations) => {
196    mutations.forEach((mutation) => {
197      mutation.addedNodes.forEach((node) => {
198        if (node.nodeType === Node.ELEMENT_NODE) {
199          // Check if the added node is a pin container
200          if (node.matches && node.matches("[data-pin-input]")) {
201            initializePinContainer(node);
202          }
203
204          // Check for pin containers within the added node
205          const pinContainers =
206            node.querySelectorAll && node.querySelectorAll("[data-pin-input]");
207          if (pinContainers) {
208            pinContainers.forEach((container) => {
209              initializePinContainer(container);
210            });
211          }
212        }
213      });
214    });
215  });
216
217  // Start observing
218  observer.observe(document.body, {
219    childList: true,
220    subtree: true,
221  });
222
223  // Store observer reference for potential cleanup
224  if (!window._pinInputObserver) {
225    window._pinInputObserver = observer;
226  }
227}
228
229/**
230 * Handles input events for pin code fields
231 * @param {Event} e - The input event
232 * @param {NodeList} inputs - All input elements
233 * @param {number} index - Current input index
234 */
235function handleInput(e, inputs, index) {
236  const input = e.target;
237  const value = input.value;
238
239  // Limit to single character and sanitize
240  if (value.length > 1) {
241    input.value = value.slice(-1); // Keep only the last character
242  }
243
244  // Auto-advance to next input if current has value and there's a next input
245  if (input.value && index < inputs.length - 1) {
246    const nextInput = inputs[index + 1];
247    if (nextInput && !nextInput.disabled) {
248      setTimeout(() => {
249        nextInput.focus();
250        nextInput.select();
251      }, 0);
252    }
253  }
254
255  // Trigger form validation or completion check
256  checkCompletion(inputs);
257}
258
259/**
260 * Handles keydown events for navigation and deletion
261 * @param {Event} e - The keydown event
262 * @param {NodeList} inputs - All input elements
263 * @param {number} index - Current input index
264 */
265function handleKeydown(e, inputs, index) {
266  const input = e.target;
267
268  switch (e.key) {
269    case "Backspace":
270      // If current input is empty, move to previous and clear it
271      if (!input.value && index > 0) {
272        e.preventDefault();
273        const prevInput = inputs[index - 1];
274        if (prevInput && !prevInput.disabled) {
275          prevInput.focus();
276          prevInput.select();
277          prevInput.value = "";
278        }
279      }
280      break;
281
282    case "Delete":
283      // Clear current input
284      input.value = "";
285      break;
286
287    case "ArrowLeft":
288      e.preventDefault();
289      if (index > 0) {
290        const prevInput = inputs[index - 1];
291        if (prevInput && !prevInput.disabled) {
292          prevInput.focus();
293          prevInput.select();
294        }
295      }
296      break;
297
298    case "ArrowRight":
299      e.preventDefault();
300      if (index < inputs.length - 1) {
301        const nextInput = inputs[index + 1];
302        if (nextInput && !nextInput.disabled) {
303          nextInput.focus();
304          nextInput.select();
305        }
306      }
307      break;
308
309    case "Home":
310      e.preventDefault();
311      const firstInput = inputs[0];
312      if (firstInput && !firstInput.disabled) {
313        firstInput.focus();
314        firstInput.select();
315      }
316      break;
317
318    case "End":
319      e.preventDefault();
320      const lastInput = inputs[inputs.length - 1];
321      if (lastInput && !lastInput.disabled) {
322        lastInput.focus();
323        lastInput.select();
324      }
325      break;
326  }
327}
328
329/**
330 * Handles paste operations to distribute characters across inputs
331 * @param {Event} e - The paste event
332 * @param {NodeList} inputs - All input elements
333 * @param {number} index - Current input index
334 */
335function handlePaste(e, inputs, index) {
336  e.preventDefault();
337
338  try {
339    const pastedData = (e.clipboardData || window.clipboardData).getData(
340      "text",
341    );
342    const sanitizedData = pastedData.replace(/[^0-9a-zA-Z]/g, ""); // Remove invalid characters
343
344    // Distribute characters across available inputs starting from current position
345    for (
346      let i = 0;
347      i < sanitizedData.length && index + i < inputs.length;
348      i++
349    ) {
350      const targetInput = inputs[index + i];
351      if (targetInput && !targetInput.disabled) {
352        targetInput.value = sanitizedData[i];
353      }
354    }
355
356    // Focus the next empty input or the last filled input
357    const nextEmptyIndex = Math.min(
358      index + sanitizedData.length,
359      inputs.length - 1,
360    );
361    const targetInput = inputs[nextEmptyIndex];
362    if (targetInput && !targetInput.disabled) {
363      setTimeout(() => {
364        targetInput.focus();
365        targetInput.select();
366      }, 0);
367    }
368
369    // Check completion after paste
370    checkCompletion(inputs);
371  } catch (error) {
372    console.warn("Error handling paste operation:", error);
373  }
374}
375
376/**
377 * Checks if navigation key should be allowed
378 * @param {string} key - The key being pressed
379 * @returns {boolean} - Whether the key is a navigation key
380 */
381function isNavigationKey(key) {
382  const navigationKeys = [
383    "Backspace",
384    "Delete",
385    "Tab",
386    "Escape",
387    "Enter",
388    "Home",
389    "End",
390    "ArrowLeft",
391    "ArrowRight",
392    "ArrowUp",
393    "ArrowDown",
394  ];
395  return navigationKeys.includes(key);
396}
397
398/**
399 * Checks if all inputs are filled and triggers completion event
400 * @param {NodeList} inputs - All input elements
401 */
402function checkCompletion(inputs) {
403  try {
404    const values = Array.from(inputs).map((input) => input.value);
405    const allFilled = values.every((value) => value.length > 0);
406    const pinValue = values.join("");
407    const container = inputs[0].closest("[data-pin-input]");
408
409    // Find the first hidden input within the container that has the [data-pin-input-value] selector
410    const hiddenInput = container.querySelector("[data-pin-input-value]");
411
412    if (hiddenInput) {
413      // Update the hidden input value
414      const previousValue = hiddenInput.value;
415      hiddenInput.value = pinValue;
416
417      // Trigger change event if the value actually changed
418      if (previousValue !== pinValue) {
419        // Create and dispatch input event
420        const inputEvent = new Event("input", {
421          bubbles: true,
422          cancelable: true,
423        });
424        hiddenInput.dispatchEvent(inputEvent);
425
426        // Create and dispatch change event
427        const changeEvent = new Event("change", {
428          bubbles: true,
429          cancelable: true,
430        });
431        hiddenInput.dispatchEvent(changeEvent);
432      }
433    }
434
435    if (allFilled) {
436      // Dispatch custom event for completion
437      const completionEvent = new CustomEvent("pinCompleted", {
438        detail: { value: pinValue, inputs: inputs },
439        bubbles: true,
440      });
441
442      container.dispatchEvent(completionEvent);
443    }
444  } catch (error) {
445    console.warn("Error checking completion:", error);
446  }
447}
448
449/**
450 * Cleanup function to remove observers and event listeners if needed
451 * Call this if you need to clean up the pin input functionality
452 */
453window.cleanupPinInputs = function () {
454  if (window._pinInputObserver) {
455    window._pinInputObserver.disconnect();
456    window._pinInputObserver = null;
457  }
458
459  // Remove initialization markers
460  document.querySelectorAll("[data-pin-initialized]").forEach((container) => {
461    container.removeAttribute("data-pin-initialized");
462    delete container._pinInputs;
463  });
464};

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.