-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react-positioning): simplify maxSize options (#28649)
* wip * comments * try to make it better * chg * use middleware * update flag * test inline overflow * comment * use clip to test overflow * normalize autosize once * nit fix in test * rename autoSize to rawAutoSize, and normalizeAutoSize to autoSize * apply suggestions * fix test
- Loading branch information
1 parent
118a4e2
commit d2d8068
Showing
8 changed files
with
275 additions
and
33 deletions.
There are no files selected for viewing
This file contains 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
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-positioning-bc570315-c0a5-4a0f-a762-5fb18e58b4e8.json
This file contains 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: simplify autoSize options to make 'always'/'height-always'/'width-always' equivalent to true/'height'/'width'.", | ||
"packageName": "@fluentui/react-positioning", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
79 changes: 53 additions & 26 deletions
79
packages/react-components/react-positioning/src/middleware/maxSize.ts
This file contains 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 |
---|---|---|
@@ -1,41 +1,68 @@ | ||
import { size } from '@floating-ui/dom'; | ||
import type { Middleware } from '@floating-ui/dom'; | ||
import type { PositioningOptions } from '../types'; | ||
import type { NormalizedAutoSize, PositioningOptions } from '../types'; | ||
import { getBoundary } from '../utils/getBoundary'; | ||
export interface MaxSizeMiddlewareOptions extends Pick<PositioningOptions, 'overflowBoundary'> { | ||
container: HTMLElement | null; | ||
} | ||
|
||
export function maxSize(autoSize: PositioningOptions['autoSize'], options: MaxSizeMiddlewareOptions): Middleware { | ||
/** | ||
* floating-ui `size` middleware uses floating element's height/width to calculate available height/width. | ||
* This middleware only runs once per lifecycle, resetting styles applied by maxSize from previous lifecycle. | ||
* Then floating element's original size is restored and `size` middleware can calculate available height/width correctly. | ||
*/ | ||
export const resetMaxSize = (autoSize: NormalizedAutoSize): Middleware => ({ | ||
name: 'resetMaxSize', | ||
fn({ middlewareData: { maxSizeAlreadyReset }, elements }) { | ||
if (maxSizeAlreadyReset) { | ||
return {}; | ||
} | ||
|
||
const { applyMaxWidth, applyMaxHeight } = autoSize; | ||
if (applyMaxWidth) { | ||
elements.floating.style.removeProperty('box-sizing'); | ||
elements.floating.style.removeProperty('max-width'); | ||
elements.floating.style.removeProperty('width'); | ||
} | ||
if (applyMaxHeight) { | ||
elements.floating.style.removeProperty('box-sizing'); | ||
elements.floating.style.removeProperty('max-height'); | ||
elements.floating.style.removeProperty('height'); | ||
} | ||
|
||
return { | ||
data: { maxSizeAlreadyReset: true }, | ||
reset: { rects: true }, | ||
}; | ||
}, | ||
}); | ||
|
||
export function maxSize(autoSize: NormalizedAutoSize, options: MaxSizeMiddlewareOptions): Middleware { | ||
const { container, overflowBoundary } = options; | ||
return size({ | ||
...(overflowBoundary && { altBoundary: true, boundary: getBoundary(container, overflowBoundary) }), | ||
apply({ availableHeight, availableWidth, elements, rects }) { | ||
if (autoSize) { | ||
const applyMaxSizeStyles = (apply: boolean, dimension: 'width' | 'height', availableSize: number) => { | ||
if (!apply) { | ||
return; | ||
} | ||
|
||
elements.floating.style.setProperty('box-sizing', 'border-box'); | ||
} | ||
|
||
const applyMaxWidth = autoSize === 'always' || autoSize === 'width-always'; | ||
const widthOverflow = rects.floating.width > availableWidth && (autoSize === true || autoSize === 'width'); | ||
|
||
const applyMaxHeight = autoSize === 'always' || autoSize === 'height-always'; | ||
const heightOverflow = rects.floating.height > availableHeight && (autoSize === true || autoSize === 'height'); | ||
|
||
if (applyMaxHeight || heightOverflow) { | ||
elements.floating.style.setProperty('max-height', `${availableHeight}px`); | ||
} | ||
if (heightOverflow) { | ||
elements.floating.style.setProperty('height', `${availableHeight}px`); | ||
elements.floating.style.setProperty('overflow-y', 'auto'); | ||
} | ||
|
||
if (applyMaxWidth || widthOverflow) { | ||
elements.floating.style.setProperty('max-width', `${availableWidth}px`); | ||
} | ||
if (widthOverflow) { | ||
elements.floating.style.setProperty('width', `${availableWidth}px`); | ||
elements.floating.style.setProperty('overflow-x', 'auto'); | ||
} | ||
elements.floating.style.setProperty(`max-${dimension}`, `${availableSize}px`); | ||
|
||
if (rects.floating[dimension] > availableSize) { | ||
elements.floating.style.setProperty(dimension, `${availableSize}px`); | ||
|
||
const axis = dimension === 'width' ? 'x' : 'y'; | ||
if (!elements.floating.style.getPropertyValue(`overflow-${axis}`)) { | ||
elements.floating.style.setProperty(`overflow-${axis}`, 'auto'); | ||
} | ||
} | ||
}; | ||
|
||
const { applyMaxWidth, applyMaxHeight } = autoSize; | ||
applyMaxSizeStyles(applyMaxWidth, 'width', availableWidth); | ||
applyMaxSizeStyles(applyMaxHeight, 'height', availableHeight); | ||
}, | ||
}); | ||
} |
This file contains 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 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 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
53 changes: 53 additions & 0 deletions
53
packages/react-components/react-positioning/src/utils/normalizeAutoSize.test.ts
This file contains 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,53 @@ | ||
import { normalizeAutoSize } from './normalizeAutoSize'; | ||
|
||
describe('normalizeAutoSize', () => { | ||
const cases = [ | ||
[ | ||
'always', | ||
{ | ||
applyMaxWidth: true, | ||
applyMaxHeight: true, | ||
}, | ||
], | ||
[ | ||
true, | ||
{ | ||
applyMaxWidth: true, | ||
applyMaxHeight: true, | ||
}, | ||
], | ||
[ | ||
'width-always', | ||
{ | ||
applyMaxWidth: true, | ||
applyMaxHeight: false, | ||
}, | ||
], | ||
[ | ||
'width', | ||
{ | ||
applyMaxWidth: true, | ||
applyMaxHeight: false, | ||
}, | ||
], | ||
[ | ||
'height-always', | ||
{ | ||
applyMaxWidth: false, | ||
applyMaxHeight: true, | ||
}, | ||
], | ||
[ | ||
'height', | ||
{ | ||
applyMaxWidth: false, | ||
applyMaxHeight: true, | ||
}, | ||
], | ||
[false, false], | ||
] as const; | ||
|
||
it.each(cases)('should normalize autoSize', (rawAutoSize, normalizedAutoSize) => { | ||
expect(normalizeAutoSize(rawAutoSize)).toEqual(normalizedAutoSize); | ||
}); | ||
}); |
34 changes: 34 additions & 0 deletions
34
packages/react-components/react-positioning/src/utils/normalizeAutoSize.ts
This file contains 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,34 @@ | ||
import type { NormalizedAutoSize, PositioningOptions } from '../types'; | ||
|
||
/** | ||
* AutoSizes contains many options from historic implementation. | ||
* Now options 'always'/'height-always'/'width-always' are obsolete. | ||
* This function maps them to true/'height'/'width' | ||
*/ | ||
export const normalizeAutoSize = (autoSize?: PositioningOptions['autoSize']): NormalizedAutoSize | false => { | ||
switch (autoSize) { | ||
case 'always': | ||
case true: | ||
return { | ||
applyMaxWidth: true, | ||
applyMaxHeight: true, | ||
}; | ||
|
||
case 'width-always': | ||
case 'width': | ||
return { | ||
applyMaxWidth: true, | ||
applyMaxHeight: false, | ||
}; | ||
|
||
case 'height-always': | ||
case 'height': | ||
return { | ||
applyMaxWidth: false, | ||
applyMaxHeight: true, | ||
}; | ||
|
||
default: | ||
return false; | ||
} | ||
}; |