Skip to content

Commit eb35968

Browse files
committed
fix borders
1 parent 817449f commit eb35968

File tree

5 files changed

+173
-37
lines changed

5 files changed

+173
-37
lines changed

src/css-length-converter.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* eslint-disable */
2+
/*
3+
* from https://github.com/heygrady/Units/blob/master/Length.js
4+
* by heygrady
5+
*/
6+
7+
// create a test element
8+
var testElem = document.createElement('test'),
9+
docElement = document.documentElement,
10+
defaultView = document.defaultView,
11+
getComputedStyle = defaultView && defaultView.getComputedStyle,
12+
computedValueBug,
13+
runit = /^(-?[\d+\.\-]+)([a-z]+|%)$/i,
14+
convert = {},
15+
conversions = [1 / 25.4, 1 / 2.54, 1 / 72, 1 / 6],
16+
units = ['mm', 'cm', 'pt', 'pc', 'in', 'mozmm'],
17+
i = 6; // units.length
18+
19+
// add the test element to the dom
20+
docElement.appendChild(testElem);
21+
22+
// test for the WebKit getComputedStyle bug
23+
// @see http://bugs.jquery.com/ticket/10639
24+
if (getComputedStyle) {
25+
// add a percentage margin and measure it
26+
testElem.style.marginTop = '1%';
27+
computedValueBug = getComputedStyle(testElem).marginTop === '1%';
28+
}
29+
30+
// pre-calculate absolute unit conversions
31+
while (i--) {
32+
convert[units[i] + "toPx"] = conversions[i] ? conversions[i] * convert.inToPx : toPx(testElem, '1' + units[i]);
33+
}
34+
35+
// remove the test element from the DOM and delete it
36+
docElement.removeChild(testElem);
37+
testElem = undefined;
38+
39+
// convert a value to pixels
40+
function toPx(elem, value, prop, force) {
41+
// use width as the default property, or specify your own
42+
prop = prop || 'width';
43+
44+
var style,
45+
inlineValue,
46+
ret,
47+
unit = (value.match(runit) || [])[2],
48+
conversion = unit === 'px' ? 1 : convert[unit + 'toPx'],
49+
rem = /r?em/i;
50+
51+
if (conversion || rem.test(unit) && !force) {
52+
// calculate known conversions immediately
53+
// find the correct element for absolute units or rem or fontSize + em or em
54+
elem = conversion ? elem : unit === 'rem' ? docElement : prop === 'fontSize' ? elem.parentNode || elem : elem;
55+
56+
// use the pre-calculated conversion or fontSize of the element for rem and em
57+
conversion = conversion || parseFloat(curCSS(elem, 'fontSize'));
58+
59+
// multiply the value by the conversion
60+
ret = parseFloat(value) * conversion;
61+
} else {
62+
// begin "the awesome hack by Dean Edwards"
63+
// @see http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
64+
65+
// remember the current style
66+
style = elem.style;
67+
inlineValue = style[prop];
68+
69+
// set the style on the target element
70+
try {
71+
style[prop] = value;
72+
} catch (e) {
73+
// IE 8 and below throw an exception when setting unsupported units
74+
return 0;
75+
}
76+
77+
// read the computed value
78+
// if style is nothing we probably set an unsupported unit
79+
ret = !style[prop] ? 0 : parseFloat(curCSS(elem, prop));
80+
81+
// reset the style back to what it was or blank it out
82+
style[prop] = inlineValue !== undefined ? inlineValue : null;
83+
}
84+
85+
// return a number
86+
return ret;
87+
}
88+
89+
// return the computed value of a CSS property
90+
function curCSS(elem, prop) {
91+
var value,
92+
pixel,
93+
unit,
94+
rvpos = /^top|bottom/,
95+
outerProp = ["paddingTop", "paddingBottom", "borderTop", "borderBottom"],
96+
innerHeight,
97+
parent,
98+
i = 4; // outerProp.length
99+
100+
if (getComputedStyle) {
101+
// FireFox, Chrome/Safari, Opera and IE9+
102+
value = getComputedStyle(elem)[prop];
103+
} else if (pixel = elem.style['pixel' + prop.charAt(0).toUpperCase() + prop.slice(1)]) {
104+
// IE and Opera support pixel shortcuts for top, bottom, left, right, height, width
105+
// WebKit supports pixel shortcuts only when an absolute unit is used
106+
value = pixel + 'px';
107+
} else if (prop === 'fontSize') {
108+
// correct IE issues with font-size
109+
// @see http://bugs.jquery.com/ticket/760
110+
value = toPx(elem, '1em', 'left', 1) + 'px';
111+
} else {
112+
// IE 8 and below return the specified style
113+
value = elem.currentStyle[prop];
114+
}
115+
116+
// check the unit
117+
unit = (value.match(runit) || [])[2];
118+
if (unit === '%' && computedValueBug) {
119+
// WebKit won't convert percentages for top, bottom, left, right, margin and text-indent
120+
if (rvpos.test(prop)) {
121+
// Top and bottom require measuring the innerHeight of the parent.
122+
innerHeight = (parent = elem.parentNode || elem).offsetHeight;
123+
while (i--) {
124+
innerHeight -= parseFloat(curCSS(parent, outerProp[i]));
125+
}
126+
value = parseFloat(value) / 100 * innerHeight + 'px';
127+
} else {
128+
// This fixes margin, left, right and text-indent
129+
// @see https://bugs.webkit.org/show_bug.cgi?id=29084
130+
// @see http://bugs.jquery.com/ticket/10639
131+
value = toPx(elem, value);
132+
}
133+
} else if ((value === 'auto' || (unit && unit !== 'px')) && getComputedStyle) {
134+
// WebKit and Opera will return auto in some cases
135+
// Firefox will pass back an unaltered value when it can't be set, like top on a static element
136+
value = 0;
137+
} else if (unit && unit !== 'px' && !getComputedStyle) {
138+
// IE 8 and below won't convert units for us
139+
// try to convert using a prop that will return pixels
140+
// this will be accurate for everything (except font-size and some percentages)
141+
value = toPx(elem, value) + 'px';
142+
}
143+
return value;
144+
}
145+
146+
export default toPx

