@instructure/ui-position 11.7.4-snapshot-185 → 11.7.4-snapshot-8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -7
- package/es/Position/index.js +34 -2
- package/es/PositionPropTypes.js +8 -0
- package/es/calculateElementPosition.js +99 -13
- package/lib/Position/index.js +34 -2
- package/lib/PositionPropTypes.js +8 -1
- package/lib/calculateElementPosition.js +98 -14
- package/package.json +12 -12
- package/src/Position/index.tsx +29 -5
- package/src/PositionPropTypes.ts +11 -0
- package/src/calculateElementPosition.ts +100 -14
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Position/index.d.ts +4 -1
- package/types/Position/index.d.ts.map +1 -1
- package/types/PositionPropTypes.d.ts +10 -0
- package/types/PositionPropTypes.d.ts.map +1 -1
- package/types/calculateElementPosition.d.ts +2 -2
- package/types/calculateElementPosition.d.ts.map +1 -1
|
@@ -33,7 +33,6 @@ import {
|
|
|
33
33
|
} from '@instructure/ui-dom-utils'
|
|
34
34
|
import type { RectType } from '@instructure/ui-dom-utils'
|
|
35
35
|
import { mirrorPlacement } from './mirrorPlacement'
|
|
36
|
-
// @ts-expect-error will be needed for fix in the `offsetToPx` method
|
|
37
36
|
import { px } from '@instructure/ui-utils'
|
|
38
37
|
|
|
39
38
|
import type {
|
|
@@ -42,6 +41,7 @@ import type {
|
|
|
42
41
|
PositionConstraint,
|
|
43
42
|
PositionMountNode,
|
|
44
43
|
ElementPosition,
|
|
44
|
+
ElementPositionWithAvailableSpace,
|
|
45
45
|
PositionElement,
|
|
46
46
|
Size,
|
|
47
47
|
Overflow,
|
|
@@ -95,7 +95,7 @@ function calculateElementPosition(
|
|
|
95
95
|
element?: PositionElement,
|
|
96
96
|
target?: PositionElement,
|
|
97
97
|
options: Options = {}
|
|
98
|
-
):
|
|
98
|
+
): ElementPositionWithAvailableSpace {
|
|
99
99
|
if (!element || options.placement === 'offscreen') {
|
|
100
100
|
// hide offscreen content at the bottom of the DOM from screenreaders
|
|
101
101
|
// unless content is contained somewhere else
|
|
@@ -113,10 +113,13 @@ function calculateElementPosition(
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
const pos = new PositionData(element, target, options)
|
|
116
|
+
const { height: availableHeight, width: availableWidth } = pos.availableSpace
|
|
116
117
|
|
|
117
118
|
return {
|
|
118
119
|
placement: pos.placement,
|
|
119
|
-
style: pos.style
|
|
120
|
+
style: pos.style,
|
|
121
|
+
availableHeight,
|
|
122
|
+
availableWidth
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -304,7 +307,7 @@ class PositionData {
|
|
|
304
307
|
) {
|
|
305
308
|
this.options = options || {}
|
|
306
309
|
|
|
307
|
-
const { container,
|
|
310
|
+
const { container, placement, over } = this.options
|
|
308
311
|
|
|
309
312
|
if (!element || placement === 'offscreen') return
|
|
310
313
|
|
|
@@ -320,16 +323,9 @@ class PositionData {
|
|
|
320
323
|
over ? this.element.placement : this.element.mirroredPlacement
|
|
321
324
|
)
|
|
322
325
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
this.constrainTo(getScrollParents(this.target.node)[0])
|
|
327
|
-
} else if (constrain === 'parent') {
|
|
328
|
-
this.constrainTo(this.container)
|
|
329
|
-
} else if (typeof constrain === 'function') {
|
|
330
|
-
this.constrainTo(findDOMNode(constrain.call(null)))
|
|
331
|
-
} else if (typeof constrain === 'object') {
|
|
332
|
-
this.constrainTo(findDOMNode(constrain))
|
|
326
|
+
const constraintNode = this.resolveConstraintNode()
|
|
327
|
+
if (constraintNode) {
|
|
328
|
+
this.constrainTo(constraintNode)
|
|
333
329
|
}
|
|
334
330
|
}
|
|
335
331
|
|
|
@@ -543,6 +539,96 @@ class PositionData {
|
|
|
543
539
|
}
|
|
544
540
|
}
|
|
545
541
|
}
|
|
542
|
+
|
|
543
|
+
// Resolves the `constrain` option (`'window'`, `'scroll-parent'`,
|
|
544
|
+
// `'parent'`, a function, or an element) to the DOM node it points at.
|
|
545
|
+
resolveConstraintNode(): Node | Window | null {
|
|
546
|
+
const { constrain } = this.options
|
|
547
|
+
const elementNode = this.element?.node
|
|
548
|
+
if (!elementNode) return null
|
|
549
|
+
|
|
550
|
+
if (constrain === 'window') return ownerWindow(elementNode) ?? null
|
|
551
|
+
if (constrain === 'scroll-parent') {
|
|
552
|
+
return getScrollParents(this.target?.node)[0] ?? null
|
|
553
|
+
}
|
|
554
|
+
if (constrain === 'parent') return findDOMNode(this.container) ?? null
|
|
555
|
+
if (typeof constrain === 'function') {
|
|
556
|
+
return findDOMNode(constrain.call(null)) ?? null
|
|
557
|
+
}
|
|
558
|
+
if (typeof constrain === 'object' && constrain) {
|
|
559
|
+
return findDOMNode(constrain) ?? null
|
|
560
|
+
}
|
|
561
|
+
return null
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Maximum height/width (in CSS px) the positioned element can occupy in
|
|
566
|
+
* the resolved placement before crossing the constraint. Drives the
|
|
567
|
+
* `--ui-position-available-{height,width}` CSS variables
|
|
568
|
+
*/
|
|
569
|
+
get availableSpace(): { height: number; width: number } {
|
|
570
|
+
const targetNode = this.target?.node as Element | undefined
|
|
571
|
+
const constraintNode = this.resolveConstraintNode()
|
|
572
|
+
if (
|
|
573
|
+
!this.element ||
|
|
574
|
+
!targetNode?.getBoundingClientRect ||
|
|
575
|
+
!constraintNode
|
|
576
|
+
) {
|
|
577
|
+
return { height: Infinity, width: Infinity }
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const targetRect = targetNode.getBoundingClientRect()
|
|
581
|
+
let constraintRect: RectType
|
|
582
|
+
if ('getBoundingClientRect' in constraintNode) {
|
|
583
|
+
constraintRect = (constraintNode as Element).getBoundingClientRect()
|
|
584
|
+
} else {
|
|
585
|
+
const win =
|
|
586
|
+
'defaultView' in constraintNode
|
|
587
|
+
? (constraintNode as Document).defaultView
|
|
588
|
+
: (constraintNode as Window)
|
|
589
|
+
if (!win) return { height: Infinity, width: Infinity }
|
|
590
|
+
constraintRect = {
|
|
591
|
+
top: 0,
|
|
592
|
+
left: 0,
|
|
593
|
+
right: win.innerWidth,
|
|
594
|
+
bottom: win.innerHeight,
|
|
595
|
+
width: win.innerWidth,
|
|
596
|
+
height: win.innerHeight
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// `offsetX` / `offsetY` always push the popover *away* from the trigger
|
|
601
|
+
// so on the primary axis they consume available space.
|
|
602
|
+
const elementNode = this.element.node
|
|
603
|
+
const offsetY = px(this.element._offset.top, elementNode)
|
|
604
|
+
const offsetX = px(this.element._offset.left, elementNode)
|
|
605
|
+
const [primary] = this.element.placement
|
|
606
|
+
|
|
607
|
+
let height: number
|
|
608
|
+
if (primary === 'bottom') {
|
|
609
|
+
height = constraintRect.bottom - targetRect.bottom - offsetY
|
|
610
|
+
} else if (primary === 'top') {
|
|
611
|
+
height = targetRect.top - constraintRect.top - offsetY
|
|
612
|
+
} else {
|
|
613
|
+
height = constraintRect.height
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
let width: number
|
|
617
|
+
if (primary === 'end') {
|
|
618
|
+
width = constraintRect.right - targetRect.right - offsetX
|
|
619
|
+
} else if (primary === 'start') {
|
|
620
|
+
width = targetRect.left - constraintRect.left - offsetX
|
|
621
|
+
} else {
|
|
622
|
+
width = constraintRect.width
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Floor at 16px so a consumer's `max-height` doesn't collapse to 0 in the
|
|
626
|
+
// frame(s) before placement flips when the trigger sits right at the edge.
|
|
627
|
+
return {
|
|
628
|
+
height: Math.max(16, height),
|
|
629
|
+
width: Math.max(16, width)
|
|
630
|
+
}
|
|
631
|
+
}
|
|
546
632
|
}
|
|
547
633
|
|
|
548
634
|
function addOffsets(offsets: Offset[]) {
|