diff --git a/field/lib/field.ts b/field/lib/field.ts index 356da6fce5..b167618d37 100644 --- a/field/lib/field.ts +++ b/field/lib/field.ts @@ -7,13 +7,14 @@ import {html, LitElement, nothing, PropertyValues, TemplateResult} from 'lit'; import {property, query, state} from 'lit/decorators.js'; import {classMap} from 'lit/directives/class-map.js'; +import {SurfacePositionTarget} from '../../menu/lib/surfacePositionController.js'; import {EASING} from '../../motion/animation.js'; /** * A field component. */ -export class Field extends LitElement { +export class Field extends LitElement implements SurfacePositionTarget { @property({type: Boolean}) disabled = false; @property({type: Boolean}) error = false; @property({type: Boolean}) focused = false; @@ -36,6 +37,7 @@ export class Field extends LitElement { private labelAnimation?: Animation; @query('.label.floating') private readonly floatingLabelEl!: HTMLElement|null; @query('.label.resting') private readonly restingLabelEl!: HTMLElement|null; + @query('.container') private readonly containerEl!: HTMLElement|null; protected override update(props: PropertyValues) { // Client-side property updates @@ -226,4 +228,8 @@ export class Field extends LitElement { {transform: floatTransform, width}, {transform: restTransform, width} ]; } + + getSurfacePositionClientRect() { + return this.containerEl!.getBoundingClientRect(); + } } diff --git a/menu/lib/surfacePositionController.ts b/menu/lib/surfacePositionController.ts index bae3ab83e8..e00c9f0202 100644 --- a/menu/lib/surfacePositionController.ts +++ b/menu/lib/surfacePositionController.ts @@ -12,6 +12,15 @@ import {StyleInfo} from 'lit/directives/style-map.js'; */ export type Corner = 'END_START'|'END_END'|'START_START'|'START_END'; +/** + * An interface that provides a method to customize the rect from which to + * calculate the anchor positioning. Useful for when you want a surface to + * anchor to an element in your shadow DOM rather than the host element. + */ +export interface SurfacePositionTarget extends HTMLElement { + getSurfacePositionClientRect?: () => DOMRect; +} + /** * The configurable options for the surface position controller. */ @@ -27,11 +36,11 @@ export interface SurfacePositionControllerProperties { /** * The HTMLElement reference of the surface to be positioned. */ - surfaceEl: HTMLElement|null; + surfaceEl: SurfacePositionTarget|null; /** * The HTMLElement reference of the anchor to align to. */ - anchorEl: HTMLElement|null; + anchorEl: SurfacePositionTarget|null; /** * Whether or not the calculation should be relative to the top layer rather * than relative to the parent of the anchor. @@ -147,8 +156,12 @@ export class SurfacePositionController implements ReactiveController { this.host.requestUpdate(); await this.host.updateComplete; - const surfaceRect = surfaceEl.getBoundingClientRect(); - const anchorRect = anchorEl.getBoundingClientRect(); + const surfaceRect = surfaceEl.getSurfacePositionClientRect ? + surfaceEl.getSurfacePositionClientRect() : + surfaceEl.getBoundingClientRect(); + const anchorRect = anchorEl.getSurfacePositionClientRect ? + anchorEl.getSurfacePositionClientRect() : + anchorEl.getBoundingClientRect(); const [surfaceBlock, surfaceInline] = surfaceCorner.split('_') as Array<'START'|'END'>; const [anchorBlock, anchorInline] = @@ -159,7 +172,8 @@ export class SurfacePositionController implements ReactiveController { // statements because it _heavily_ cuts down on nesting and readability const isTopLayer = topLayerRaw ? 1 : 0; // LTR depends on the direction of the SURFACE not the anchor. - const isLTR = getComputedStyle(surfaceEl).direction === 'ltr' ? 1 : 0; + const isLTR = + getComputedStyle(surfaceEl as HTMLElement).direction === 'ltr' ? 1 : 0; const isRTL = isLTR ? 0 : 1; const isSurfaceInlineStart = surfaceInline === 'START' ? 1 : 0; const isSurfaceInlineEnd = surfaceInline === 'END' ? 1 : 0;