# Component Interop

Source: https://lumeo.nativ.sh/docs/services/component-interop

# 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

This service implements `IAsyncDisposable`. Always handle `JSDisconnectedException` in cleanup methods to avoid errors when the Blazor circuit disconnects.

## 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

Copy

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

Scroll to Top

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

Download .txt Download .csv Download .json

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();

Calls are reference-counted internally. If you call `LockScroll()` twice, you must call `UnlockScroll()` twice to fully restore scrolling. This prevents issues when multiple overlays are stacked.

## 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