src/css-utils.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import CSS_COLOR_NAMES from "./html-colors";
2+
import toPx from "./css-length-converter";
23

3-
function _getAttributeFromString(string, method) {
4+
function _getAttributeFromString(string, method, ...data) {
45
if (!string) return false
56
string = string.split(' ')
67
for (let i in string) {
7-
const res = method(string, Number(i))
8+
const res = method(string, Number(i), ...data)
89
if (res) return res
910
}
1011
}
@@ -45,24 +46,25 @@ function _getOpacity(border, i) {
4546
return 1
4647
}
4748

48-
const htmlLengthNotSvgError = new Error('<RoundDiv> Border lengths must be either "thin", "medium", "thick", or in one of the following units: em, ex, cm, in, mm, px, pc, pt.')
49+
const htmlLengthNotSvgError = new Error('<RoundDiv> Border lengths must be either "thin", "medium", "thick", or in one of the following units: ch, cm, em, ex, in, mm, pc, pt, px, rem, vh, vmax, vmin, vw.')
4950

5051
function unitCheck(length) {
51-
if (length?.match(/(cap|ch|ic|lh|rem|rlh|vh|vw|vi|vb|vmin|vmax|Q)/g)) throw htmlLengthNotSvgError
52+
if (length?.match(/(cap|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g)) throw htmlLengthNotSvgError
5253
return length
5354
}
5455

55-
function _getWidth(border, i) {
56+
function _getWidth(border, i, element) {
5657
const val = border[i]
5758
// width is 0
58-
if (val === '0') return '0'
59+
if (val === '0') return 0
5960
// width is a word
60-
if (val.toLowerCase() === 'thin') return '1px'
61-
if (val.toLowerCase() === 'medium') return '3px'
62-
if (val.toLowerCase() === 'thick') return '5px'
61+
if (val.toLowerCase() === 'thin') return 1
62+
if (val.toLowerCase() === 'medium') return 3
63+
if (val.toLowerCase() === 'thick') return 5
6364
unitCheck(val)
6465
// width is <length>
65-
if (val.match(/(\d+(\.\d+)?(em|ex|px|cm|mm|in|pc|pt)|0)/g)) return val
66+
if (val.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/))
67+
return toPx(element, val)
6668
return false
6769
}
6870

src/main.js

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function RoundDiv({clip, style, children, ...props}) {
1010
const [background, setBackground] = useState('transparent')
1111
const [backgroundOpacity, setBackgroundOpacity] = useState(0)
1212
const [borderColor, setBorderColor] = useState('transparent')
13-
const [borderWidth, setBorderWidth] = useState('1')
13+
const [borderWidth, setBorderWidth] = useState(1)
1414
const [borderOpacity, setBorderOpacity] = useState(1)
1515

1616
const div = useRef()
@@ -44,18 +44,6 @@ export default function RoundDiv({clip, style, children, ...props}) {
4444

4545
return <div {...props} style={divStyle} ref={div}>
4646
<ShadowRoot>
47-
<style>{`
48-
rect {
49-
width: calc(${width}px + ${borderWidth} * 2);
50-
height: calc(${height}px + ${borderWidth} * 2);
51-
x: calc(${borderWidth} * -1);
52-
y: calc(${borderWidth} * -1);
53-
}
54-
#border {
55-
stroke-width: ${borderWidth};
56-
}
57-
`}
58-
</style>
5947
<svg viewBox={`0 0 ${height} ${width}`} style={{
6048
position: 'fixed',
6149
height,
@@ -64,17 +52,18 @@ export default function RoundDiv({clip, style, children, ...props}) {
6452
zIndex: -1
6553
}} xmlnsXlink="http://www.w3.org/1999/xlink">
6654
<defs>
67-
<path d={generateSvgSquircle(height, width, radius, clip)} id="shape"/>
55+
<path d={
56+
generateSvgSquircle(height + borderWidth * 2, width + borderWidth * 2, radius, clip)
57+
} id="shape"/>
6858

69-
<mask id="outsideOnly">
70-
<rect fill="white"/>
59+
<clipPath id="insideOnly">
7160
<use xlinkHref="#shape" fill="black"/>
72-
</mask>
61+
</clipPath>
7362
</defs>
74-
75-
<use xlinkHref="#shape" id="border" stroke={borderColor} fill="none"
76-
opacity={borderOpacity} mask="url(#outsideOnly)"/>
77-
<use xlinkHref="#shape" fill={background} opacity={backgroundOpacity}/>
63+
<use xlinkHref="#shape" fill={background} opacity={backgroundOpacity}
64+
x={-borderWidth} y={-borderWidth}/>
65+
<use xlinkHref="#shape" stroke={borderColor} fill="none" strokeWidth={borderWidth * 2}
66+
opacity={borderOpacity} clipPath="url(#insideOnly)" x={-borderWidth} y={-borderWidth}/>
7867
</svg>
7968
<slot style={{zIndex: 1}}/>
8069
</ShadowRoot>

src/react-shadow-dom.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* from https://github.com/apearce/react-shadow-root/blob/master/src/lib/ReactShadowRoot.js
3-
* by @apearce
3+
* by apearce
44
*/
55

66
import React from 'react';

src/updateStates.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,12 @@ export default function updateStates(args) {
4242
|| (getStyle('borderColor', div.current)?.overwritten || [])[1]?.value
4343
|| (getStyle('borderTopColor', div.current)?.overwritten || [])[1]?.value
4444
) || 'transparent')
45-
setBorderWidth(
46-
getWidth(style?.border)
45+
setBorderWidth(getWidth(
46+
style?.border
4747
|| style?.borderWidth
4848
|| unitCheck((getStyle('borderWidth', div.current)?.overwritten || [])[0]?.value)
49-
|| unitCheck((getStyle('borderTopWidth', div.current)?.overwritten || [])[0]?.value)
50-
|| '1'
51-
)
49+
|| unitCheck((getStyle('borderTopWidth', div.current)?.overwritten || [])[0]?.value),
50+
div.current) || 1)
5251
setBorderOpacity(getOpacity(
5352
style?.border
5453
|| style?.borderColor

0 commit comments

Comments
 (0)