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.

0 commit comments

Comments
 (0)