Skip to content

Commit

Permalink
fix borders
Browse files Browse the repository at this point in the history
  • Loading branch information
drinking-code committed Apr 17, 2021
1 parent 817449f commit eb35968
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 37 deletions.
146 changes: 146 additions & 0 deletions src/css-length-converter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* eslint-disable */
/*
* from https://github.com/heygrady/Units/blob/master/Length.js
* by heygrady
*/

// create a test element
var testElem = document.createElement('test'),
docElement = document.documentElement,
defaultView = document.defaultView,
getComputedStyle = defaultView && defaultView.getComputedStyle,
computedValueBug,
runit = /^(-?[\d+\.\-]+)([a-z]+|%)$/i,
convert = {},
conversions = [1 / 25.4, 1 / 2.54, 1 / 72, 1 / 6],
units = ['mm', 'cm', 'pt', 'pc', 'in', 'mozmm'],
i = 6; // units.length

// add the test element to the dom
docElement.appendChild(testElem);

// test for the WebKit getComputedStyle bug
// @see http://bugs.jquery.com/ticket/10639
if (getComputedStyle) {
// add a percentage margin and measure it
testElem.style.marginTop = '1%';
computedValueBug = getComputedStyle(testElem).marginTop === '1%';
}

// pre-calculate absolute unit conversions
while (i--) {
convert[units[i] + "toPx"] = conversions[i] ? conversions[i] * convert.inToPx : toPx(testElem, '1' + units[i]);
}

// remove the test element from the DOM and delete it
docElement.removeChild(testElem);
testElem = undefined;

// convert a value to pixels
function toPx(elem, value, prop, force) {
// use width as the default property, or specify your own
prop = prop || 'width';

var style,
inlineValue,
ret,
unit = (value.match(runit) || [])[2],
conversion = unit === 'px' ? 1 : convert[unit + 'toPx'],
rem = /r?em/i;

if (conversion || rem.test(unit) && !force) {
// calculate known conversions immediately
// find the correct element for absolute units or rem or fontSize + em or em
elem = conversion ? elem : unit === 'rem' ? docElement : prop === 'fontSize' ? elem.parentNode || elem : elem;

// use the pre-calculated conversion or fontSize of the element for rem and em
conversion = conversion || parseFloat(curCSS(elem, 'fontSize'));

// multiply the value by the conversion
ret = parseFloat(value) * conversion;
} else {
// begin "the awesome hack by Dean Edwards"
// @see http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

// remember the current style
style = elem.style;
inlineValue = style[prop];

// set the style on the target element
try {
style[prop] = value;
} catch (e) {
// IE 8 and below throw an exception when setting unsupported units
return 0;
}

// read the computed value
// if style is nothing we probably set an unsupported unit
ret = !style[prop] ? 0 : parseFloat(curCSS(elem, prop));

// reset the style back to what it was or blank it out
style[prop] = inlineValue !== undefined ? inlineValue : null;
}

// return a number
return ret;
}

// return the computed value of a CSS property
function curCSS(elem, prop) {
var value,
pixel,
unit,
rvpos = /^top|bottom/,
outerProp = ["paddingTop", "paddingBottom", "borderTop", "borderBottom"],
innerHeight,
parent,
i = 4; // outerProp.length

if (getComputedStyle) {
// FireFox, Chrome/Safari, Opera and IE9+
value = getComputedStyle(elem)[prop];
} else if (pixel = elem.style['pixel' + prop.charAt(0).toUpperCase() + prop.slice(1)]) {
// IE and Opera support pixel shortcuts for top, bottom, left, right, height, width
// WebKit supports pixel shortcuts only when an absolute unit is used
value = pixel + 'px';
} else if (prop === 'fontSize') {
// correct IE issues with font-size
// @see http://bugs.jquery.com/ticket/760
value = toPx(elem, '1em', 'left', 1) + 'px';
} else {
// IE 8 and below return the specified style
value = elem.currentStyle[prop];
}

// check the unit
unit = (value.match(runit) || [])[2];
if (unit === '%' && computedValueBug) {
// WebKit won't convert percentages for top, bottom, left, right, margin and text-indent
if (rvpos.test(prop)) {
// Top and bottom require measuring the innerHeight of the parent.
innerHeight = (parent = elem.parentNode || elem).offsetHeight;
while (i--) {
innerHeight -= parseFloat(curCSS(parent, outerProp[i]));
}
value = parseFloat(value) / 100 * innerHeight + 'px';
} else {
// This fixes margin, left, right and text-indent
// @see https://bugs.webkit.org/show_bug.cgi?id=29084
// @see http://bugs.jquery.com/ticket/10639
value = toPx(elem, value);
}
} else if ((value === 'auto' || (unit && unit !== 'px')) && getComputedStyle) {
// WebKit and Opera will return auto in some cases
// Firefox will pass back an unaltered value when it can't be set, like top on a static element
value = 0;
} else if (unit && unit !== 'px' && !getComputedStyle) {
// IE 8 and below won't convert units for us
// try to convert using a prop that will return pixels
// this will be accurate for everything (except font-size and some percentages)
value = toPx(elem, value) + 'px';
}
return value;
}

