Skip to content

Commit af7a09c

Browse files
Merge pull request #2 from drinking-code/v1.1.0
Merge v1.1.0
2 parents eb35968 + a310c9a commit af7a09c

17 files changed

+521
-457
lines changed

.babelrc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
{
22
"presets": [
33
"@babel/react",
4-
"@babel/env",
5-
"minify"
4+
"@babel/env"
65
],
76
"plugins": [
87
"@babel/plugin-proposal-class-properties"

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.

img/compare.svg

Lines changed: 1 addition & 1 deletion
Loading

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-round-div",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Make your rounded corners look phenomenal with g2 continuity.",
55
"main": "dist/main.js",
66
"scripts": {

src/css-utils.js

Lines changed: 49 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +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-
const res = method(string, Number(i), ...data)
9-
if (res) return res
10-
}
11-
}
12-
13-
function _getColor(border, i) {
14-
const val = border[i]
4+
/** @returns {string} */
5+
function convertPlainColor(val) {
6+
if (!val) return '#000'
7+
val = val?.toLowerCase()
158
// color is a hex code
16-
if (val.toLowerCase().match(/#([0-9a-f]{3}){1,2}/)) return val
9+
if (val?.match(/#([0-9a-f]{3}){1,2}/)) return val
1710
// color is a function (rgb, rgba, hsl, hsla)
18-
if (val.startsWith('rgb') || val.startsWith('hsl')) {
19-
let color = val;
20-
if (!val.endsWith(')'))
21-
for (let j = 1; !border[i + j - 1].endsWith(')'); j++) {
22-
color += border[i + j]
23-
}
24-
if (color[3] === 'a')
25-
color = color.replace('a', '').replace(/,[^),]+\)/, ')')
26-
return color
27-
}
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)')
2815
// color is a html color name
29-
if (
30-
CSS_COLOR_NAMES.map(color => color.toLowerCase())
31-
.includes(val.toLowerCase())
32-
) return val
33-
return false
16+
else if (CSS_COLOR_NAMES.map(color => color.toLowerCase())
17+
.includes(val.toLowerCase()))
18+
return val
19+
else if (val === 'currentcolor') {
20+
return 'currentcolor'
21+
} else return '#000'
3422
}
3523

36-
function _getOpacity(border, i) {
37-
let val = border[i]
38-
if (val.startsWith('rgba') || val.startsWith('hsla')) {
39-
if (!val.endsWith(')'))
40-
for (let j = 1; !border[i + j - 1].endsWith(')'); j++) {
41-
val += border[i + j]
42-
}
43-
return val.replace(/(rgb|hsl)a?\(([^,)]+,){3}/, '').replace(/\)$/, '')
44-
}
45-
if (border.length - 1 === i)
46-
return 1
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
4729
}
4830

49-
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.')
50-
51-
function unitCheck(length) {
52-
if (length?.match(/(cap|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g)) throw htmlLengthNotSvgError
53-
return length
31+
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.`
32+
const htmlBorderLengthNotSvgError =
33+
new Error(htmlLengthNotSvgErrorTemplate('border lengths', '"thin", "medium", "thick"'))
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))
42+
if (err) throw err
43+
else return false
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)
5446
}
5547

56-
function _getWidth(border, i, element) {
57-
const val = border[i]
58-
// width is 0
59-
if (val === '0') return 0
48+
/** @returns {number} */
49+
function convertBorderWidth(val, element) {
50+
if (!val) return 0
6051
// width is a word
61-
if (val.toLowerCase() === 'thin') return 1
62-
if (val.toLowerCase() === 'medium') return 3
63-
if (val.toLowerCase() === 'thick') return 5
64-
unitCheck(val)
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
6558
// width is <length>
66-
if (val.match(/(\d+(\.\d+)?(ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmax|vmin|vw)|0)/))
67-
return toPx(element, val)
68-
return false
59+
else
60+
return toNumber(val, element, htmlBorderLengthNotSvgError) || 0
6961
}
7062

71-
const getWidth = s => _getAttributeFromString(s, _getWidth),
72-
getColor = s => _getAttributeFromString(s, _getColor),
73-
getOpacity = s => _getAttributeFromString(s, _getOpacity)
74-
75-
export {getWidth, getColor, unitCheck, 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.

src/generator.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @param {number} height
3+
* @param {number} width
4+
* @param {number | Array<number>} radius
5+
*
6+
* @returns {string} SVG path data
7+
* */
8+
export default function generateSvgSquircle(height, width, radius) {
9+
/* from right to left top left corner upper (right half) */
10+
const ratios = [1.528665037, 1.0884928889, 0.8684068148, 0.07491140741, 0.6314939259, 0.1690595556, 0.3728238519];
11+
const roundToNthPlace = 1;
12+
13+
if (typeof radius === 'number')
14+
radius = Array(4).fill(radius)
15+
else if (radius.length === 2)
16+
radius.push(radius[0])
17+
if (radius.length === 3)
18+
radius.push(radius[1])
19+
20+
height = Number(height);
21+
width = Number(width);
22+
23+
const _rawRadius = [...radius].map(n => Number(n))
24+
const max = radius.length - 1
25+
const next = i => i === max ? 0 : i + 1
26+
const prev = i => i === 0 ? max : i - 1
27+
radius = _rawRadius.map((radius, i) =>
28+
Math.min(
29+
radius,
30+
Math.min(
31+
height - _rawRadius[i % 2 === 0 ? prev(i) : next(i)],
32+
height / 2
33+
),
34+
Math.min(
35+
width - _rawRadius[i % 2 === 0 ? next(i) : prev(i)],
36+
width / 2
37+
)
38+
)
39+
)
40+
41+
const [a0x, a1x, a2x, a3y, a3x, b1y, b1x] = Array(7)
42+
.fill(Array(4).fill(0))
43+
.map((a, i) => a.map((b, j) => radius[j] * ratios[i])),
44+
[b0y, b0x] = [a3y, a3x]
45+
46+
if (isNaN(height)) throw new Error(`'height' must be a number`);
47+
if (isNaN(width)) throw new Error(`'width' must be a number`);
48+
if (radius.includes(NaN)) throw new Error(`'radius' must be a number or an array containing 2 to 4 numbers`);
49+
50+
const a0xF = x => Math.min(x / 2, a0x[0]),
51+
a0xw = a0xF(width),
52+
a0xh = a0xF(height)
53+
54+
/*function mapRange(number, in_min, in_max, out_min, out_max) {
55+
return (number - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
56+
}*/
57+
58+
// const maxRadius = Math.max(...radius);
59+
60+
const yOffsetF = (x) => 0,
61+
hyOffset = yOffsetF(height) || 0,
62+
wyOffset = yOffsetF(width) || 0
63+
64+
const startPoint = `${a0xw},${wyOffset}`
65+
66+
return `M${startPoint}
67+
${width / 2 < a0x[1]
68+
? ''
69+
: `L${width - a0xw},0`
70+
}
71+
72+
C${width - a1x[1]},0,${width - a2x[1]},0,${width - a3x[1]},${a3y[1]}
73+
C${width - b1x[1]},${b1y[1]},${width - b1y[1]},${b1x[1]},${width - b0y[1]},${b0x[1]}
74+
C${width},${a2x[1]},${width},${a1x[1]},
75+
76+
${width - hyOffset},${a0xh}
77+
${height / 2 < a0x[2]
78+
? ''
79+
: `L${width},${height - a0xh}`
80+
}
81+
82+
C${width},${height - a1x[2]},${width},${height - a2x[2]},${width - a3y[2]},${height - a3x[2]}
83+
C${width - b1y[2]},${height - b1x[2]},${width - b1x[2]},${height - b1y[2]},${width - b0x[2]},${height - b0y[2]}
84+
C${width - a2x[2]},${height},${width - a1x[2]},${height},
85+
86+
${width - a0xw},${height - wyOffset}
87+
${width / 2 < a0x[3]
88+
? ''
89+
: `L${a0xw},${height}`
90+
}
91+
92+
C${a1x[3]},${height},${a2x[3]},${height},${a3x[3]},${height - a3y[3]}
93+
C${b1x[3]},${height - b1y[3]},${b1y[3]},${height - b1x[3]},${b0y[3]},${height - b0x[3]}
94+
C0,${height - a2x[3]},0,${height - a1x[3]},
95+
96+
${hyOffset},${height - a0xh}
97+
${height / 2 < a0x[0]
98+
? ''
99+
: `L0,${a0xh}`
100+
}
101+
102+
C0,${a1x[0]},0,${a2x[0]},${a3y[0]},${a3x[0]}
103+
C${b1y[0]},${b1x[0]},${b1x[0]},${b1y[0]},${b0x[0]},${b0y[0]}
104+
C${a2x[0]},0,${a1x[0]},0,${startPoint}
105+
Z`
106+
.replace(/[\n ]/g, '')
107+
.replace(/NaN/g, '0')
108+
.replace(/\d+\.\d+/g, match =>
109+
Math.round(Number(match) * (10 ** roundToNthPlace)) / (10 ** roundToNthPlace)
110+
)
111+
}

0 commit comments

Comments
 (0)