Lumeo

Component Interop Service

Low-level JS interop helpers for building custom components — scroll locking, focus traps, positioning, clipboard, and more.

Overview

The ComponentInteropService is the shared JS interop layer used by all Lumeo components internally. If you are building custom overlay components, dropdown menus, or anything that needs DOM interaction from C#, inject this service instead of calling IJSRuntime directly.

@inject ComponentInteropService Interop

Copy to Clipboard

Copy arbitrary text to the user's clipboard. Useful for code snippets, share links, API keys, and similar.

Try it: type something and copy it

await Interop.CopyToClipboard("Hello, world!");

Scroll to Top

Smooth-scroll the page back to the top. Useful for long pages, back-to-top buttons, or after navigation.

Try it: scroll down the page first, then click the button

await Interop.ScrollToTop();

File Download

Trigger a browser file download from a base64-encoded string. Works for any file type -- text, CSV, JSON, images, PDFs, etc.

Try it: download a sample file

var bytes = System.Text.Encoding.UTF8.GetBytes("Hello from Lumeo!"); var base64 = Convert.ToBase64String(bytes); await Interop.DownloadFile("hello.txt", base64, "text/plain");

Scroll Locking

Prevent background scrolling when a modal or overlay is open. Used internally by Dialog, Sheet, Drawer, and AlertDialog.

// Lock scrolling when opening a modal await Interop.LockScroll(); // Unlock scrolling when closing await Interop.UnlockScroll();

Focus Management

Control keyboard focus for accessible overlay components. Focus trapping ensures that Tab and Shift+Tab cycle within a container element, which is required by WCAG for modal dialogs.

Focus Trapping

// Set up a focus trap on a dialog element await Interop.SetupFocusTrap("my-dialog-id"); // Remove the trap when the dialog closes await Interop.RemoveFocusTrap("my-dialog-id");

Focus a Specific Element

// Focus a specific element by ID await Interop.FocusElement("search-input"); // Focus a menu item by index (used for keyboard navigation in menus) await Interop.FocusMenuItemByIndex("menu-container-id", 0); // Count focusable menu items in a container int count = await Interop.GetMenuItemCount("menu-container-id");

Used internally by: Dialog, AlertDialog, Sheet, Drawer, DropdownMenu, ContextMenu, Command, Select, Combobox.

Click Outside Detection

Detect clicks outside a specific element to auto-close dropdowns, popovers, and similar floating UI. Optionally exclude a trigger element so that clicking the trigger does not fire the handler.

// Register handler -- fires when user clicks outside the element await Interop.RegisterClickOutside( elementId: "dropdown-content", triggerElementId: "dropdown-trigger", // clicks on trigger are excluded handler: async () => { _isOpen = false; await InvokeAsync(StateHasChanged); } ); // Unregister when component disposes await Interop.UnregisterClickOutside("dropdown-content");

Used internally by: Popover, DropdownMenu, Combobox, Select, DatePicker, NavigationMenu, HoverCard.

Element Positioning

Position a floating element (dropdown, tooltip, popover) relative to a reference element. Handles viewport boundaries, alignment, and optional width matching.