export default toPx
22 changes: 12 additions & 10 deletions src/css-utils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import CSS_COLOR_NAMES from "./html-colors";
import toPx from "./css-length-converter";

function _getAttributeFromString(string, method) {
function _getAttributeFromString(string, method, ...data) {
if (!string) return false
string = string.split(' ')
for (let i in string) {
const res = method(string, Number(i))
const res = method(string, Number(i), ...data)
if (res) return res
}
}
Expand Down Expand Up @@ -45,24 +46,25 @@ function _getOpacity(border, i) {
return 1
}

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.')
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.')

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

function _getWidth(border, i) {
function _getWidth(border, i, element) {
const val = border[i]
// width is 0
if (val === '0') return '0'
if (val === '0') return 0
// width is a word
if (val.toLowerCase() === 'thin') return '1px'
if (val.toLowerCase() === 'medium') return '3px'
if (val.toLowerCase() === 'thick') return '5px'
if (val.toLowerCase() === 'thin') return 1
if (val.toLowerCase() === 'medium') return 3
if (val.toLowerCase() === 'thick') return 5
unitCheck(val)
// width is <length>
if (val.match(/(\d+(\.\d+)?(em|ex|px|cm|mm|in|pc|pt)|0)/g)) return val
if (val.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/))
return toPx(element, val)
return false
}

Expand Down
31 changes: 10 additions & 21 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function RoundDiv({clip, style, children, ...props}) {
const [background, setBackground] = useState('transparent')
const [backgroundOpacity, setBackgroundOpacity] = useState(0)
const [borderColor, setBorderColor] = useState('transparent')
const [borderWidth, setBorderWidth] = useState('1')
const [borderWidth, setBorderWidth] = useState(1)
const [borderOpacity, setBorderOpacity] = useState(1)

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

return <div {...props} style={divStyle} ref={div}>
<ShadowRoot>
<style>{`
rect {
width: calc(${width}px + ${borderWidth} * 2);
height: calc(${height}px + ${borderWidth} * 2);
x: calc(${borderWidth} * -1);
y: calc(${borderWidth} * -1);
}
#border {
stroke-width: ${borderWidth};
}
`}
</style>
<svg viewBox={`0 0 ${height} ${width}`} style={{
position: 'fixed',
height,
Expand All @@ -64,17 +52,18 @@ export default function RoundDiv({clip, style, children, ...props}) {
zIndex: -1
}} xmlnsXlink="http://www.w3.org/1999/xlink">
<defs>
<path d={generateSvgSquircle(height, width, radius, clip)} id="shape"/>
<path d={
generateSvgSquircle(height + borderWidth * 2, width + borderWidth * 2, radius, clip)
} id="shape"/>

<mask id="outsideOnly">
<rect fill="white"/>
<clipPath id="insideOnly">
<use xlinkHref="#shape" fill="black"/>
</mask>
</clipPath>
</defs>

<use xlinkHref="#shape" id="border" stroke={borderColor} fill="none"
opacity={borderOpacity} mask="url(#outsideOnly)"/>
<use xlinkHref="#shape" fill={background} opacity={backgroundOpacity}/>
<use xlinkHref="#shape" fill={background} opacity={backgroundOpacity}
x={-borderWidth} y={-borderWidth}/>
<use xlinkHref="#shape" stroke={borderColor} fill="none" strokeWidth={borderWidth * 2}
opacity={borderOpacity} clipPath="url(#insideOnly)" x={-borderWidth} y={-borderWidth}/>
</svg>
<slot style={{zIndex: 1}}/>
</ShadowRoot>
Expand Down
2 changes: 1 addition & 1 deletion src/react-shadow-dom.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* from https://github.com/apearce/react-shadow-root/blob/master/src/lib/ReactShadowRoot.js
* by @apearce
* by apearce
*/

import React from 'react';
Expand Down
9 changes: 4 additions & 5 deletions src/updateStates.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,12 @@ export default function updateStates(args) {
|| (getStyle('borderColor', div.current)?.overwritten || [])[1]?.value
|| (getStyle('borderTopColor', div.current)?.overwritten || [])[1]?.value
) || 'transparent')
setBorderWidth(
getWidth(style?.border)
setBorderWidth(getWidth(
style?.border
|| style?.borderWidth
|| unitCheck((getStyle('borderWidth', div.current)?.overwritten || [])[0]?.value)
|| unitCheck((getStyle('borderTopWidth', div.current)?.overwritten || [])[0]?.value)
|| '1'
)
|| unitCheck((getStyle('borderTopWidth', div.current)?.overwritten || [])[0]?.value),
div.current) || 1)
setBorderOpacity(getOpacity(
style?.border
|| style?.borderColor
Expand Down

0 comments on commit eb35968

Please sign in to comment.