From ab352b217a4a3f77341c97be60739a73dcf7f69b Mon Sep 17 00:00:00 2001 From: Elijah Duffy Date: Sun, 12 Apr 2026 17:34:02 -0700 Subject: [PATCH] floating: add constraint size option --- src/lib/floating.svelte.ts | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/lib/floating.svelte.ts b/src/lib/floating.svelte.ts index ba7dcf4..082a2bf 100644 --- a/src/lib/floating.svelte.ts +++ b/src/lib/floating.svelte.ts @@ -4,7 +4,7 @@ * for more details, examples, and original source. */ -import { computePosition, autoUpdate, flip, offset, type Placement } from '@floating-ui/dom'; +import { computePosition, autoUpdate, flip, offset, type Placement, size } from '@floating-ui/dom'; import { createAttachmentKey } from 'svelte/attachments'; /** @@ -17,6 +17,13 @@ export interface PopoverOptions { placement?: Placement; /** Offset distance between the reference and floating elements (default: 8) */ offset?: number; + /** + * Constraints the width and height of the popover to prevent it from + * overflowing the viewport. If true, the popover will adjust its max-width + * and max-height to fit. Scrolling is the responsibility of the parent + * element. Default is true. + */ + constrainSize?: boolean; /** Callback when the popover is opened or closed */ ontoggle?: (open: boolean) => void; } @@ -29,7 +36,8 @@ export class Popover { private options: PopoverOptions = { interaction: 'click', placement: 'bottom-start', - offset: 8 + offset: 8, + constrainSize: true }; private referenceElement: HTMLElement | undefined = $state(); private floatingElement: HTMLElement | undefined = $state(); @@ -165,7 +173,21 @@ export class Popover { } const position = await computePosition(this.referenceElement, this.floatingElement, { placement: this.options.placement, - middleware: [flip(), offset(this.options.offset)] + middleware: [ + flip(), + offset(this.options.offset), + size({ + apply: ({ availableWidth, availableHeight, elements }) => { + if (this.options.constrainSize) { + elements.floating.style.maxWidth = `${availableWidth}px`; + elements.floating.style.maxHeight = `${availableHeight}px`; + } else { + elements.floating.style.maxWidth = ''; + elements.floating.style.maxHeight = ''; + } + } + }) + ] }); const { x, y } = position; Object.assign(this.floatingElement.style, {