Skip to content

Commit a310c9a

Browse files
Merge pull request #1 from drinking-code/clip-path
Use clip path
2 parents 640d1b4 + 8a4fd23 commit a310c9a

14 files changed

+346
-530
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ const App = () => {
2828

2929
export default App;
3030
```
31-
`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}`.
3231

3332

3433
## Caveats
35-
This package is still in the starting blocks, so for now only single colored backgrounds and solid, uniform borders are supported.
36-
There is support to come for gradients and image backgrounds, gradient borders, and perhaps proper `backdrop-filter` support.
34+
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.
35+
36+
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.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/css-utils.js

Lines changed: 41 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,188 +1,63 @@
1-
import CSS_COLOR_NAMES from "./html-colors";
2-
import toPx from "./css-length-converter";
1+
import CSS_COLOR_NAMES from "./external/bobspace:html-colors";
2+
import toPx from "./external/heygrady:units:length";
33

4-
function _getAttributeFromString(string, method, ...data) {
5-
if (!string) return false
6-
string = string.split(' ')
7-
for (let i in string) {
8-
if (!string.hasOwnProperty(i)) continue
9-
const res = method(string, Number(i), ...data)
10-
if (res) return res
11-
}
12-
}
13-
14-
// eslint-disable-next-line no-extend-native
15-
String.prototype.matchesValidCSSLength = () =>
16-
this.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/)
17-
18-
function _getColor(b, i) {
19-
const val = b[i]
4+
/** @returns {string} */
5+
function convertPlainColor(val) {
6+
if (!val) return '#000'
7+
val = val?.toLowerCase()
208
// color is a hex code
21-
if (val.toLowerCase().match(/#([0-9a-f]{3}){1,2}/)) return val
9+
if (val?.match(/#([0-9a-f]{3}){1,2}/)) return val
2210
// color is a function (rgb, rgba, hsl, hsla)
23-
if (val.startsWith('rgb') || val.startsWith('hsl')) {
24-
let color = val;
25-
if (!val.endsWith(')'))
26-
for (let j = 1; !b[i + j - 1].endsWith(')'); j++) {
27-
color += b[i + j]
28-
}
29-
if (color[3] === 'a')
30-
color = color.replace('a', '').replace(/,[^),]+\)/, ')')
31-
return color
32-
}
11+
else if (val?.match(/^(rgb|hsl)a?\(([^,]{1,3},? *){3}(\d*\.?\d+)?\)/))
12+
return val
13+
.replace('a', '')
14+
.replace(/\((([\d%]{1,3}, *){2}([\d%]{1,3}))(, *\d*\.?\d+)?\)/, '($1)')
3315
// color is a html color name
34-
if (CSS_COLOR_NAMES.map(color => color.toLowerCase())
16+
else if (CSS_COLOR_NAMES.map(color => color.toLowerCase())
3517
.includes(val.toLowerCase()))
3618
return val
37-
if (val === 'currentcolor') {
19+
else if (val === 'currentcolor') {
3820
return 'currentcolor'
39-
}
40-
return false
41-
}
42-
43-
function _getImage(b, i) {
44-
const val = b[i]
45-
46-
if (val.startsWith('url')) {
47-
let url = val;
48-
for (let j = 1; b[i + j]; j++) {
49-
url += b[i + j]
50-
}
51-
url = /url\(("[^"]+"|'[^']+'|[^)]+)\)/g.exec(url)
52-
url = url ? url[1] : false
53-
url = url?.startsWith('"') || url?.startsWith("'")
54-
? url.substr(1, url.length - 2)
55-
: url
56-
return url
57-
}
58-
}
59-
60-
function _getImageSize(b, i, element) {
61-
// "val" is always what is defined in backgrund-size ([i]th argument)
62-
const val = b[i]
63-
64-
if (['cover', 'contain'].includes(val)) {
65-
return [val, null]
66-
}
67-
68-
return __getTwoNumerics(b, i, element, htmlBackgroundSizeNotSvgError)
21+
} else return '#000'
6922
}
7023

71-
function _getPosition(b, i, element) {
72-
// "val" is always what is defined in backgrund-size ([i]th argument)
73-
const val = b[i]
74-
const allWords = ['top', 'bottom', 'left', 'right', 'center']
75-
76-
if (b.length === 1 && allWords.includes(val)) {
77-
if (val === 'center')
78-
return ['center', 'center']
79-
else if (['left', 'right'].includes(val))
80-
return [val, 0]
81-
else if (['top', 'bottom'].includes(val))
82-
return [0, val]
83-
}
84-
85-
const a = [0, 0]
86-
87-
if (allWords.includes(val)) {
88-
if (b[i + 1].matchesValidCSSLength()) {
89-
90-
}
91-
}
92-
93-
/*if (['cover', 'contain'].includes(val)) {
94-
return [val, null]
95-
}*/
96-
97-
return __getTwoNumerics(b, i, element, htmlBackgroundPositionNotSvgError)
98-
}
99-
100-
function __getTwoNumerics(b, i, element, err) {
101-
const returnIfCSSNumeric = (val, throwErr) => {
102-
if (val?.endsWith('%'))
103-
return val
104-
else if (val?.matchesValidCSSLength()) {
105-
unitCheck(val, throwErr ? err : undefined)
106-
return toPx(element, val)
107-
} else
108-
return null
109-
}
110-
111-
const convertedVal = returnIfCSSNumeric(b[i], true) // has null as fallback already
112-
// "background-size: 50% 50%" is different to "background-size: 50%"
113-
return [convertedVal, returnIfCSSNumeric(b[i + 1])]
114-
}
115-
116-
function _getOpacity(b, i) {
117-
let val = b[i]
118-
if (val.startsWith('rgba') || val.startsWith('hsla')) {
119-
if (!val.endsWith(')'))
120-
for (let j = 1; !b[i + j - 1].endsWith(')'); j++) {
121-
val += b[i + j]
122-
}
123-
return val.replace(/(rgb|hsl)a?\(([^,)]+,){3}/, '').replace(/\)$/, '')
124-
}
125-
if (b.length - 1 === i)
126-
return 1
127-
}
128-
129-
function _getRepeat(b, i) {
130-
let val = b[i]
131-
if (val === 'repeat-x')
132-
return ['repeat', 'no-repeat']
133-
else if (val === 'repeat-y')
134-
return ['no-repeat', 'repeat']
135-
else if (['repeat', 'space', 'round', 'no-repeat'].includes(val)) {
136-
if (['repeat', 'space', 'round', 'no-repeat'].includes(b[i + 1] || ''))
137-
return [val, b[i + 1]]
138-
else
139-
return [val, val]
140-
}
24+
/** @returns {number} */
25+
function convertColorOpacity(val) {
26+
if (val?.startsWith('rgba') || val?.startsWith('hsla')) {
27+
return Number(val.match(/(\d*\.?\d+)?\)$/)[1])
28+
} else return 1
14129
}
14230

14331
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.`
14432
const htmlBorderLengthNotSvgError =
14533
new Error(htmlLengthNotSvgErrorTemplate('border lengths', '"thin", "medium", "thick"'))
146-
const htmlBackgroundSizeNotSvgError =
147-
new Error(htmlLengthNotSvgErrorTemplate('background size', '"cover", "contain"'))
148-
const htmlBackgroundPositionNotSvgError =
149-
new Error(htmlLengthNotSvgErrorTemplate('background position', '"top", "bottom", "left", "right", "center"'))
150-
151-
function unitCheck(length, err) {
152-
if (length?.match(/(cap|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g))
34+
const htmlBorderRadiusNotSvgError =
35+
new Error(htmlLengthNotSvgErrorTemplate('border radii'))
36+
37+
function toNumber(length, element, err) {
38+
if (!length) return false
39+
if (typeof length === 'number' || !length.match(/\D+/))
40+
return Number(length);
41+
else if (length?.match(/(cap|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g))
15342
if (err) throw err
15443
else return false
155-
return length
44+
else if (length?.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/))
45+
return toPx(element, length)
15646
}
15747

158-
function _getWidth(border, i, element) {
159-
const val = border[i]
160-
// width is 0
161-
if (val === '0') return 0
48+
/** @returns {number} */
49+
function convertBorderWidth(val, element) {
50+
if (!val) return 0
16251
// width is a word
163-
if (val.toLowerCase() === 'thin') return 1
164-
if (val.toLowerCase() === 'medium') return 3
165-
if (val.toLowerCase() === 'thick') return 5
166-
unitCheck(val, htmlBorderLengthNotSvgError)
52+
if (val?.toLowerCase() === 'thin')
53+
return 1
54+
else if (val?.toLowerCase() === 'medium')
55+
return 3
56+
else if (val?.toLowerCase() === 'thick')
57+
return 5
16758
// width is <length>
168-
if (val.matchesValidCSSLength())
169-
return toPx(element, val)
170-
return false
59+
else
60+
return toNumber(val, element, htmlBorderLengthNotSvgError) || 0
17161
}
17262

173-
/** @returns {number} */
174-
const getWidth = (s, el) => _getAttributeFromString(s, _getWidth, el),
175-
/** @returns {string} */
176-
getImage = s => _getAttributeFromString(s, _getImage),
177-
/** @returns {Array<string|number>} */
178-
getImageSize = (s, el) => _getAttributeFromString(s, _getImageSize, el),
179-
/** @returns {Array<string|number>} */
180-
getPosition = (s, el) => _getAttributeFromString(s, _getPosition, el),
181-
/** @returns {string} */
182-
getColor = s => _getAttributeFromString(s, _getColor),
183-
/** @returns {Array<string>} */
184-
getRepeat = s => _getAttributeFromString(s, _getRepeat),
185-
/** @returns {number} */
186-
getOpacity = s => _getAttributeFromString(s, _getOpacity)
187-
188-
export {getWidth, getImage, getImageSize, getPosition, getColor, getRepeat, getOpacity}
63+
export {convertPlainColor, convertColorOpacity, convertBorderWidth, toNumber, htmlBorderRadiusNotSvgError}
File renamed without changes.

src/external/bobspace:html-colors.js

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)