floating: add constraint size option

This commit is contained in:
Elijah Duffy
2026-04-12 17:34:02 -07:00
parent 18b0b8963a
commit ab352b217a

View File

@@ -4,7 +4,7 @@
* for more details, examples, and original source. * 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'; import { createAttachmentKey } from 'svelte/attachments';
/** /**
@@ -17,6 +17,13 @@ export interface PopoverOptions {
placement?: Placement; placement?: Placement;
/** Offset distance between the reference and floating elements (default: 8) */ /** Offset distance between the reference and floating elements (default: 8) */
offset?: number; 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 */ /** Callback when the popover is opened or closed */
ontoggle?: (open: boolean) => void; ontoggle?: (open: boolean) => void;
} }
@@ -29,7 +36,8 @@ export class Popover {
private options: PopoverOptions = { private options: PopoverOptions = {
interaction: 'click', interaction: 'click',
placement: 'bottom-start', placement: 'bottom-start',
offset: 8 offset: 8,
constrainSize: true
}; };
private referenceElement: HTMLElement | undefined = $state(); private referenceElement: HTMLElement | undefined = $state();
private floatingElement: HTMLElement | undefined = $state(); private floatingElement: HTMLElement | undefined = $state();
@@ -165,7 +173,21 @@ export class Popover {
} }
const position = await computePosition(this.referenceElement, this.floatingElement, { const position = await computePosition(this.referenceElement, this.floatingElement, {
placement: this.options.placement, 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; const { x, y } = position;
Object.assign(this.floatingElement.style, { Object.assign(this.floatingElement.style, {