@instructure/ui-position 11.7.3 → 11.7.4-pr-snapshot-1781695314229
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 +12 -0
- package/LICENSE.md +1 -0
- package/{lib/Position/props.js → babel.config.cjs} +12 -7
- package/es/Position/index.js +40 -8
- package/es/PositionPropTypes.js +8 -0
- package/es/calculateElementPosition.js +108 -13
- package/es/index.js +6 -6
- package/es/mirrorHorizontalPlacement.js +4 -2
- package/es/mirrorPlacement.js +4 -2
- package/es/parsePlacement.js +1 -1
- package/package.json +14 -16
- package/src/Position/index.tsx +36 -11
- package/src/PositionPropTypes.ts +11 -0
- package/src/calculateElementPosition.ts +119 -15
- package/src/executeMirrorFunction.ts +1 -1
- package/src/index.ts +6 -6
- package/src/mirrorHorizontalPlacement.ts +2 -2
- package/src/mirrorPlacement.ts +2 -2
- package/src/parsePlacement.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Position/index.d.ts +11 -7
- 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
- package/types/executeMirrorFunction.d.ts +1 -1
- package/types/executeMirrorFunction.d.ts.map +1 -1
- package/types/index.d.ts +6 -6
- package/types/index.d.ts.map +1 -1
- package/types/mirrorHorizontalPlacement.d.ts +1 -1
- package/types/mirrorHorizontalPlacement.d.ts.map +1 -1
- package/types/mirrorPlacement.d.ts +1 -1
- package/types/mirrorPlacement.d.ts.map +1 -1
- package/types/parsePlacement.d.ts +1 -1
- package/types/parsePlacement.d.ts.map +1 -1
- package/lib/Position/index.js +0 -249
- package/lib/Position/styles.js +0 -50
- package/lib/Position/theme.js +0 -47
- package/lib/PositionPropTypes.js +0 -57
- package/lib/calculateElementPosition.js +0 -524
- package/lib/executeMirrorFunction.js +0 -37
- package/lib/index.js +0 -47
- package/lib/mirrorHorizontalPlacement.js +0 -60
- package/lib/mirrorPlacement.js +0 -63
- package/lib/package.json +0 -1
- package/lib/parsePlacement.js +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [11.7.4-pr-snapshot-1781695314229](https://github.com/instructure/instructure-ui/compare/v11.7.3...v11.7.4-pr-snapshot-1781695314229) (2026-06-17)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **ui-position:** add available space to position ([2ce7236](https://github.com/instructure/instructure-ui/commit/2ce7236db58f6678aabd544823c65dda4a51082c))
|
|
12
|
+
* **ui-position:** use border and padding in available space calculation ([1967e20](https://github.com/instructure/instructure-ui/commit/1967e20a214df364ead442b4f236b5e7c6019c66))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
6
18
|
## [11.7.3](https://github.com/instructure/instructure-ui/compare/v11.7.2...v11.7.3) (2026-05-07)
|
|
7
19
|
|
|
8
20
|
|
package/LICENSE.md
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.allowedProps = void 0;
|
|
7
1
|
/*
|
|
8
2
|
* The MIT License (MIT)
|
|
9
3
|
*
|
|
@@ -28,4 +22,15 @@ exports.allowedProps = void 0;
|
|
|
28
22
|
* SOFTWARE.
|
|
29
23
|
*/
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
module.exports = {
|
|
26
|
+
presets: [
|
|
27
|
+
[
|
|
28
|
+
require('@instructure/ui-babel-preset'),
|
|
29
|
+
{
|
|
30
|
+
esModules: Boolean(process.env.ES_MODULES),
|
|
31
|
+
removeConsole: process.env.NODE_ENV === 'production',
|
|
32
|
+
transformImports: Boolean(process.env.TRANSFORM_IMPORTS)
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
]
|
|
36
|
+
}
|
package/es/Position/index.js
CHANGED
|
@@ -30,18 +30,18 @@ import { deepEqual, shallowEqual, combineDataCid } from '@instructure/ui-utils';
|
|
|
30
30
|
import { debounce } from '@instructure/debounce';
|
|
31
31
|
import { Portal } from '@instructure/ui-portal';
|
|
32
32
|
import { withStyle } from '@instructure/emotion';
|
|
33
|
-
import generateStyle from
|
|
34
|
-
import generateComponentTheme from
|
|
35
|
-
import { allowedProps } from
|
|
36
|
-
import { calculateElementPosition } from
|
|
33
|
+
import generateStyle from './styles.js';
|
|
34
|
+
import generateComponentTheme from './theme.js';
|
|
35
|
+
import { allowedProps } from './props.js';
|
|
36
|
+
import { calculateElementPosition } from '../calculateElementPosition.js';
|
|
37
37
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
38
38
|
/**
|
|
39
39
|
---
|
|
40
|
-
category: components/
|
|
40
|
+
category: components/Util Components
|
|
41
41
|
---
|
|
42
42
|
**/
|
|
43
43
|
let Position = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, generateComponentTheme), _dec(_class = _dec2(_class = class Position extends Component {
|
|
44
|
-
static displayName =
|
|
44
|
+
static displayName = 'Position';
|
|
45
45
|
static componentId = 'Position';
|
|
46
46
|
static allowedProps = allowedProps;
|
|
47
47
|
static defaultProps = {
|
|
@@ -60,9 +60,13 @@ let Position = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, g
|
|
|
60
60
|
static contentLocatorAttribute = 'data-position-content';
|
|
61
61
|
constructor(props) {
|
|
62
62
|
super(props);
|
|
63
|
+
const initial = this.calculatePosition(props);
|
|
64
|
+
this._availableHeight = initial.availableHeight;
|
|
65
|
+
this._availableWidth = initial.availableWidth;
|
|
63
66
|
this.state = {
|
|
64
67
|
positioned: false,
|
|
65
|
-
|
|
68
|
+
placement: initial.placement,
|
|
69
|
+
style: initial.style
|
|
66
70
|
};
|
|
67
71
|
this.position = debounce(this.position, 0, {
|
|
68
72
|
leading: false,
|
|
@@ -76,6 +80,8 @@ let Position = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, g
|
|
|
76
80
|
_listener = null;
|
|
77
81
|
_content;
|
|
78
82
|
_target;
|
|
83
|
+
_availableHeight;
|
|
84
|
+
_availableWidth;
|
|
79
85
|
handleRef = el => {
|
|
80
86
|
const {
|
|
81
87
|
elementRef
|
|
@@ -152,6 +158,22 @@ let Position = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, g
|
|
|
152
158
|
}
|
|
153
159
|
}, 0));
|
|
154
160
|
};
|
|
161
|
+
|
|
162
|
+
// Write `--ui-position-available-{height,width}` directly on the content
|
|
163
|
+
// node's inline style
|
|
164
|
+
applyAvailableSpaceCustomProperties() {
|
|
165
|
+
const node = findDOMNode(this._content);
|
|
166
|
+
if (!node?.style) return;
|
|
167
|
+
const set = (name, value) => {
|
|
168
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
169
|
+
node.style.setProperty(name, `${value}px`);
|
|
170
|
+
} else {
|
|
171
|
+
node.style.removeProperty(name);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
set('--ui-position-available-height', this._availableHeight);
|
|
175
|
+
set('--ui-position-available-width', this._availableWidth);
|
|
176
|
+
}
|
|
155
177
|
calculatePosition(props) {
|
|
156
178
|
return calculateElementPosition(this._content, this._target, {
|
|
157
179
|
placement: props.placement,
|
|
@@ -163,9 +185,19 @@ let Position = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, g
|
|
|
163
185
|
});
|
|
164
186
|
}
|
|
165
187
|
position = () => {
|
|
188
|
+
const {
|
|
189
|
+
placement,
|
|
190
|
+
style,
|
|
191
|
+
availableHeight,
|
|
192
|
+
availableWidth
|
|
193
|
+
} = this.calculatePosition(this.props);
|
|
194
|
+
this._availableHeight = availableHeight;
|
|
195
|
+
this._availableWidth = availableWidth;
|
|
196
|
+
this.applyAvailableSpaceCustomProperties();
|
|
166
197
|
this.setState({
|
|
167
198
|
positioned: true,
|
|
168
|
-
|
|
199
|
+
placement,
|
|
200
|
+
style
|
|
169
201
|
});
|
|
170
202
|
};
|
|
171
203
|
startTracking() {
|
package/es/PositionPropTypes.js
CHANGED
|
@@ -49,4 +49,12 @@ const mirrorMap = {
|
|
|
49
49
|
stretch: 'stretch',
|
|
50
50
|
offscreen: 'offscreen'
|
|
51
51
|
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The full output of `calculateElementPosition`, including the available
|
|
55
|
+
* space numbers driving `--ui-position-available-{height,width}`. Kept
|
|
56
|
+
* separate from `ElementPosition` so `PositionState` (which extends
|
|
57
|
+
* `ElementPosition`) doesn't falsely advertise these fields
|
|
58
|
+
*/
|
|
59
|
+
|
|
52
60
|
export { placementPropValues, mirrorMap };
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { getBoundingClientRect, getScrollParents, getOffsetParents, canUseDOM, findDOMNode, ownerDocument, ownerWindow } from '@instructure/ui-dom-utils';
|
|
26
|
-
import { mirrorPlacement } from
|
|
26
|
+
import { mirrorPlacement } from './mirrorPlacement.js';
|
|
27
|
+
import { px } from '@instructure/ui-utils';
|
|
27
28
|
/**
|
|
28
29
|
* ---
|
|
29
30
|
* category: utilities/position
|
|
@@ -63,9 +64,15 @@ function calculateElementPosition(element, target, options = {}) {
|
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
const pos = new PositionData(element, target, options);
|
|
67
|
+
const {
|
|
68
|
+
height: availableHeight,
|
|
69
|
+
width: availableWidth
|
|
70
|
+
} = pos.availableSpace;
|
|
66
71
|
return {
|
|
67
72
|
placement: pos.placement,
|
|
68
|
-
style: pos.style
|
|
73
|
+
style: pos.style,
|
|
74
|
+
availableHeight,
|
|
75
|
+
availableWidth
|
|
69
76
|
};
|
|
70
77
|
}
|
|
71
78
|
class PositionedElement {
|
|
@@ -211,7 +218,6 @@ class PositionData {
|
|
|
211
218
|
this.options = options || {};
|
|
212
219
|
const {
|
|
213
220
|
container,
|
|
214
|
-
constrain,
|
|
215
221
|
placement,
|
|
216
222
|
over
|
|
217
223
|
} = this.options;
|
|
@@ -222,16 +228,9 @@ class PositionData {
|
|
|
222
228
|
left: this.options.offsetX
|
|
223
229
|
});
|
|
224
230
|
this.target = new PositionedElement(target || this.container, over ? this.element.placement : this.element.mirroredPlacement);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
this.constrainTo(getScrollParents(this.target.node)[0]);
|
|
229
|
-
} else if (constrain === 'parent') {
|
|
230
|
-
this.constrainTo(this.container);
|
|
231
|
-
} else if (typeof constrain === 'function') {
|
|
232
|
-
this.constrainTo(findDOMNode(constrain.call(null)));
|
|
233
|
-
} else if (typeof constrain === 'object') {
|
|
234
|
-
this.constrainTo(findDOMNode(constrain));
|
|
231
|
+
const constraintNode = this.resolveConstraintNode();
|
|
232
|
+
if (constraintNode) {
|
|
233
|
+
this.constrainTo(constraintNode);
|
|
235
234
|
}
|
|
236
235
|
}
|
|
237
236
|
options;
|
|
@@ -418,6 +417,102 @@ class PositionData {
|
|
|
418
417
|
}
|
|
419
418
|
}
|
|
420
419
|
}
|
|
420
|
+
|
|
421
|
+
// Resolves the `constrain` option (`'window'`, `'scroll-parent'`,
|
|
422
|
+
// `'parent'`, a function, or an element) to the DOM node it points at.
|
|
423
|
+
resolveConstraintNode() {
|
|
424
|
+
const {
|
|
425
|
+
constrain
|
|
426
|
+
} = this.options;
|
|
427
|
+
const elementNode = this.element?.node;
|
|
428
|
+
if (!elementNode) return null;
|
|
429
|
+
if (constrain === 'window') return ownerWindow(elementNode) ?? null;
|
|
430
|
+
if (constrain === 'scroll-parent') {
|
|
431
|
+
return getScrollParents(this.target?.node)[0] ?? null;
|
|
432
|
+
}
|
|
433
|
+
if (constrain === 'parent') return findDOMNode(this.container) ?? null;
|
|
434
|
+
if (typeof constrain === 'function') {
|
|
435
|
+
return findDOMNode(constrain.call(null)) ?? null;
|
|
436
|
+
}
|
|
437
|
+
if (typeof constrain === 'object' && constrain) {
|
|
438
|
+
return findDOMNode(constrain) ?? null;
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Maximum height/width (in CSS px) an *inner* box inside the positioned
|
|
445
|
+
* element can occupy before the wrapper crosses the constraint edge.
|
|
446
|
+
* Drives the `--ui-position-available-{height,width}` CSS variables.
|
|
447
|
+
*/
|
|
448
|
+
get availableSpace() {
|
|
449
|
+
const targetNode = this.target?.node;
|
|
450
|
+
const constraintNode = this.resolveConstraintNode();
|
|
451
|
+
if (!this.element || !targetNode?.getBoundingClientRect || !constraintNode) {
|
|
452
|
+
return {
|
|
453
|
+
height: Infinity,
|
|
454
|
+
width: Infinity
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const targetRect = targetNode.getBoundingClientRect();
|
|
458
|
+
let constraintRect;
|
|
459
|
+
if ('getBoundingClientRect' in constraintNode) {
|
|
460
|
+
constraintRect = constraintNode.getBoundingClientRect();
|
|
461
|
+
} else {
|
|
462
|
+
const win = 'defaultView' in constraintNode ? constraintNode.defaultView : constraintNode;
|
|
463
|
+
if (!win) return {
|
|
464
|
+
height: Infinity,
|
|
465
|
+
width: Infinity
|
|
466
|
+
};
|
|
467
|
+
constraintRect = {
|
|
468
|
+
top: 0,
|
|
469
|
+
left: 0,
|
|
470
|
+
right: win.innerWidth,
|
|
471
|
+
bottom: win.innerHeight,
|
|
472
|
+
width: win.innerWidth,
|
|
473
|
+
height: win.innerHeight
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// `offsetX` / `offsetY` always push the popover *away* from the trigger
|
|
478
|
+
// so on the primary axis they consume available space.
|
|
479
|
+
const elementNode = this.element.node;
|
|
480
|
+
const offsetY = px(this.element._offset.top, elementNode);
|
|
481
|
+
const offsetX = px(this.element._offset.left, elementNode);
|
|
482
|
+
const [primary] = this.element.placement;
|
|
483
|
+
let height;
|
|
484
|
+
if (primary === 'bottom') {
|
|
485
|
+
height = constraintRect.bottom - targetRect.bottom - offsetY;
|
|
486
|
+
} else if (primary === 'top') {
|
|
487
|
+
height = targetRect.top - constraintRect.top - offsetY;
|
|
488
|
+
} else {
|
|
489
|
+
height = constraintRect.height;
|
|
490
|
+
}
|
|
491
|
+
let width;
|
|
492
|
+
if (primary === 'end') {
|
|
493
|
+
width = constraintRect.right - targetRect.right - offsetX;
|
|
494
|
+
} else if (primary === 'start') {
|
|
495
|
+
width = targetRect.left - constraintRect.left - offsetX;
|
|
496
|
+
} else {
|
|
497
|
+
width = constraintRect.width;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Measure the wrapper's frame
|
|
501
|
+
let vFrame = 0;
|
|
502
|
+
let hFrame = 0;
|
|
503
|
+
if (elementNode && 'ownerDocument' in elementNode) {
|
|
504
|
+
const view = elementNode.ownerDocument?.defaultView;
|
|
505
|
+
if (view) {
|
|
506
|
+
const cs = view.getComputedStyle(elementNode);
|
|
507
|
+
vFrame = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0) + (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
|
|
508
|
+
hFrame = (parseFloat(cs.borderLeftWidth) || 0) + (parseFloat(cs.borderRightWidth) || 0) + (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return {
|
|
512
|
+
height: Math.max(0, Math.floor(height - vFrame - 1)),
|
|
513
|
+
width: Math.max(0, Math.floor(width - hFrame - 1))
|
|
514
|
+
};
|
|
515
|
+
}
|
|
421
516
|
}
|
|
422
517
|
function addOffsets(offsets) {
|
|
423
518
|
return offsets.reduce((sum, offset) => {
|
package/es/index.js
CHANGED
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
-
export { Position } from
|
|
25
|
-
export { calculateElementPosition } from
|
|
26
|
-
export { executeMirrorFunction } from
|
|
27
|
-
export { mirrorHorizontalPlacement } from
|
|
28
|
-
export { mirrorPlacement } from
|
|
29
|
-
export { parsePlacement } from
|
|
24
|
+
export { Position } from './Position/index.js';
|
|
25
|
+
export { calculateElementPosition } from './calculateElementPosition.js';
|
|
26
|
+
export { executeMirrorFunction } from './executeMirrorFunction.js';
|
|
27
|
+
export { mirrorHorizontalPlacement } from './mirrorHorizontalPlacement.js';
|
|
28
|
+
export { mirrorPlacement } from './mirrorPlacement.js';
|
|
29
|
+
export { parsePlacement } from './parsePlacement.js';
|
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
import
|
|
24
|
+
|
|
25
|
+
import { mirrorMap } from './PositionPropTypes.js';
|
|
26
|
+
import executeMirrorFunction from './executeMirrorFunction.js';
|
|
27
|
+
|
|
26
28
|
/**
|
|
27
29
|
* Given a string or array of one or two placement values, mirrors the placement
|
|
28
30
|
* horizontally.
|
package/es/mirrorPlacement.js
CHANGED
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
import
|
|
24
|
+
|
|
25
|
+
import { mirrorMap } from './PositionPropTypes.js';
|
|
26
|
+
import executeMirrorFunction from './executeMirrorFunction.js';
|
|
27
|
+
|
|
26
28
|
/**
|
|
27
29
|
* ---
|
|
28
30
|
* category: utilities/position
|
package/es/parsePlacement.js
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-position",
|
|
3
|
-
"version": "11.7.
|
|
3
|
+
"version": "11.7.4-pr-snapshot-1781695314229",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "A component for positioning content with respect to a designated target.",
|
|
5
6
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
7
|
"module": "./es/index.js",
|
|
7
|
-
"main": "./lib/index.js",
|
|
8
8
|
"types": "./types/index.d.ts",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -15,22 +15,22 @@
|
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@babel/runtime": "^7.29.2",
|
|
18
|
-
"@instructure/
|
|
19
|
-
"@instructure/
|
|
20
|
-
"@instructure/
|
|
21
|
-
"@instructure/
|
|
22
|
-
"@instructure/
|
|
23
|
-
"@instructure/ui-react-utils": "11.7.
|
|
24
|
-
"@instructure/ui-themes": "11.7.
|
|
25
|
-
"@instructure/ui-utils": "11.7.
|
|
26
|
-
"@instructure/uid": "11.7.
|
|
18
|
+
"@instructure/debounce": "11.7.4-pr-snapshot-1781695314229",
|
|
19
|
+
"@instructure/shared-types": "11.7.4-pr-snapshot-1781695314229",
|
|
20
|
+
"@instructure/emotion": "11.7.4-pr-snapshot-1781695314229",
|
|
21
|
+
"@instructure/ui-portal": "11.7.4-pr-snapshot-1781695314229",
|
|
22
|
+
"@instructure/ui-dom-utils": "11.7.4-pr-snapshot-1781695314229",
|
|
23
|
+
"@instructure/ui-react-utils": "11.7.4-pr-snapshot-1781695314229",
|
|
24
|
+
"@instructure/ui-themes": "11.7.4-pr-snapshot-1781695314229",
|
|
25
|
+
"@instructure/ui-utils": "11.7.4-pr-snapshot-1781695314229",
|
|
26
|
+
"@instructure/uid": "11.7.4-pr-snapshot-1781695314229"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@testing-library/jest-dom": "^6.6.3",
|
|
30
30
|
"@testing-library/react": "15.0.7",
|
|
31
31
|
"vitest": "^3.2.2",
|
|
32
|
-
"@instructure/ui-babel-preset": "11.7.
|
|
33
|
-
"@instructure/ui-color-utils": "11.7.
|
|
32
|
+
"@instructure/ui-babel-preset": "11.7.4-pr-snapshot-1781695314229",
|
|
33
|
+
"@instructure/ui-color-utils": "11.7.4-pr-snapshot-1781695314229"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"react": ">=18 <=19"
|
|
@@ -44,10 +44,8 @@
|
|
|
44
44
|
"src": "./src/index.ts",
|
|
45
45
|
"types": "./types/index.d.ts",
|
|
46
46
|
"import": "./es/index.js",
|
|
47
|
-
"require": "./lib/index.js",
|
|
48
47
|
"default": "./es/index.js"
|
|
49
48
|
},
|
|
50
|
-
"./lib/*": "./lib/*",
|
|
51
49
|
"./es/*": "./es/*",
|
|
52
50
|
"./types/*": "./types/*",
|
|
53
51
|
"./package.json": "./package.json",
|
|
@@ -57,7 +55,7 @@
|
|
|
57
55
|
"lint": "ui-scripts lint",
|
|
58
56
|
"lint:fix": "ui-scripts lint --fix",
|
|
59
57
|
"clean": "ui-scripts clean",
|
|
60
|
-
"build": "ui-scripts build
|
|
58
|
+
"build": "ui-scripts build",
|
|
61
59
|
"build:watch": "pnpm run ts:check -- --watch & ui-scripts build --watch",
|
|
62
60
|
"build:types": "tsc -p tsconfig.build.json",
|
|
63
61
|
"ts:check": "tsc -p tsconfig.build.json --noEmit --emitDeclarationOnly false"
|
package/src/Position/index.tsx
CHANGED
|
@@ -41,22 +41,23 @@ import { debounce } from '@instructure/debounce'
|
|
|
41
41
|
import { Portal } from '@instructure/ui-portal'
|
|
42
42
|
import { withStyle } from '@instructure/emotion'
|
|
43
43
|
|
|
44
|
-
import generateStyle from './styles'
|
|
45
|
-
import generateComponentTheme from './theme'
|
|
44
|
+
import generateStyle from './styles.js'
|
|
45
|
+
import generateComponentTheme from './theme.js'
|
|
46
46
|
import type { PositionProps, PositionState } from './props'
|
|
47
|
-
import { allowedProps } from './props'
|
|
47
|
+
import { allowedProps } from './props.js'
|
|
48
48
|
|
|
49
|
-
import { calculateElementPosition } from '../calculateElementPosition'
|
|
50
|
-
import { PositionElement } from '../PositionPropTypes'
|
|
49
|
+
import { calculateElementPosition } from '../calculateElementPosition.js'
|
|
50
|
+
import { PositionElement } from '../PositionPropTypes.js'
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
---
|
|
54
|
-
category: components/
|
|
54
|
+
category: components/Util Components
|
|
55
55
|
---
|
|
56
56
|
**/
|
|
57
57
|
@withDeterministicId()
|
|
58
58
|
@withStyle(generateStyle, generateComponentTheme)
|
|
59
59
|
class Position extends Component<PositionProps, PositionState> {
|
|
60
|
+
static displayName = 'Position'
|
|
60
61
|
static readonly componentId = 'Position'
|
|
61
62
|
|
|
62
63
|
static allowedProps = allowedProps
|
|
@@ -80,9 +81,13 @@ class Position extends Component<PositionProps, PositionState> {
|
|
|
80
81
|
constructor(props: PositionProps) {
|
|
81
82
|
super(props)
|
|
82
83
|
|
|
84
|
+
const initial = this.calculatePosition(props)
|
|
85
|
+
this._availableHeight = initial.availableHeight
|
|
86
|
+
this._availableWidth = initial.availableWidth
|
|
83
87
|
this.state = {
|
|
84
88
|
positioned: false,
|
|
85
|
-
|
|
89
|
+
placement: initial.placement,
|
|
90
|
+
style: initial.style
|
|
86
91
|
}
|
|
87
92
|
this.position = debounce(this.position, 0, {
|
|
88
93
|
leading: false,
|
|
@@ -98,6 +103,8 @@ class Position extends Component<PositionProps, PositionState> {
|
|
|
98
103
|
_listener: PositionChangeListenerType | null = null
|
|
99
104
|
_content?: PositionElement
|
|
100
105
|
_target?: PositionElement
|
|
106
|
+
_availableHeight?: number
|
|
107
|
+
_availableWidth?: number
|
|
101
108
|
|
|
102
109
|
handleRef = (el: Element | null) => {
|
|
103
110
|
const { elementRef } = this.props
|
|
@@ -220,6 +227,22 @@ class Position extends Component<PositionProps, PositionState> {
|
|
|
220
227
|
)
|
|
221
228
|
}
|
|
222
229
|
|
|
230
|
+
// Write `--ui-position-available-{height,width}` directly on the content
|
|
231
|
+
// node's inline style
|
|
232
|
+
applyAvailableSpaceCustomProperties() {
|
|
233
|
+
const node = findDOMNode(this._content) as HTMLElement | null
|
|
234
|
+
if (!node?.style) return
|
|
235
|
+
const set = (name: string, value: number | undefined) => {
|
|
236
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
237
|
+
node.style.setProperty(name, `${value}px`)
|
|
238
|
+
} else {
|
|
239
|
+
node.style.removeProperty(name)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
set('--ui-position-available-height', this._availableHeight)
|
|
243
|
+
set('--ui-position-available-width', this._availableWidth)
|
|
244
|
+
}
|
|
245
|
+
|
|
223
246
|
calculatePosition(props: PositionProps) {
|
|
224
247
|
return calculateElementPosition(this._content, this._target, {
|
|
225
248
|
placement: props.placement,
|
|
@@ -232,10 +255,12 @@ class Position extends Component<PositionProps, PositionState> {
|
|
|
232
255
|
}
|
|
233
256
|
|
|
234
257
|
position = () => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
258
|
+
const { placement, style, availableHeight, availableWidth } =
|
|
259
|
+
this.calculatePosition(this.props)
|
|
260
|
+
this._availableHeight = availableHeight
|
|
261
|
+
this._availableWidth = availableWidth
|
|
262
|
+
this.applyAvailableSpaceCustomProperties()
|
|
263
|
+
this.setState({ positioned: true, placement, style })
|
|
239
264
|
}
|
|
240
265
|
|
|
241
266
|
startTracking() {
|
package/src/PositionPropTypes.ts
CHANGED
|
@@ -135,6 +135,17 @@ export type ElementPosition = {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* The full output of `calculateElementPosition`, including the available
|
|
140
|
+
* space numbers driving `--ui-position-available-{height,width}`. Kept
|
|
141
|
+
* separate from `ElementPosition` so `PositionState` (which extends
|
|
142
|
+
* `ElementPosition`) doesn't falsely advertise these fields
|
|
143
|
+
*/
|
|
144
|
+
export type ElementPositionWithAvailableSpace = ElementPosition & {
|
|
145
|
+
availableHeight?: number
|
|
146
|
+
availableWidth?: number
|
|
147
|
+
}
|
|
148
|
+
|
|
138
149
|
export type PositionElement = UIElement
|
|
139
150
|
|
|
140
151
|
export type Offset<Type extends number | string | undefined = number> = {
|