uiHelpers.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. // RackPeek UI helpers — lightweight Blazor JSInterop primitives that don't
  2. // fit cleanly into a Razor component on their own. Currently exposes a
  3. // "dismiss me when the user interacts outside this element" helper used by
  4. // the mobile nav dropdown.
  5. //
  6. // Public API (called from Blazor via JSInterop):
  7. // window.rackpeekUi.registerOutsideDismiss(id, containerSelector, dotnetRef, methodName)
  8. // Listens for clicks/touches/Escape outside `containerSelector` and
  9. // invokes `methodName` on the supplied .NET ref. Idempotent — calling
  10. // twice with the same id replaces the prior handler.
  11. //
  12. // window.rackpeekUi.unregisterOutsideDismiss(id)
  13. // Removes the previously-registered handlers for `id`.
  14. (function () {
  15. "use strict";
  16. const _state = new Map();
  17. function registerOutsideDismiss(id, containerSelector, dotnetRef, methodName) {
  18. unregisterOutsideDismiss(id);
  19. const dismiss = () => {
  20. dotnetRef.invokeMethodAsync(methodName).catch(() => {
  21. // Component already disposed — drop the call silently.
  22. });
  23. };
  24. const pointerHandler = (e) => {
  25. const container = document.querySelector(containerSelector);
  26. if (!container) return;
  27. if (container.contains(e.target)) return;
  28. dismiss();
  29. };
  30. const keyHandler = (e) => {
  31. if (e.key === "Escape") dismiss();
  32. };
  33. // Listen on pointerdown rather than click so the dropdown closes
  34. // immediately when interaction starts elsewhere — feels snappier and
  35. // also catches drags / scrolls beginning outside the menu.
  36. document.addEventListener("pointerdown", pointerHandler, true);
  37. document.addEventListener("keydown", keyHandler, true);
  38. _state.set(id, { pointerHandler, keyHandler });
  39. }
  40. function unregisterOutsideDismiss(id) {
  41. const entry = _state.get(id);
  42. if (!entry) return;
  43. document.removeEventListener("pointerdown", entry.pointerHandler, true);
  44. document.removeEventListener("keydown", entry.keyHandler, true);
  45. _state.delete(id);
  46. }
  47. window.rackpeekUi = { registerOutsideDismiss, unregisterOutsideDismiss };
  48. })();