From eb359686b8896ea5a79b9befb8ba844d7fcd859e Mon Sep 17 00:00:00 2001 From: johannes Date: Sat, 17 Apr 2021 04:05:35 +0200 Subject: [PATCH] fix borders --- src/css-length-converter.js | 146 ++++++++++++++++++++++++++++++++++++ src/css-utils.js | 22 +++--- src/main.js | 31 +++----- src/react-shadow-dom.js | 2 +- src/updateStates.js | 9 +-- 5 files changed, 173 insertions(+), 37 deletions(-) create mode 100644 src/css-length-converter.js diff --git a/src/css-length-converter.js b/src/css-length-converter.js new file mode 100644 index 0000000..b8a15c4 --- /dev/null +++ b/src/css-length-converter.js @@ -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 diff --git a/src/css-utils.js b/src/css-utils.js index b2bbaaa..7d1e742 100644 --- a/src/css-utils.js +++ b/src/css-utils.js @@ -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 } } @@ -45,24 +46,25 @@ function _getOpacity(border, i) { return 1 } -const htmlLengthNotSvgError = new Error(' 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(' 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 - 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 } diff --git a/src/main.js b/src/main.js index f613597..7f7682d 100644 --- a/src/main.js +++ b/src/main.js @@ -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() @@ -44,18 +44,6 @@ export default function RoundDiv({clip, style, children, ...props}) { return
- - + - - + - + - - - + + diff --git a/src/react-shadow-dom.js b/src/react-shadow-dom.js index bd0cbb7..ac9c681 100644 --- a/src/react-shadow-dom.js +++ b/src/react-shadow-dom.js @@ -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'; diff --git a/src/updateStates.js b/src/updateStates.js index e2e6854..3bed3f9 100644 --- a/src/updateStates.js +++ b/src/updateStates.js @@ -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