diff --git a/.babelrc b/.babelrc
index 90d355d..f9e2a23 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,8 +1,7 @@
{
"presets": [
"@babel/react",
- "@babel/env",
- "minify"
+ "@babel/env"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
diff --git a/README.md b/README.md
index 2445258..9334147 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/img/compare.svg b/img/compare.svg
index 6833db0..a187b5f 100644
--- a/img/compare.svg
+++ b/img/compare.svg
@@ -8,7 +8,7 @@
not hunky-dory
default html corners
-
+
very much hunky-dory
smooth corners via svg
diff --git a/package-lock.json b/package-lock.json
index de579d3..43c5601 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "react-round-div",
- "version": "1.0.0",
+ "version": "1.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "react-round-div",
- "version": "1.0.0",
+ "version": "1.1.0",
"license": "MIT",
"dependencies": {
"prop-types": "^15.7.2",
@@ -27,16 +27,15 @@
}
},
"node_modules/@babel/cli": {
- "version": "7.13.14",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz",
- "integrity": "sha512-zmEFV8WBRsW+mPQumO1/4b34QNALBVReaiHJOkxhUsdo/AvYM62c+SKSuLi2aZ42t3ocK6OI0uwUXRvrIbREZw==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.14.5.tgz",
+ "integrity": "sha512-poegjhRvXHWO0EAsnYajwYZuqcz7gyfxwfaecUESxDujrqOivf3zrjFbub8IJkrqEaz3fvJWh001EzxBub54fg==",
"dev": true,
"dependencies": {
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
- "lodash": "^4.17.19",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
@@ -45,8 +44,11 @@
"babel": "bin/babel.js",
"babel-external-helpers": "bin/babel-external-helpers.js"
},
+ "engines": {
+ "node": ">=6.9.0"
+ },
"optionalDependencies": {
- "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents",
+ "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.2",
"chokidar": "^3.4.0"
},
"peerDependencies": {
@@ -2183,16 +2185,16 @@
}
},
"node_modules/@nicolo-ribaudo/chokidar-2": {
- "version": "2.1.8-no-fsevents",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz",
- "integrity": "sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w==",
+ "version": "2.1.8-no-fsevents.2",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz",
+ "integrity": "sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg==",
"dev": true,
"optional": true,
"dependencies": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
- "glob-parent": "^3.1.0",
+ "glob-parent": "^5.1.2",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
@@ -3128,14 +3130,14 @@
"dev": true
},
"node_modules/browserslist": {
- "version": "4.16.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.4.tgz",
- "integrity": "sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==",
+ "version": "4.16.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+ "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
"dev": true,
"dependencies": {
- "caniuse-lite": "^1.0.30001208",
+ "caniuse-lite": "^1.0.30001219",
"colorette": "^1.2.2",
- "electron-to-chromium": "^1.3.712",
+ "electron-to-chromium": "^1.3.723",
"escalade": "^3.1.1",
"node-releases": "^1.1.71"
},
@@ -3217,10 +3219,14 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001208",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz",
- "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==",
- "dev": true
+ "version": "1.0.30001237",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz",
+ "integrity": "sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ }
},
"node_modules/capture-exit": {
"version": "2.0.0",
@@ -3335,19 +3341,6 @@
"node": ">=8"
}
},
- "node_modules/chokidar/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "optional": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/chokidar/node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -3828,9 +3821,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.3.717",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz",
- "integrity": "sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ==",
+ "version": "1.3.752",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz",
+ "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==",
"dev": true
},
"node_modules/emittery": {
@@ -4414,27 +4407,16 @@
}
},
"node_modules/glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
- "dev": true,
- "optional": true,
- "dependencies": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- }
- },
- "node_modules/glob-parent/node_modules/is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"optional": true,
"dependencies": {
- "is-extglob": "^2.1.0"
+ "is-glob": "^4.0.1"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">= 6"
}
},
"node_modules/globals": {
@@ -7741,13 +7723,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/path-dirname": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
- "dev": true,
- "optional": true
- },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -9755,9 +9730,9 @@
}
},
"node_modules/ws": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
- "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==",
+ "version": "7.4.6",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"dev": true,
"engines": {
"node": ">=8.3.0"
@@ -9837,18 +9812,17 @@
},
"dependencies": {
"@babel/cli": {
- "version": "7.13.14",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz",
- "integrity": "sha512-zmEFV8WBRsW+mPQumO1/4b34QNALBVReaiHJOkxhUsdo/AvYM62c+SKSuLi2aZ42t3ocK6OI0uwUXRvrIbREZw==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.14.5.tgz",
+ "integrity": "sha512-poegjhRvXHWO0EAsnYajwYZuqcz7gyfxwfaecUESxDujrqOivf3zrjFbub8IJkrqEaz3fvJWh001EzxBub54fg==",
"dev": true,
"requires": {
- "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents",
+ "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.2",
"chokidar": "^3.4.0",
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
- "lodash": "^4.17.19",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
@@ -11561,16 +11535,16 @@
}
},
"@nicolo-ribaudo/chokidar-2": {
- "version": "2.1.8-no-fsevents",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz",
- "integrity": "sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w==",
+ "version": "2.1.8-no-fsevents.2",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz",
+ "integrity": "sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
- "glob-parent": "^3.1.0",
+ "glob-parent": "^5.1.2",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
@@ -12385,14 +12359,14 @@
"dev": true
},
"browserslist": {
- "version": "4.16.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.4.tgz",
- "integrity": "sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==",
+ "version": "4.16.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+ "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
"dev": true,
"requires": {
- "caniuse-lite": "^1.0.30001208",
+ "caniuse-lite": "^1.0.30001219",
"colorette": "^1.2.2",
- "electron-to-chromium": "^1.3.712",
+ "electron-to-chromium": "^1.3.723",
"escalade": "^3.1.1",
"node-releases": "^1.1.71"
}
@@ -12452,9 +12426,9 @@
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30001208",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz",
- "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==",
+ "version": "1.0.30001237",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz",
+ "integrity": "sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==",
"dev": true
},
"capture-exit": {
@@ -12544,16 +12518,6 @@
"to-regex-range": "^5.0.1"
}
},
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "optional": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -12943,9 +12907,9 @@
}
},
"electron-to-chromium": {
- "version": "1.3.717",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz",
- "integrity": "sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ==",
+ "version": "1.3.752",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz",
+ "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==",
"dev": true
},
"emittery": {
@@ -13399,26 +13363,13 @@
}
},
"glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"optional": true,
"requires": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- },
- "dependencies": {
- "is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
- "dev": true,
- "optional": true,
- "requires": {
- "is-extglob": "^2.1.0"
- }
- }
+ "is-glob": "^4.0.1"
}
},
"globals": {
@@ -15939,13 +15890,6 @@
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
"dev": true
},
- "path-dirname": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
- "dev": true,
- "optional": true
- },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -17557,9 +17501,9 @@
}
},
"ws": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
- "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==",
+ "version": "7.4.6",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"dev": true,
"requires": {}
},
diff --git a/package.json b/package.json
index 359557d..8297208 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-round-div",
- "version": "1.0.0",
+ "version": "1.1.0",
"description": "Make your rounded corners look phenomenal with g2 continuity.",
"main": "dist/main.js",
"scripts": {
diff --git a/src/css-utils.js b/src/css-utils.js
index 7d1e742..94d9c7c 100644
--- a/src/css-utils.js
+++ b/src/css-utils.js
@@ -1,75 +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) {
- const res = method(string, Number(i), ...data)
- if (res) return res
- }
-}
-
-function _getColor(border, i) {
- const val = border[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; !border[i + j - 1].endsWith(')'); j++) {
- color += border[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())
- .includes(val.toLowerCase())
- ) return val
- return false
+ else if (CSS_COLOR_NAMES.map(color => color.toLowerCase())
+ .includes(val.toLowerCase()))
+ return val
+ else if (val === 'currentcolor') {
+ return 'currentcolor'
+ } else return '#000'
}
-function _getOpacity(border, i) {
- let val = border[i]
- if (val.startsWith('rgba') || val.startsWith('hsla')) {
- if (!val.endsWith(')'))
- for (let j = 1; !border[i + j - 1].endsWith(')'); j++) {
- val += border[i + j]
- }
- return val.replace(/(rgb|hsl)a?\(([^,)]+,){3}/, '').replace(/\)$/, '')
- }
- if (border.length - 1 === i)
- return 1
+/** @returns {number} */
+function convertColorOpacity(val) {
+ if (val?.startsWith('rgba') || val?.startsWith('hsla')) {
+ return Number(val.match(/(\d*\.?\d+)?\)$/)[1])
+ } else return 1
}
-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|ic|lh|rlh|vi|vm|vb|Q|mozmm)/g)) throw htmlLengthNotSvgError
- return length
+const htmlLengthNotSvgErrorTemplate = (a, b) => ` ${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 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
+ 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)
+ if (val?.toLowerCase() === 'thin')
+ return 1
+ else if (val?.toLowerCase() === 'medium')
+ return 3
+ else if (val?.toLowerCase() === 'thick')
+ return 5
// width is
- 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
+ else
+ return toNumber(val, element, htmlBorderLengthNotSvgError) || 0
}
-const getWidth = s => _getAttributeFromString(s, _getWidth),
- getColor = s => _getAttributeFromString(s, _getColor),
- getOpacity = s => _getAttributeFromString(s, _getOpacity)
-
-export {getWidth, getColor, unitCheck, getOpacity}
+export {convertPlainColor, convertColorOpacity, convertBorderWidth, toNumber, htmlBorderRadiusNotSvgError}
diff --git a/src/react-shadow-dom.js b/src/external/apearce:eact-shadow-dom.js
similarity index 100%
rename from src/react-shadow-dom.js
rename to src/external/apearce:eact-shadow-dom.js
diff --git a/src/external/bobspace:html-colors.js b/src/external/bobspace:html-colors.js
new file mode 100644
index 0000000..3d69e4b
--- /dev/null
+++ b/src/external/bobspace:html-colors.js
@@ -0,0 +1,8 @@
+// CSS Color Names
+// Compiled by @bobspace.
+//
+// A javascript array containing all of the color names listed in the CSS Spec.
+// The full list can be found here: https://www.w3schools.com/cssref/css_colors.asp
+// Use it as you please, 'cuz you can't, like, own a color, man.
+
+const CSS_COLOR_NAMES=["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige","Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood","CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk","Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGrey","DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","DarkOrange","DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue","DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink","DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite","ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray","Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo","Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon","LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray","LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen","LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue","LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon","MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple","MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise","MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin","NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed","Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed","PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple","RebeccaPurple","Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown","SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray","SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle","Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"];export default CSS_COLOR_NAMES
diff --git a/src/getMatchedCSSRules-polyfill.js b/src/external/getMatchedCSSRules-polyfill.js
similarity index 100%
rename from src/getMatchedCSSRules-polyfill.js
rename to src/external/getMatchedCSSRules-polyfill.js
diff --git a/src/css-length-converter.js b/src/external/heygrady:units:length.js
similarity index 100%
rename from src/css-length-converter.js
rename to src/external/heygrady:units:length.js
diff --git a/src/styles-extractor.js b/src/external/styles-extractor.js
similarity index 100%
rename from src/styles-extractor.js
rename to src/external/styles-extractor.js
diff --git a/src/generator.js b/src/generator.js
new file mode 100644
index 0000000..1331f5b
--- /dev/null
+++ b/src/generator.js
@@ -0,0 +1,111 @@
+/**
+ * @param {number} height
+ * @param {number} width
+ * @param {number | Array} radius
+ *
+ * @returns {string} SVG path data
+ * */
+export default function generateSvgSquircle(height, width, radius) {
+ /* from right to left top left corner upper (right half) */
+ const ratios = [1.528665037, 1.0884928889, 0.8684068148, 0.07491140741, 0.6314939259, 0.1690595556, 0.3728238519];
+ const roundToNthPlace = 1;
+
+ if (typeof radius === 'number')
+ radius = Array(4).fill(radius)
+ else if (radius.length === 2)
+ radius.push(radius[0])
+ if (radius.length === 3)
+ radius.push(radius[1])
+
+ height = Number(height);
+ width = Number(width);
+
+ const _rawRadius = [...radius].map(n => Number(n))
+ const max = radius.length - 1
+ const next = i => i === max ? 0 : i + 1
+ const prev = i => i === 0 ? max : i - 1
+ radius = _rawRadius.map((radius, i) =>
+ Math.min(
+ radius,
+ Math.min(
+ height - _rawRadius[i % 2 === 0 ? prev(i) : next(i)],
+ height / 2
+ ),
+ Math.min(
+ width - _rawRadius[i % 2 === 0 ? next(i) : prev(i)],
+ width / 2
+ )
+ )
+ )
+
+ const [a0x, a1x, a2x, a3y, a3x, b1y, b1x] = Array(7)
+ .fill(Array(4).fill(0))
+ .map((a, i) => a.map((b, j) => radius[j] * ratios[i])),
+ [b0y, b0x] = [a3y, a3x]
+
+ if (isNaN(height)) throw new Error(`'height' must be a number`);
+ if (isNaN(width)) throw new Error(`'width' must be a number`);
+ if (radius.includes(NaN)) throw new Error(`'radius' must be a number or an array containing 2 to 4 numbers`);
+
+ const a0xF = x => Math.min(x / 2, a0x[0]),
+ a0xw = a0xF(width),
+ a0xh = a0xF(height)
+
+ /*function mapRange(number, in_min, in_max, out_min, out_max) {
+ return (number - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+ }*/
+
+ // const maxRadius = Math.max(...radius);
+
+ const yOffsetF = (x) => 0,
+ hyOffset = yOffsetF(height) || 0,
+ wyOffset = yOffsetF(width) || 0
+
+ const startPoint = `${a0xw},${wyOffset}`
+
+ return `M${startPoint}
+ ${width / 2 < a0x[1]
+ ? ''
+ : `L${width - a0xw},0`
+ }
+
+ C${width - a1x[1]},0,${width - a2x[1]},0,${width - a3x[1]},${a3y[1]}
+ C${width - b1x[1]},${b1y[1]},${width - b1y[1]},${b1x[1]},${width - b0y[1]},${b0x[1]}
+ C${width},${a2x[1]},${width},${a1x[1]},
+
+ ${width - hyOffset},${a0xh}
+ ${height / 2 < a0x[2]
+ ? ''
+ : `L${width},${height - a0xh}`
+ }
+
+ C${width},${height - a1x[2]},${width},${height - a2x[2]},${width - a3y[2]},${height - a3x[2]}
+ C${width - b1y[2]},${height - b1x[2]},${width - b1x[2]},${height - b1y[2]},${width - b0x[2]},${height - b0y[2]}
+ C${width - a2x[2]},${height},${width - a1x[2]},${height},
+
+ ${width - a0xw},${height - wyOffset}
+ ${width / 2 < a0x[3]
+ ? ''
+ : `L${a0xw},${height}`
+ }
+
+ C${a1x[3]},${height},${a2x[3]},${height},${a3x[3]},${height - a3y[3]}
+ C${b1x[3]},${height - b1y[3]},${b1y[3]},${height - b1x[3]},${b0y[3]},${height - b0x[3]}
+ C0,${height - a2x[3]},0,${height - a1x[3]},
+
+ ${hyOffset},${height - a0xh}
+ ${height / 2 < a0x[0]
+ ? ''
+ : `L0,${a0xh}`
+ }
+
+ C0,${a1x[0]},0,${a2x[0]},${a3y[0]},${a3x[0]}
+ C${b1y[0]},${b1x[0]},${b1x[0]},${b1y[0]},${b0x[0]},${b0y[0]}
+ C${a2x[0]},0,${a1x[0]},0,${startPoint}
+ Z`
+ .replace(/[\n ]/g, '')
+ .replace(/NaN/g, '0')
+ .replace(/\d+\.\d+/g, match =>
+ Math.round(Number(match) * (10 ** roundToNthPlace)) / (10 ** roundToNthPlace)
+ )
+}
diff --git a/src/html-colors.js b/src/html-colors.js
deleted file mode 100644
index 7ff5a77..0000000
--- a/src/html-colors.js
+++ /dev/null
@@ -1,159 +0,0 @@
-// CSS Color Names
-// Compiled by @bobspace.
-//
-// A javascript array containing all of the color names listed in the CSS Spec.
-// The full list can be found here: https://www.w3schools.com/cssref/css_colors.asp
-// Use it as you please, 'cuz you can't, like, own a color, man.
-
-const CSS_COLOR_NAMES = [
- "AliceBlue",
- "AntiqueWhite",
- "Aqua",
- "Aquamarine",
- "Azure",
- "Beige",
- "Bisque",
- "Black",
- "BlanchedAlmond",
- "Blue",
- "BlueViolet",
- "Brown",
- "BurlyWood",
- "CadetBlue",
- "Chartreuse",
- "Chocolate",
- "Coral",
- "CornflowerBlue",
- "Cornsilk",
- "Crimson",
- "Cyan",
- "DarkBlue",
- "DarkCyan",
- "DarkGoldenRod",
- "DarkGray",
- "DarkGrey",
- "DarkGreen",
- "DarkKhaki",
- "DarkMagenta",
- "DarkOliveGreen",
- "DarkOrange",
- "DarkOrchid",
- "DarkRed",
- "DarkSalmon",
- "DarkSeaGreen",
- "DarkSlateBlue",
- "DarkSlateGray",
- "DarkSlateGrey",
- "DarkTurquoise",
- "DarkViolet",
- "DeepPink",
- "DeepSkyBlue",
- "DimGray",
- "DimGrey",
- "DodgerBlue",
- "FireBrick",
- "FloralWhite",
- "ForestGreen",
- "Fuchsia",
- "Gainsboro",
- "GhostWhite",
- "Gold",
- "GoldenRod",
- "Gray",
- "Grey",
- "Green",
- "GreenYellow",
- "HoneyDew",
- "HotPink",
- "IndianRed",
- "Indigo",
- "Ivory",
- "Khaki",
- "Lavender",
- "LavenderBlush",
- "LawnGreen",
- "LemonChiffon",
- "LightBlue",
- "LightCoral",
- "LightCyan",
- "LightGoldenRodYellow",
- "LightGray",
- "LightGrey",
- "LightGreen",
- "LightPink",
- "LightSalmon",
- "LightSeaGreen",
- "LightSkyBlue",
- "LightSlateGray",
- "LightSlateGrey",
- "LightSteelBlue",
- "LightYellow",
- "Lime",
- "LimeGreen",
- "Linen",
- "Magenta",
- "Maroon",
- "MediumAquaMarine",
- "MediumBlue",
- "MediumOrchid",
- "MediumPurple",
- "MediumSeaGreen",
- "MediumSlateBlue",
- "MediumSpringGreen",
- "MediumTurquoise",
- "MediumVioletRed",
- "MidnightBlue",
- "MintCream",
- "MistyRose",
- "Moccasin",
- "NavajoWhite",
- "Navy",
- "OldLace",
- "Olive",
- "OliveDrab",
- "Orange",
- "OrangeRed",
- "Orchid",
- "PaleGoldenRod",
- "PaleGreen",
- "PaleTurquoise",
- "PaleVioletRed",
- "PapayaWhip",
- "PeachPuff",
- "Peru",
- "Pink",
- "Plum",
- "PowderBlue",
- "Purple",
- "RebeccaPurple",
- "Red",
- "RosyBrown",
- "RoyalBlue",
- "SaddleBrown",
- "Salmon",
- "SandyBrown",
- "SeaGreen",
- "SeaShell",
- "Sienna",
- "Silver",
- "SkyBlue",
- "SlateBlue",
- "SlateGray",
- "SlateGrey",
- "Snow",
- "SpringGreen",
- "SteelBlue",
- "Tan",
- "Teal",
- "Thistle",
- "Tomato",
- "Turquoise",
- "Violet",
- "Wheat",
- "White",
- "WhiteSmoke",
- "Yellow",
- "YellowGreen",
-];
-
-export default CSS_COLOR_NAMES
diff --git a/src/main.js b/src/main.js
index 7f7682d..3722e79 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,92 +1,121 @@
-import React, {useRef, useEffect, useState} from 'react';
-import './getMatchedCSSRules-polyfill'
-import updateStates from "./updateStates";
-import ShadowRoot from "./react-shadow-dom";
+import React, {useRef, useEffect, useState, useCallback} from 'react'
+import generateSvgSquircle from './generator'
+import './external/getMatchedCSSRules-polyfill'
+import updateStates from "./updateStates"
+import ShadowRoot from "./external/apearce:eact-shadow-dom"
+import attachCSSWatcher from './styleSheetWatcher'
+import getMaskPaths from './mask-generator'
-export default function RoundDiv({clip, style, children, ...props}) {
+export default function RoundDiv({style, children, ...props}) {
+ // welcome to react states hell
+ const [position, setPosition] = useState([0, 0])
const [height, setHeight] = useState(0)
const [width, setWidth] = useState(0)
- const [radius, setRadius] = useState(0)
- const [background, setBackground] = useState('transparent')
- const [backgroundOpacity, setBackgroundOpacity] = useState(0)
- const [borderColor, setBorderColor] = useState('transparent')
- const [borderWidth, setBorderWidth] = useState(1)
- const [borderOpacity, setBorderOpacity] = useState(1)
+ const [radius, setRadius] = useState(Array(4).fill(0))
- const div = useRef()
+ const [borderColor, setBorderColor] = useState(Array(4).fill('transparent'))
+ const [borderOpacity, setBorderOpacity] = useState(Array(4).fill(1))
+ const [borderWidth, setBorderWidth] = useState(Array(4).fill(0))
- useEffect(() => {
- // attach shadow root to div
- if (!div.current?.shadowRoot)
- div.current?.attachShadow({mode: 'open'})
- }, [])
+ const [path, setPath] = useState('Z')
+ const [innerPath, setInnerPath] = useState('Z')
+ const [maskPaths, setMaskPaths] = useState('Z')
- useEffect(() => updateStates({
+ const div = useRef()
+
+ const updateStatesWithArgs = useCallback(() => updateStates({
div,
style,
+ setPosition,
setHeight,
setWidth,
setRadius,
- setBackground,
- setBackgroundOpacity,
setBorderColor,
setBorderWidth,
setBorderOpacity
- }), [div, clip, style])
+ }), [style])
+
+ useEffect(updateStatesWithArgs, [style, updateStatesWithArgs])
+
+ useEffect(() => {
+ attachCSSWatcher(() => updateStatesWithArgs())
+ }, [updateStatesWithArgs])
+
+ useEffect(() => {
+ setPath(generateSvgSquircle(height, width, radius))
+ setInnerPath(generateSvgSquircle(
+ height - (borderWidth[0] + borderWidth[2]),
+ width - (borderWidth[1] + borderWidth[3]),
+ radius.map((val, i) =>
+ Math.max(0,
+ val - Math.max(borderWidth[i], borderWidth[i === 0 ? 3 : i - 1])
+ )
+ )
+ ).replace(
+ /(\d+(\.\d+)?),(\d+(\.\d+)?)/g,
+ match => match.split(',').map((number, i) =>
+ Number(number) + (i === 0 ? borderWidth[3] : borderWidth[0])
+ ).join(',')
+ ))
+
+ // prevents unnecessary re-renders:
+ // single value states (numbers and strings) prevent this out of the box,
+ // complex states (objects, arrays, etc.) don't, so here it is manually for objects (non-nested)
+ const lazySetObjectsState = (setState, newState) =>
+ setState(oldState => {
+ if (areEqualObjects(oldState, newState)) return oldState
+ else return newState
+ })
+
+ function areEqualObjects(a, b) {
+ if (Object.keys(a).length !== Object.keys(b).length) return false
+ for (let key in a) {
+ if (a[key] !== b[key]) return false
+ }
+ return true
+ }
+
+ lazySetObjectsState(setMaskPaths, getMaskPaths(borderWidth, height, width, radius))
+ }, [height, width, radius, borderWidth])
+
const divStyle = {
- ...style
+ ...style,
+ clipPath: `path("${path}")`,
+ borderColor: 'transparent'
}
- divStyle.background = 'transparent'
- divStyle.borderWidth = divStyle.borderWidth || '0'
- divStyle.borderColor = 'transparent'
-
return
-
-
+
{children}
}
-
-function generateSvgSquircle(height, width, radius, clip) {
- const RADIUS_SCALE_FACTOR = 1.25
- height = Number(height);
- width = Number(width);
- radius = clip === false
- ? Number(radius)
- : Math.min(Number(radius), height / 2 / RADIUS_SCALE_FACTOR, width / 2 / RADIUS_SCALE_FACTOR);
- if (isNaN(height)) throw new Error(`'height' must be a number`);
- if (isNaN(width)) throw new Error(`'width' must be a number`);
- if (isNaN(radius)) throw new Error(`'radius' must be a number`);
- const point = RADIUS_SCALE_FACTOR * radius,
- bezier = radius / 3;
-
- return `M 0,${point} C 0,${bezier}, ${bezier},0, ${point},0
- L ${width - point},0 C ${width - bezier},0, ${width},${bezier}, ${width},${point}
- L ${width},${height - point} C ${width},${height - bezier}, ${width - bezier},${height}, ${width - point},${height}
- L ${point},${height} C ${bezier},${height}, 0,${height - bezier}, 0,${height - point}
- Z`;
-}
diff --git a/src/mask-generator.js b/src/mask-generator.js
new file mode 100644
index 0000000..c2d9806
--- /dev/null
+++ b/src/mask-generator.js
@@ -0,0 +1,70 @@
+export default function getMaskPaths(borderWidth, height, width, radius) {
+ // console.log(borderWidth, height, width, radius)
+ const allSides = ['top', 'right', 'bottom', 'left']
+ const max = allSides.length - 1
+ const next = i => i === max ? 0 : i + 1
+ const prev = i => i === 0 ? max : i - 1
+
+ /**
+ * @constant
+ * @type {Array}
+ * */
+ const allRatios = allSides.map((side, i) =>
+ ((i % 2 === 0 ? height : width)
+ - Math.max(borderWidth[next(next(i))], radius[prev(i)], radius[prev(prev(i))])
+ )
+ / borderWidth[i]
+ )
+
+ /**
+ * @param {string} posY
+ * @param {string} posX
+ * @param {boolean} [inside]
+ * @param {string} [borderPos] needed if inside=true
+ * */
+ const makePoint = (posY, posX, inside, borderPos) => {
+ return makeValue(posX, inside, borderPos) + ',' + makeValue(posY, inside, borderPos)
+ }
+
+ /**
+ * @param {string} pos
+ * @param {boolean} [inside]
+ * @param {string} [borderPos] needed if inside=true
+ * */
+ const makeValue = (pos, inside, borderPos) => {
+ let v = -borderWidth[allSides.indexOf(pos)]
+
+ if (inside) {
+ const i = allSides.indexOf(borderPos)
+ v *= -Math.min(allRatios[prev(i)], allRatios[i], allRatios[next(i)])
+ }
+
+ if (pos === 'right')
+ v = -v + Number(width)
+ else if (pos === 'bottom')
+ v = -v + Number(height)
+
+ return v
+ }
+
+ const makeMaskPath = (side) => {
+ const i = allSides.indexOf(side)
+ const isH = i % 2 === 0 // is "top" or "bottom"
+ const T = isH ? 'H' : 'V'
+ const nextSide = allSides[next(i)]
+ const prevSide = allSides[prev(i)]
+ const prevIfH = isH ? prevSide : side
+ const prevIfV = !isH ? prevSide : side
+ const nextIfH = isH ? nextSide : side
+ const nextIfV = !isH ? nextSide : side
+
+ return ('M' + makePoint(prevIfV, prevIfH, true, side) +
+ T + makeValue(nextSide, true, side) +
+ 'L' + makePoint(nextIfV, nextIfH) +
+ T + makeValue(prevSide) + 'Z').replace(/NaN/g, '0')
+ }
+
+ return Object.fromEntries(
+ allSides.map(side => [side, makeMaskPath(side)])
+ )
+}
diff --git a/src/styleSheetWatcher.js b/src/styleSheetWatcher.js
new file mode 100644
index 0000000..0c5bfd1
--- /dev/null
+++ b/src/styleSheetWatcher.js
@@ -0,0 +1,32 @@
+const CSSChangeEvent = new CustomEvent('css-change');
+
+export default function attachCSSWatcher(callback) {
+ CSSWatcher.addEventListener('css-change', () => callback())
+}
+
+const CSSWatcher = new EventTarget()
+
+;(function watchCSS() {
+ let CSS = getCSSText()
+ setInterval(() => {
+ const newCSS = getCSSText()
+ if (CSS === newCSS) return
+ CSS = newCSS
+ CSSWatcher.dispatchEvent(CSSChangeEvent)
+ }, 30)
+ window.addEventListener('resize', () => {
+ CSS = getCSSText()
+ CSSWatcher.dispatchEvent(CSSChangeEvent)
+ })
+})()
+
+function getCSSText() {
+ let CSS = ''
+ for (let i = 0; i < document.styleSheets.length; i++) {
+ const sheet = document.styleSheets[i]
+ for (let j = 0; j < sheet.rules.length; j++) {
+ CSS += sheet.rules[j].cssText
+ }
+ }
+ return CSS
+}
diff --git a/src/updateStates.js b/src/updateStates.js
index 3bed3f9..59df963 100644
--- a/src/updateStates.js
+++ b/src/updateStates.js
@@ -1,58 +1,100 @@
-import {getColor, getOpacity, getWidth, unitCheck} from "./css-utils";
-import getStyle from "./styles-extractor";
+import {
+ convertPlainColor,
+ convertColorOpacity,
+ convertBorderWidth,
+ toNumber,
+ htmlBorderRadiusNotSvgError
+} from "./css-utils";
+import getStyle from "./external/styles-extractor";
+import ReactDOM from 'react-dom'
+
+// prevents unnecessary re-renders:
+// single value states (numbers and strings) prevent this out of the box,
+// complex states (objects, arrays, etc.) don't, so here it is manually for arrays (non-nested)
+const lazySetArrayState = (setState, newState) =>
+ setState(oldState => {
+ if (oldState.every((val, i) => val === newState[i])) return oldState
+ else return newState
+ })
export default function updateStates(args) {
- const {
- div,
- style,
- setHeight,
- setWidth,
- setRadius,
- setBackground,
- setBackgroundOpacity,
- setBorderColor,
- setBorderWidth,
- setBorderOpacity
- } = args
+ const {div, setPosition, setHeight, setWidth} = args
const boundingClientRect = div.current?.getBoundingClientRect()
+ let height, width;
if (boundingClientRect) {
- setHeight(boundingClientRect.height)
- setWidth(boundingClientRect.width)
+ lazySetArrayState(setPosition, [boundingClientRect.x, boundingClientRect.y])
+ height = boundingClientRect.height
+ width = boundingClientRect.width
+ setHeight(height)
+ setWidth(width)
}
- const divStyle = boundingClientRect ? window?.getComputedStyle(div.current) : null
- if (divStyle) {
- setRadius(Number(
- (divStyle.borderRadius || divStyle.borderTopLeftRadius)
- .replace('px', '')))
- setBackground(getColor(
- style?.background
- || style?.backgroundColor
- || (getStyle('background', div.current)?.overwritten || [])[1]?.value
- || (getStyle('background-color', div.current)?.overwritten || [])[1]?.value
- ) || 'transparent')
- setBackgroundOpacity(getOpacity(
- style?.background
- || style?.backgroundColor
- || (getStyle('background', div.current)?.overwritten || [])[1]?.value
- || (getStyle('background-color', div.current)?.overwritten || [])[1]?.value
- ) || 1)
- setBorderColor(getColor(
- style?.border
- || style?.borderColor
- || (getStyle('borderColor', div.current)?.overwritten || [])[1]?.value
- || (getStyle('borderTopColor', div.current)?.overwritten || [])[1]?.value
- ) || 'transparent')
- setBorderWidth(getWidth(
- style?.border
- || style?.borderWidth
- || unitCheck((getStyle('borderWidth', div.current)?.overwritten || [])[0]?.value)
- || unitCheck((getStyle('borderTopWidth', div.current)?.overwritten || [])[0]?.value),
- div.current) || 1)
- setBorderOpacity(getOpacity(
- style?.border
- || style?.borderColor
- || (getStyle('borderColor', div.current)?.overwritten || [])[1]?.value
- || (getStyle('borderTopColor', div.current)?.overwritten || [])[1]?.value
- ) || 1)
+
+ function camelise(str) {
+ return str?.replace(/^\w|[A-Z]|\b\w|\s+/g, function (match, index) {
+ if (+match === 0) return "";
+ return index === 0 ? match.toLowerCase() : match.toUpperCase();
+ }).replace(/-/g, '');
}
+
+ const getNthStyle = (key, n) => {
+ const returnNthOverwrittenOrCurrent = r =>
+ !r ? false :
+ r?.overwritten.length > 0
+ ? r.overwritten[n ?? 0].value
+ : r.current?.value
+
+ const normal = getStyle(key, div.current)
+ const camelised = getStyle(camelise(key), div.current)
+
+ return returnNthOverwrittenOrCurrent(normal) || returnNthOverwrittenOrCurrent(camelised)
+ };
+
+ const getBorderStyles = (key, n) => [
+ getNthStyle('border-top-' + key, n),
+ getNthStyle('border-right-' + key, n),
+ getNthStyle('border-bottom-' + key, n),
+ getNthStyle('border-left-' + key, n),
+ ]
+
+ const getBorderRadii = (n) => [
+ getNthStyle('border-top-right-radius', n),
+ getNthStyle('border-top-left-radius', n),
+ getNthStyle('border-bottom-right-radius', n),
+ getNthStyle('border-bottom-left-radius', n),
+ ]
+
+ const states = args
+ const lazySetRadius = newState => lazySetArrayState(states.setRadius, newState),
+ lazySetBorderColor = newState => lazySetArrayState(states.setBorderColor, newState),
+ lazySetBorderOpacity = newState => lazySetArrayState(states.setBorderOpacity, newState),
+ lazySetBorderWidth = newState => lazySetArrayState(states.setBorderWidth, newState)
+
+ const divStyle = div.current ? window?.getComputedStyle(div.current) : null
+ if (!divStyle) return
+ ReactDOM.unstable_batchedUpdates(() => {
+ lazySetRadius(
+ getBorderRadii(1)
+ .map(s => Math.min(
+ toNumber(s, div.current, htmlBorderRadiusNotSvgError),
+ height / 2,
+ width / 2
+ ))
+ )
+
+ // get color
+ lazySetBorderColor(
+ getBorderStyles('color', 1)
+ .map(s => convertPlainColor(s))
+ )
+ // get alpha value of color
+ lazySetBorderOpacity(
+ getBorderStyles('color', 1)
+ .map(s => convertColorOpacity(s))
+ )
+
+ lazySetBorderWidth(
+ getBorderStyles('width', 0)
+ .map(s => convertBorderWidth(s, div.current))
+ )
+ })
}