-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat: positioning should happen out of React lifecycle #25456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ling1726
merged 18 commits into
microsoft:master
from
ling1726:feat/use-positioning-ref-update
Nov 2, 2022
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
372bb8f
feat: Updates ref update handling in `usePositioning`
ling1726 3cad896
revert button changes
ling1726 0e498a7
changefiles
ling1726 16af62c
update md
ling1726 3b60653
fix ssr
ling1726 a4db4d9
update
ling1726 92e4f17
fix blurriness
ling1726 c211a6d
Update change/@fluentui-react-positioning-b6d61cdf-e434-43ff-a859-ab5…
ling1726 91cca72
document usePortalMountNode memo
ling1726 f191e8b
fix breaking api
ling1726 22160da
rename to targetWindow
ling1726 6251c90
createPositionManager is internal
ling1726 5dd2379
short circuit
ling1726 2c1605b
update check to include override target
ling1726 6669e93
Merge branch 'feat/use-positioning-ref-update' of https://github.com/…
ling1726 d9a2d53
remove useless improt
ling1726 294aa77
revert blurriness change
ling1726 96ad42a
fix position fixed and effect
ling1726 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-portal-323fba1d-79e4-4b55-af4f-cfa9444a2bcf.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "patch", | ||
| "comment": "refactor: usePortalMount node updates mount node attributes in memo", | ||
| "packageName": "@fluentui/react-portal", | ||
| "email": "[email protected]", | ||
| "dependentChangeType": "patch" | ||
| } |
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-positioning-b6d61cdf-e434-43ff-a859-ab563e6a47f5.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "minor", | ||
| "comment": "feat: position updates are handled out of react lifecycle", | ||
| "packageName": "@fluentui/react-positioning", | ||
| "email": "[email protected]", | ||
| "dependentChangeType": "patch" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
packages/react-components/react-positioning/src/createPositionManager.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| import { computePosition } from '@floating-ui/dom'; | ||
| import type { Middleware, Placement, Strategy } from '@floating-ui/dom'; | ||
| import type { PositionManager, TargetElement } from './types'; | ||
| import { debounce, writeArrowUpdates, writeContainerUpdates, getScrollParent } from './utils'; | ||
|
|
||
| interface PositionManagerOptions { | ||
| /** | ||
| * The positioned element | ||
| */ | ||
| container: HTMLElement; | ||
| /** | ||
| * Element that the container will be anchored to | ||
| */ | ||
| target: TargetElement; | ||
| /** | ||
| * Arrow that points from the container to the target | ||
| */ | ||
| arrow: HTMLElement | null; | ||
| /** | ||
| * The value of the css `position` property | ||
| * @default absolute | ||
| */ | ||
| strategy: Strategy; | ||
| /** | ||
| * [Floating UI middleware](https://floating-ui.com/docs/middleware) | ||
| */ | ||
| middleware: Middleware[]; | ||
| /** | ||
| * [Floating UI placement](https://floating-ui.com/docs/computePosition#placement) | ||
| */ | ||
| placement?: Placement; | ||
| } | ||
|
|
||
| /** | ||
| * @internal | ||
| * @returns manager that handles positioning out of the react lifecycle | ||
ling1726 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| */ | ||
| export function createPositionManager(options: PositionManagerOptions): PositionManager { | ||
| const { container, target, arrow, strategy, middleware, placement } = options; | ||
| if (!target || !container) { | ||
| return { | ||
| updatePosition: () => undefined, | ||
| dispose: () => undefined, | ||
| }; | ||
| } | ||
|
|
||
| let isFirstUpdate = true; | ||
| const scrollParents: Set<HTMLElement> = new Set<HTMLElement>(); | ||
| const targetWindow = container.ownerDocument.defaultView; | ||
|
|
||
| // When the container is first resolved, set position `fixed` to avoid scroll jumps. | ||
| // Without this scroll jumps can occur when the element is rendered initially and receives focus | ||
| Object.assign(container.style, { position: 'fixed', left: 0, top: 0, margin: 0 }); | ||
|
|
||
| const forceUpdate = () => { | ||
| if (isFirstUpdate) { | ||
| scrollParents.add(getScrollParent(container)); | ||
| if (target instanceof HTMLElement) { | ||
| scrollParents.add(getScrollParent(target)); | ||
| } | ||
|
|
||
| scrollParents.forEach(scrollParent => { | ||
| scrollParent.addEventListener('scroll', updatePosition); | ||
| }); | ||
|
|
||
| isFirstUpdate = false; | ||
| } | ||
|
|
||
| Object.assign(container.style, { position: strategy }); | ||
| computePosition(target, container, { placement, middleware, strategy }) | ||
| .then(({ x, y, middlewareData, placement: computedPlacement }) => { | ||
| writeArrowUpdates({ arrow, middlewareData }); | ||
| writeContainerUpdates({ | ||
| container, | ||
| middlewareData, | ||
| placement: computedPlacement, | ||
| coordinates: { x, y }, | ||
| lowPPI: (targetWindow?.devicePixelRatio || 1) <= 1, | ||
| strategy, | ||
| }); | ||
| }) | ||
| .catch(err => { | ||
| // https://github.com/floating-ui/floating-ui/issues/1845 | ||
| // FIXME for node > 14 | ||
| // node 15 introduces promise rejection which means that any components | ||
| // tests need to be `it('', async () => {})` otherwise there can be race conditions with | ||
| // JSDOM being torn down before this promise is resolved so globals like `window` and `document` don't exist | ||
| // Unless all tests that ever use `usePositioning` are turned into async tests, any logging during testing | ||
| // will actually be counter productive | ||
| if (process.env.NODE_ENV === 'development') { | ||
| // eslint-disable-next-line no-console | ||
| console.error('[usePositioning]: Failed to calculate position', err); | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| const updatePosition = debounce(() => forceUpdate()); | ||
|
|
||
| const dispose = () => { | ||
| if (targetWindow) { | ||
| targetWindow.removeEventListener('scroll', updatePosition); | ||
| targetWindow.removeEventListener('resize', updatePosition); | ||
| } | ||
|
|
||
| scrollParents.forEach(scrollParent => { | ||
| scrollParent.removeEventListener('scroll', updatePosition); | ||
| }); | ||
| }; | ||
|
|
||
| if (targetWindow) { | ||
| targetWindow.addEventListener('scroll', updatePosition); | ||
| targetWindow.addEventListener('resize', updatePosition); | ||
| } | ||
|
|
||
| // Update the position on initialization | ||
| updatePosition(); | ||
|
|
||
| return { | ||
| updatePosition, | ||
| dispose, | ||
| }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.