await Interop.PositionFixed( contentId: "popover-content", // the floating element referenceId: "popover-trigger", // the anchor element align: "start", // "start", "center", "end" matchWidth: false, // true = match reference element's width side: "bottom" // "top", "bottom", "left", "right" );
Parameter Type Description
contentId string ID of the floating element to position
referenceId string ID of the anchor/trigger element
align string "start", "center", or "end" alignment
matchWidth bool Whether to match the reference element's width
side string "top", "bottom", "left", or "right"

Used internally by: Tooltip, Popover, DropdownMenu, Select, Combobox, DatePicker, HoverCard, Menubar, NavigationMenu.

Element Measurement

Read element dimensions and positions from the DOM. Returns an ElementRect record with X, Y, Width, and Height.

// Get full bounding rect by element ID var rect = await Interop.GetElementRect("element-id"); // rect?.X, rect?.Y, rect?.Width, rect?.Height // Get a single dimension value double width = await Interop.GetElementDimension("element-id", "width"); // Get rect by CSS selector (useful for Tour, Scrollspy) var rect2 = await Interop.GetElementRectBySelector(".highlight-target");

GetElementRect returns null if the element is not found. The dimension parameter accepts "width", "height", "offsetWidth", "offsetHeight", "scrollWidth", "scrollHeight".

Advanced

These methods are used internally by specific Lumeo components. You typically do not need them unless building custom components with similar behavior.

Gestures

Touch and swipe gesture handling for mobile-friendly interactions.

// Drawer: detect swipe-to-dismiss await Interop.RegisterDrawerSwipe("drawer-id", async () => { _isOpen = false; await InvokeAsync(StateHasChanged); }); await Interop.UnregisterDrawerSwipe("drawer-id"); // Carousel: handle swipe and scroll await Interop.RegisterCarouselSwipe("carousel-id", "horizontal", swipeHandler: async (direction) => { /* "left" or "right" */ }, scrollHandler: async (scrollPos, maxScroll) => { /* track position */ } ); await Interop.UnregisterCarouselSwipe("carousel-id"); await Interop.CarouselScrollTo("carousel-id", index: 2, behavior: "smooth");

Resize Handles

Drag-to-resize for resizable panel layouts. Used by the Resizable component.

await Interop.RegisterResizeHandle("handle-id", "horizontal", resizeHandler: async (delta) => { /* update panel size */ }, resizeEndHandler: async () => { /* save final size */ } ); await Interop.UnregisterResizeHandle("handle-id");

Scrollspy

Track which section is currently visible during scroll for navigation highlighting.

await Interop.RegisterScrollspy("container-id", offset: 100, smooth: true, handler: async (activeId) => { /* highlight the active nav item */ } ); await Interop.ScrollspyScrollTo("container-id", "section-3", smooth: true); await Interop.UnregisterScrollspy("container-id");

Textarea Auto-Resize

Automatically grow a textarea element as the user types, up to a maximum number of rows.

await Interop.SetupAutoResize("textarea-id", maxRows: 10);

OTP Paste Handling

Handle clipboard paste events on OTP (one-time password) input groups. Distributes pasted digits across individual input fields.

await Interop.RegisterOtpPaste("otp-base-id", length: 6, handler: async (digits) => { /* fill OTP fields with pasted digits */ } ); await Interop.UnregisterOtpPaste("otp-base-id", length: 6);

Affix (Sticky Elements)

Stick an element to the viewport when it would scroll out of view. Similar to CSS position: sticky but with callback notifications.

await Interop.RegisterAffix("header-id", offsetTop: 0, offsetBottom: null, target: null, // or a CSS selector for the scroll container handler: async (isFixed) => { /* update shadow or styling */ } ); await Interop.UnregisterAffix("header-id");

Back to Top

Register a scroll listener that fires when the user scrolls past a threshold. Used to show/hide a back-to-top button.

await Interop.RegisterBackToTop("back-to-top-id", threshold: 300, handler: async (visible) => { _showButton = visible; } ); await Interop.UnregisterBackToTop("back-to-top-id");

Mention Caret Position

Get the visual caret position inside a textarea for positioning mention/autocomplete dropdowns.

var caret = await Interop.GetTextareaCaretPosition("textarea-id"); // caret.Top, caret.Left, caret.SelectionStart

DataGrid Column Resize

Drag-to-resize columns in a data grid. Similar to resize handles but specialized for table column headers.

await Interop.RegisterColumnResize("col-handle-id", resizeHandler: async (delta) => { /* adjust column width */ }, resizeEndHandler: async () => { /* persist final widths */ } ); await Interop.UnregisterColumnResize("col-handle-id");

Full API Reference

Method Category Returns Description
LockScroll()
Scroll
ValueTask Prevent page scrolling
UnlockScroll()
Scroll
ValueTask Re-enable page scrolling
ScrollToTop()
Scroll
ValueTask Smooth scroll to page top
SetupFocusTrap(elementId)
Focus
ValueTask Trap Tab focus within an element
RemoveFocusTrap(elementId)
Focus
ValueTask Remove focus trap from element
FocusElement(elementId)
Focus
ValueTask Focus a specific element by ID
FocusMenuItemByIndex(containerId, index)
Focus
ValueTask Focus the nth menu item in a container
GetMenuItemCount(containerId)
Focus
ValueTask<int> Count focusable menu items
RegisterClickOutside(elementId, triggerId?, handler)
Click
ValueTask Detect clicks outside an element
UnregisterClickOutside(elementId)
Click
ValueTask Stop detecting outside clicks
PositionFixed(contentId, refId, align, matchWidth, side)
Position
ValueTask Position floating element relative to reference
GetElementRect(elementId)
Measure
ValueTask<ElementRect?> Get bounding rect (X, Y, Width, Height)
GetElementRectBySelector(selector)
Measure
ValueTask<ElementRect?> Get rect by CSS selector
GetElementDimension(elementId, dimension)
Measure
ValueTask<double> Get a single dimension value
CopyToClipboard(text)
Clipboard
ValueTask Copy text to clipboard
DownloadFile(fileName, base64, mimeType?)
File
ValueTask Trigger browser file download
RegisterDrawerSwipe(elementId, handler)
Gesture
ValueTask Detect swipe-to-dismiss on drawer
UnregisterDrawerSwipe(elementId)
Gesture
ValueTask Remove drawer swipe handler
RegisterCarouselSwipe(elementId, orientation, swipe, scroll)
Gesture
ValueTask Handle swipe/scroll for carousel
UnregisterCarouselSwipe(elementId)
Gesture
ValueTask Remove carousel swipe handler
CarouselScrollTo(elementId, index, behavior?)
Gesture
ValueTask Scroll carousel to specific slide
RegisterResizeHandle(elementId, direction, resize, resizeEnd)
Resize
ValueTask Drag-to-resize for panels
UnregisterResizeHandle(elementId)
Resize
ValueTask Remove resize handler
RegisterScrollspy(containerId, offset, smooth, handler)
Scroll
ValueTask Track active section during scroll
UnregisterScrollspy(containerId)
Scroll
ValueTask Remove scrollspy listener
ScrollspyScrollTo(containerId, sectionId, smooth)
Scroll
ValueTask Scroll to a specific section
SetupAutoResize(elementId, maxRows)
Textarea
ValueTask Auto-resize textarea as user types
RegisterOtpPaste(baseId, length, handler)
Input
ValueTask Handle paste for OTP inputs
UnregisterOtpPaste(baseId, length)
Input
ValueTask Remove OTP paste handler
RegisterAffix(elementId, offsetTop, offsetBottom?, target?, handler)
Scroll
ValueTask Stick element to viewport on scroll
UnregisterAffix(elementId)
Scroll
ValueTask Remove affix handler
RegisterBackToTop(id, threshold, handler)
Scroll
ValueTask Track scroll position for back-to-top
UnregisterBackToTop(id)
Scroll
ValueTask Remove back-to-top listener
GetTextareaCaretPosition(elementId)
Input
ValueTask<TextareaCaretInfo> Get caret position for mention dropdowns
RegisterColumnResize(handleId, resizeHandler, resizeEndHandler)
Resize
ValueTask Drag-to-resize for data grid columns
UnregisterColumnResize(handleId)
Resize
ValueTask Remove column resize handler
RegisterToastSwipe(elementId, toastId, handler)
Gesture
ValueTask Swipe-to-dismiss for toast notifications
UnregisterToastSwipe(toastId, elementId)
Gesture
ValueTask Remove toast swipe handler

Types

Type Properties Description
ElementRect double X, Y, Width, Height Bounding rectangle returned by GetElementRect
TextareaCaretInfo double Top, Left; int SelectionStart Caret position info returned by GetTextareaCaretPosition