react-native-figma-squircle
Version:
Figma-flavored squircles for React Native
132 lines (128 loc) • 4.02 kB
JavaScript
;
import * as React from 'react';
import { View, StyleSheet, Platform } from 'react-native';
import { useState, useRef, useLayoutEffect } from 'react';
import Svg, { Path } from 'react-native-svg';
import { getSvgPath } from 'figma-squircle';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function SquircleView({
squircleParams,
children,
...rest
}) {
return /*#__PURE__*/_jsxs(View, {
...rest,
children: [/*#__PURE__*/_jsx(SquircleBackground, {
...squircleParams
}), children]
});
}
function SquircleBackground({
cornerRadius = 0,
topLeftCornerRadius,
topRightCornerRadius,
bottomRightCornerRadius,
bottomLeftCornerRadius,
cornerSmoothing,
fillColor = '#000',
strokeColor = '#000',
strokeWidth = 0
}) {
return /*#__PURE__*/_jsx(Rect, {
style: StyleSheet.absoluteFill,
children: ({
width,
height
}) => {
const hasStroke = strokeWidth > 0;
if (!hasStroke) {
const squirclePath = getSvgPath({
width,
height,
cornerSmoothing,
cornerRadius,
topLeftCornerRadius,
topRightCornerRadius,
bottomRightCornerRadius,
bottomLeftCornerRadius
});
return /*#__PURE__*/_jsx(Svg, {
width: "100%",
height: "100%",
viewBox: `0 0 ${width} ${height}`,
children: /*#__PURE__*/_jsx(Path, {
d: squirclePath,
fill: fillColor
})
});
} else {
const cornerRadii = [cornerRadius, topLeftCornerRadius, topRightCornerRadius, bottomLeftCornerRadius, bottomRightCornerRadius].filter(cornerRadius => cornerRadius && cornerRadius > 0);
const maxStrokeWidth = Math.min(...cornerRadii);
strokeWidth = Math.min(strokeWidth, maxStrokeWidth);
const insetAmount = strokeWidth / 2;
const insetSquirclePath = getSvgPath({
width: width - strokeWidth,
height: height - strokeWidth,
cornerSmoothing,
cornerRadius: getInnerRadius(cornerRadius, insetAmount),
topLeftCornerRadius: getInnerRadius(topLeftCornerRadius, insetAmount),
topRightCornerRadius: getInnerRadius(topRightCornerRadius, insetAmount),
bottomRightCornerRadius: getInnerRadius(bottomRightCornerRadius, insetAmount),
bottomLeftCornerRadius: getInnerRadius(bottomLeftCornerRadius, insetAmount)
});
return /*#__PURE__*/_jsx(Svg, {
width: "100%",
height: "100%",
viewBox: `0 0 ${width} ${height}`,
children: /*#__PURE__*/_jsx(Path, {
d: insetSquirclePath,
fill: fillColor,
stroke: strokeColor,
strokeWidth: strokeWidth,
translate: insetAmount
})
});
}
}
});
}
function getInnerRadius(radius, insetAmount) {
if (radius) {
return Math.max(0, radius - insetAmount);
}
return radius;
}
// Inspired by https://reach.tech/rect/
function Rect({
children,
...rest
}) {
const [rect, setRect] = useState(null);
const ref = useRef(null);
useLayoutEffect(() => {
if (!isSyncLayoutAccessAvailable()) {
throw new Error("This library requires React Native's new architecture.");
}
// TODO: Maybe use `getBoundingClientRect` instead when it's stable https://gist.github.com/lunaleaps/148756563999c83220887757f2e549a3#file-tooltip-uselayouteffect-js-L77
// From my testing, `measureInWindow` is still faster than `unstable_getBoundingClientRect`
ref.current?.measureInWindow((_x, _y, width, height) => {
setRect({
width,
height
});
});
}, []);
return /*#__PURE__*/_jsx(View, {
ref: ref,
...rest,
children: rect ? children(rect) : null
});
}
function isSyncLayoutAccessAvailable() {
if (Platform.OS === 'web') {
return true;
}
return globalThis.RN$Bridgeless === true;
}
export { SquircleView, getSvgPath };
//# sourceMappingURL=index.js.map