-
-
Notifications
You must be signed in to change notification settings - Fork 45
/
calculateBoxSize.ts
123 lines (105 loc) · 5.22 KB
/
calculateBoxSize.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { ResizeObserverBoxOptions } from '../ResizeObserverBoxOptions';
import { ResizeObserverSize } from '../ResizeObserverSize';
import { DOMRectReadOnly } from '../DOMRectReadOnly';
import { isSVG, isHidden } from '../utils/element';
import { freeze } from '../utils/freeze';
import { global } from '../utils/global';
interface ResizeObserverSizeCollection {
devicePixelContentBoxSize: ResizeObserverSize;
borderBoxSize: ResizeObserverSize;
contentBoxSize: ResizeObserverSize;
contentRect: DOMRectReadOnly;
}
const cache = new WeakMap<Element, ResizeObserverSizeCollection>();
const scrollRegexp = /auto|scroll/;
const verticalRegexp = /^tb|vertical/;
const IE = (/msie|trident/i).test(global.navigator && global.navigator.userAgent);
const parseDimension = (pixel: string | null): number => parseFloat(pixel || '0');
// Helper to generate and freeze a ResizeObserverSize
const size = (inlineSize = 0, blockSize = 0, switchSizes = false): ResizeObserverSize => {
return new ResizeObserverSize(
(switchSizes ? blockSize : inlineSize) || 0,
(switchSizes ? inlineSize : blockSize) || 0
);
}
// Return this when targets are hidden
const zeroBoxes = freeze({
devicePixelContentBoxSize: size(),
borderBoxSize: size(),
contentBoxSize: size(),
contentRect: new DOMRectReadOnly(0, 0, 0, 0)
})
/**
* Gets all box sizes of an element.
*/
const calculateBoxSizes = (target: Element, forceRecalculation = false): ResizeObserverSizeCollection => {
// Check cache to prevent recalculating styles.
if (cache.has(target) && !forceRecalculation) {
return cache.get(target) as ResizeObserverSizeCollection;
}
// If the target is hidden, send zero
if (isHidden(target)) {
cache.set(target, zeroBoxes);
return zeroBoxes;
}
const cs = getComputedStyle(target);
// If element has an SVG box, handle things differently, using its bounding box.
const svg = isSVG(target) && (target as SVGElement).ownerSVGElement && (target as SVGGraphicsElement).getBBox();
// IE does not remove padding from width/height, when box-sizing is border-box.
const removePadding = !IE && cs.boxSizing === 'border-box';
// Switch sizes if writing mode is vertical.
const switchSizes = verticalRegexp.test(cs.writingMode || '');
// Could the element have any scrollbars?
const canScrollVertically = !svg && scrollRegexp.test(cs.overflowY || '');
const canScrollHorizontally = !svg && scrollRegexp.test(cs.overflowX || '');
// Calculate properties for creating boxes.
const paddingTop = svg ? 0 : parseDimension(cs.paddingTop);
const paddingRight = svg ? 0 : parseDimension(cs.paddingRight);
const paddingBottom = svg ? 0 : parseDimension(cs.paddingBottom);
const paddingLeft = svg ? 0 : parseDimension(cs.paddingLeft);
const borderTop = svg ? 0 : parseDimension(cs.borderTopWidth);
const borderRight = svg ? 0 : parseDimension(cs.borderRightWidth);
const borderBottom = svg ? 0 : parseDimension(cs.borderBottomWidth);
const borderLeft = svg ? 0 : parseDimension(cs.borderLeftWidth);
const horizontalPadding = paddingLeft + paddingRight;
const verticalPadding = paddingTop + paddingBottom;
const horizontalBorderArea = borderLeft + borderRight;
const verticalBorderArea = borderTop + borderBottom;
const horizontalScrollbarThickness = !canScrollHorizontally ? 0 : (target as HTMLElement).offsetHeight - verticalBorderArea - target.clientHeight;
const verticalScrollbarThickness = !canScrollVertically ? 0 : (target as HTMLElement).offsetWidth - horizontalBorderArea - target.clientWidth;
const widthReduction = removePadding ? horizontalPadding + horizontalBorderArea : 0;
const heightReduction = removePadding ? verticalPadding + verticalBorderArea : 0;
const contentWidth = svg ? svg.width : parseDimension(cs.width) - widthReduction - verticalScrollbarThickness;
const contentHeight = svg ? svg.height : parseDimension(cs.height) - heightReduction - horizontalScrollbarThickness;
const borderBoxWidth = contentWidth + horizontalPadding + verticalScrollbarThickness + horizontalBorderArea;
const borderBoxHeight = contentHeight + verticalPadding + horizontalScrollbarThickness + verticalBorderArea;
const boxes = freeze({
devicePixelContentBoxSize: size(
Math.round(contentWidth * devicePixelRatio),
Math.round(contentHeight * devicePixelRatio),
switchSizes
),
borderBoxSize: size(borderBoxWidth, borderBoxHeight, switchSizes),
contentBoxSize: size(contentWidth, contentHeight, switchSizes),
contentRect: new DOMRectReadOnly(paddingLeft, paddingTop, contentWidth, contentHeight)
});
cache.set(target, boxes);
return boxes;
};
/**
* Calculates the observe box size of an element.
*
* https://drafts.csswg.org/resize-observer-1/#calculate-box-size
*/
const calculateBoxSize = (target: Element, observedBox: ResizeObserverBoxOptions, forceRecalculation?: boolean): ResizeObserverSize => {
const { borderBoxSize, contentBoxSize, devicePixelContentBoxSize } = calculateBoxSizes(target, forceRecalculation);
switch (observedBox) {
case ResizeObserverBoxOptions.DEVICE_PIXEL_CONTENT_BOX:
return devicePixelContentBoxSize;
case ResizeObserverBoxOptions.BORDER_BOX:
return borderBoxSize;
default:
return contentBoxSize;
}
};
export { calculateBoxSize, calculateBoxSizes };