Skip to content

Commit

Permalink
Merge pull request #1 from drinking-code/clip-path
Browse files Browse the repository at this point in the history
Use clip path
  • Loading branch information
drinking-code authored Aug 8, 2021
2 parents 640d1b4 + 8a4fd23 commit a310c9a
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 530 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ const App = () => {

export default App;
```
`react-round-div` will clip the `border-radius` of it is too large to prevent artifacts from appearing. You can turn this off by passing `clip={false}`.


## Caveats
This package is still in the starting blocks, so for now only single colored backgrounds and solid, uniform borders are supported.
There is support to come for gradients and image backgrounds, gradient borders, and perhaps proper `backdrop-filter` support.
This package is still in the starting blocks, so for now borders are only supported in the `solid` style and some transitions on the div may not work properly.

Moreover, children inside `RoundDiv` get cut off when are placed (partly) outside the div due to `clip-path` being used to make the smooth corners. There will probably an option in later versions to use a pseudo-element for the shape, so that children can be rendered outside.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

207 changes: 41 additions & 166 deletions src/css-utils.js
Original file line number Diff line number Diff line change
@@ -1,188 +1,63 @@
import CSS_COLOR_NAMES from "./html-colors";
import toPx from "./css-length-converter";
import CSS_COLOR_NAMES from "./external/bobspace:html-colors";
import toPx from "./external/heygrady:units:length";

function _getAttributeFromString(string, method, ...data) {
if (!string) return false
string = string.split(' ')
for (let i in string) {
if (!string.hasOwnProperty(i)) continue
const res = method(string, Number(i), ...data)
if (res) return res
}
}

// eslint-disable-next-line no-extend-native
String.prototype.matchesValidCSSLength = () =>
this.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/)

function _getColor(b, i) {
const val = b[i]
/** @returns {string} */
function convertPlainColor(val) {
if (!val) return '#000'
val = val?.toLowerCase()
// color is a hex code
if (val.toLowerCase().match(/#([0-9a-f]{3}){1,2}/)) return val
if (val?.match(/#([0-9a-f]{3}){1,2}/)) return val
// color is a function (rgb, rgba, hsl, hsla)
if (val.startsWith('rgb') || val.startsWith('hsl')) {
let color = val;
if (!val.endsWith(')'))
for (let j = 1; !b[i + j - 1].endsWith(')'); j++) {
color += b[i + j]
}
if (color[3] === 'a')
color = color.replace('a', '').replace(/,[^),]+\)/, ')')
return color
}
else if (val?.match(/^(rgb|hsl)a?\(([^,]{1,3},? *){3}(\d*\.?\d+)?\)/))
return val
.replace('a', '')
.replace(/\((([\d%]{1,3}, *){2}([\d%]{1,3}))(, *\d*\.?\d+)?\)/, '($1)')
// color is a html color name
if (CSS_COLOR_NAMES.map(color => color.toLowerCase())
else if (CSS_COLOR_NAMES.map(color => color.toLowerCase())
.includes(val.toLowerCase()))
return val
if (val === 'currentcolor') {
else if (val === 'currentcolor') {
return 'currentcolor'
}
return false
}

function _getImage(b, i) {
const val = b[i]

if (val.startsWith('url')) {
let url = val;
for (let j = 1; b[i + j]; j++) {
url += b[i + j]
}
url = /url\(("[^"]+"|'[^']+'|[^)]+)\)/g.exec(url)
url = url ? url[1] : false
url = url?.startsWith('"') || url?.startsWith("'")
? url.substr(1, url.length - 2)
: url
return url
}
}

function _getImageSize(b, i, element) {
// "val" is always what is defined in backgrund-size ([i]th argument)
const val = b[i]

if (['cover', 'contain'].includes(val)) {
return [val, null]
}

return __getTwoNumerics(b, i, element, htmlBackgroundSizeNotSvgError)
} else return '#000'
}

function _getPosition(b, i, element) {
// "val" is always what is defined in backgrund-size ([i]th argument)
const val = b[i]
const allWords = ['top', 'bottom', 'left', 'right', 'center']

if (b.length === 1 && allWords.includes(val)) {
if (val === 'center')
return ['center', 'center']
else if (['left', 'right'].includes(val))
return [val, 0]
else if (['top', 'bottom'].includes(val))
return [0, val]
}

const a = [0, 0]

if (allWords.includes(val)) {
if (b[i + 1].matchesValidCSSLength()) {

}
}

/*if (['cover', 'contain'].includes(val)) {
return [val, null]
}*/

return __getTwoNumerics(b, i, element, htmlBackgroundPositionNotSvgError)
}

function __getTwoNumerics(b, i, element, err) {
const returnIfCSSNumeric = (val, throwErr) => {
if (val?.endsWith('%'))
return val
else if (val?.matchesValidCSSLength()) {
unitCheck(val, throwErr ? err : undefined)
return toPx(element, val)
} else
return null
}

const convertedVal = returnIfCSSNumeric(b[i], true) // has null as fallback already
// "background-size: 50% 50%" is different to "background-size: 50%"
return [convertedVal, returnIfCSSNumeric(b[i + 1])]
}

function _getOpacity(b, i) {
let val = b[i]
if (val.startsWith('rgba') || val.startsWith('hsla')) {
if (!val.endsWith(')'))
for (let j = 1; !b[i + j - 1].endsWith(')'); j++) {
val += b[i + j]
}
return val.replace(/(rgb|hsl)a?\(([^,)]+,){3}/, '').replace(/\)$/, '')
}
if (b.length - 1 === i)
return 1
}

function _getRepeat(b, i) {
let val = b[i]
if (val === 'repeat-x')
return ['repeat', 'no-repeat']
else if (val === 'repeat-y')
return ['no-repeat', 'repeat']
else if (['repeat', 'space', 'round', 'no-repeat'].includes(val)) {
if (['repeat', 'space', 'round', 'no-repeat'].includes(b[i + 1] || ''))
return [val, b[i + 1]]
else
return [val, val]
}
/** @returns {number} */
function convertColorOpacity(val) {
if (val?.startsWith('rgba') || val?.startsWith('hsla')) {
return Number(val.match(/(\d*\.?\d+)?\)$/)[1])
} else return 1
}

const htmlLengthNotSvgErrorTemplate = (a, b) => `<RoundDiv> ${a} must be ${b ? `either ${b}, or` : ''} in one of the following units: ch, cm, em, ex, in, mm, pc, pt, px, rem, vh, vmax, vmin, vw.`
const htmlBorderLengthNotSvgError =
new Error(htmlLengthNotSvgErrorTemplate('border lengths', '"thin", "medium", "thick"'))
const htmlBackgroundSizeNotSvgError =
new Error(htmlLengthNotSvgErrorTemplate('background size', '"cover", "contain"'))
const htmlBackgroundPositionNotSvgError =
new Error(htmlLengthNotSvgErrorTemplate('background position', '"top", "bottom", "left", "right", "center"'))

function unitCheck(length, err) {
if (length?.match(/(cap|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g))
const htmlBorderRadiusNotSvgError =
new Error(htmlLengthNotSvgErrorTemplate('border radii'))

function toNumber(length, element, err) {
if (!length) return false
if (typeof length === 'number' || !length.match(/\D+/))
return Number(length);
else if (length?.match(/(cap|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g))
if (err) throw err
else return false
return length
else if (length?.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/))
return toPx(element, length)
}

function _getWidth(border, i, element) {
const val = border[i]
// width is 0
if (val === '0') return 0
/** @returns {number} */
function convertBorderWidth(val, element) {
if (!val) return 0
// width is a word
if (val.toLowerCase() === 'thin') return 1
if (val.toLowerCase() === 'medium') return 3
if (val.toLowerCase() === 'thick') return 5
unitCheck(val, htmlBorderLengthNotSvgError)
if (val?.toLowerCase() === 'thin')
return 1
else if (val?.toLowerCase() === 'medium')
return 3
else if (val?.toLowerCase() === 'thick')
return 5
// width is <length>
if (val.matchesValidCSSLength())
return toPx(element, val)
return false
else
return toNumber(val, element, htmlBorderLengthNotSvgError) || 0
}

/** @returns {number} */
const getWidth = (s, el) => _getAttributeFromString(s, _getWidth, el),
/** @returns {string} */
getImage = s => _getAttributeFromString(s, _getImage),
/** @returns {Array<string|number>} */
getImageSize = (s, el) => _getAttributeFromString(s, _getImageSize, el),
/** @returns {Array<string|number>} */
getPosition = (s, el) => _getAttributeFromString(s, _getPosition, el),
/** @returns {string} */
getColor = s => _getAttributeFromString(s, _getColor),
/** @returns {Array<string>} */
getRepeat = s => _getAttributeFromString(s, _getRepeat),
/** @returns {number} */
getOpacity = s => _getAttributeFromString(s, _getOpacity)

export {getWidth, getImage, getImageSize, getPosition, getColor, getRepeat, getOpacity}
export {convertPlainColor, convertColorOpacity, convertBorderWidth, toNumber, htmlBorderRadiusNotSvgError}
File renamed without changes.
8 changes: 8 additions & 0 deletions src/external/bobspace:html-colors.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit a310c9a

Please sign in to comment.