serpentine-border 3.0.4 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/serpentine-border.cjs +1 -1
- package/dist/serpentine-border.cjs.map +1 -1
- package/dist/serpentine-border.js +146 -113
- package/dist/serpentine-border.js.map +1 -1
- package/index.d.ts +7 -0
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=require("react/jsx-runtime"),M=require("react"),G=["#ffffff","#000000"];function D(t,o,e){if(typeof t=="number")return t;const n=o*e;return t==="borderWidth"?n:t==="halfBorderWidth"?n/2:0}function V(t){return Object.entries(t).map(([o,e])=>{const n=o.replace(/([A-Z])/g,"-$1").toLowerCase(),r=typeof e=="number"&&!Number.isNaN(e)?`${e}px`:String(e);return`${n}: ${r}`}).join("; ")}function Z(t,o,e,n,r,d,c,s,i,h){const l=r*(e-1),$=r/2,a=o.length-1,f=[];for(let u=0;u<e;u++){const p=u*r,m=(e-1-u)*r,B=n-p,L=n-m,k=l-m,W=i-h+$,x=p-i+W,j=h,b=t+j-m-$,A=x+(n-p),S=t+j-n-$,C=t+j-l-$,N=i+s,g=[`M ${b} ${N-l-r/2-c}`,`L ${b} ${N-l-c}`,`A ${k} ${k} 0 0 1 ${C} ${N-m-c}`,`L ${A} ${p+s-c}`,`A ${B} ${B} 0 0 0 ${x} ${n+s-c}`,`L ${x} ${n+s}`];for(let R=0;R<a-1;R++){const v=o[R+1],X=o[R+2],U=v+n-i+s;R%2===0?(g.push(`L ${x} ${v-n+s}`),g.push(`A ${B} ${B} 0 0 0 ${A} ${v-p+s}`),g.push(`L ${S} ${v-p+s}`),g.push(`A ${L} ${L} 0 0 1 ${b} ${U}`),g.push(`L ${b} ${X-n+s}`)):(g.push(`L ${b} ${v-n+s}`),g.push(`A ${L} ${L} 0 0 1 ${S} ${v-m+s}`),g.push(`L ${A} ${v-m+s}`),g.push(`A ${B} ${B} 0 0 0 ${x} ${U}`),g.push(`L ${x} ${X-n+s}`))}const O=o[a];(a-2)%2===0?g.push(`L ${b} ${O}`):g.push(`L ${x} ${O}`),f.push({d:g.join(" "),stroke:d[u%d.length],"stroke-width":r,fill:"none"})}return f}const P="serpentine-border-svg",w={strokeCount:5,strokeWidth:8,radius:50,horizontalOverflow:0,layoutMode:"border"};function z(t={}){const o=t.strokeCount??w.strokeCount,e=t.strokeWidth??w.strokeWidth,n=t.horizontalOverflow??w.horizontalOverflow,r=t.layoutMode??w.layoutMode,d=D(n,o,e),c=o*e;return r==="border"?{boxSizing:"border-box",position:"relative",marginTop:`${c/2}px`,...d>0&&{paddingLeft:`${d}px`,paddingRight:`${d}px`}}:{position:"relative",boxSizing:"border-box"}}function Y(t){const o=t.strokeCount??w.strokeCount,e=t.strokeWidth??w.strokeWidth,n=t.radius??w.radius,r=t.horizontalOverflow??w.horizontalOverflow,d=t.colors??G,c=t.layoutMode??w.layoutMode,s=t.svgClassName??P;let i,h;if(t.wrapperEl!=null){const A=t.wrapperEl;if(!(typeof document<"u"&&typeof A.getBoundingClientRect=="function"))return null;const C=q(A,{layoutMode:c,horizontalOverflow:r,strokeCount:o,strokeWidth:e,excludeClassName:s});if(!C)return null;i=C.width,h=C.sectionBottomYs}else{if(t.width==null||t.sectionBottomYs==null)return null;i=t.width,h=t.sectionBottomYs}const l=D(r,o,e),$=(o-1)*e,a=2*e,f=$/2,u=(o-1)/2*e+f,p=z({strokeCount:o,strokeWidth:e,horizontalOverflow:r,layoutMode:c}),y=c==="border"?{position:"absolute",overflow:"hidden",...l>0?{width:"100%",left:0}:l<0?{width:`calc(100% + ${2*l}px)`,left:-l}:{width:"100%",left:0},top:-(a+u),height:`calc(100% + ${a+u}px)`}:{position:"absolute",overflow:"hidden",width:`calc(100% + ${2*l}px)`,left:-l,top:-(a+u),height:`calc(100% + ${a+u}px)`},m=V(y),B=Z(i,h,o,n,e,d,u,f,$,l),L=h[h.length-1]??0,k=Math.max(1,i+2*l),W=L+a+u,x=-l,j=-e*2-u,b=`${x} ${j} ${k} ${W}`;return{wrapperStyle:p,svgAttributes:{class:s,viewBox:b,style:m},paths:B}}function H(t){const{sectionCount:o,strokeCount:e,strokeWidth:n}=t,r=t.horizontalOverflow??0,d=e*n,c=D(r,e,n),s=d/2,i=d-c,h={top:s,right:0,bottom:s,left:i},l={top:s,right:i,bottom:s,left:0},$=o>0&&(o-1)%2===0;return{even:h,odd:l,last:{top:s,right:$?0:i,bottom:0,left:$?i:0}}}function q(t,o){const{layoutMode:e,horizontalOverflow:n=0,strokeCount:r,strokeWidth:d,excludeClassName:c=P}=o,s=D(n,r,d);if(!t)return null;const i=c?Array.from(t.children).filter(f=>!f.classList.contains(c)):Array.from(t.children);if(i.length===0)return null;const h=t.getBoundingClientRect(),l=h.width,$=e==="border"&&s>0?Math.max(1,l-2*s):Math.max(1,l),a=[0];for(let f=0;f<i.length;f++){const u=i[f].getBoundingClientRect();a.push(u.top-h.top+u.height)}return{width:$,sectionBottomYs:a}}function J(t){const o={};if(!t||typeof t!="string")return o;for(const e of t.split(";")){const n=e.indexOf(":");if(n===-1)continue;const r=e.slice(0,n).trim().replace(/-([a-z])/g,(c,s)=>s.toUpperCase()),d=e.slice(n+1).trim();r&&(o[r]=d)}return o}function Q(t){return Object.fromEntries(Object.entries(t).map(([o,e])=>[o==="stroke-width"?"strokeWidth":o,e]))}function I(t,o,e=.75){if(!t||!o||Math.abs(t.width-o.width)>e||t.sectionBottomYs.length!==o.sectionBottomYs.length)return!1;for(let n=0;n<t.sectionBottomYs.length;n++)if(Math.abs(t.sectionBottomYs[n]-o.sectionBottomYs[n])>e)return!1;return!0}function K({children:t,strokeCount:o,strokeWidth:e,radius:n,horizontalOverflow:r,colors:d,layoutMode:c}){const s=M.useRef(null),i=M.useRef(null),[h,l]=M.useState(null),$=M.useMemo(()=>z({strokeCount:o,strokeWidth:e,horizontalOverflow:r,layoutMode:c}),[o,e,r,c]);return M.useLayoutEffect(()=>{i.current=null;const a=s.current;if(!a)return;const f=()=>{const p=q(a,{layoutMode:c,horizontalOverflow:r,strokeCount:o,strokeWidth:e});if(!p)return;const y=i.current;if(y&&I(y,p))return;i.current={width:p.width,sectionBottomYs:[...p.sectionBottomYs]};const m=Y({width:p.width,sectionBottomYs:p.sectionBottomYs,strokeCount:o,strokeWidth:e,radius:n,horizontalOverflow:r,colors:d,layoutMode:c});l(m)};f();const u=new ResizeObserver(f);return u.observe(a),()=>u.disconnect()},[o,e,n,r,d,c]),E.jsxs("div",{ref:s,className:"serpentine-wrapper",style:$,"data-testid":"serpentine-wrapper",children:[h&&(()=>{const{class:a,style:f,...u}=h.svgAttributes,p=typeof f=="string"?J(f):f;return E.jsx("svg",{"data-testid":"serpentine-svg",className:a,style:p,preserveAspectRatio:"none",...u,children:h.paths.map((y,m)=>E.jsx("path",{...Q(y)},m))})})(),t]})}exports.SerpentineBorder=K;exports.getSectionsPadding=H;exports.getWrapperBoxStyle=z;exports.serpentineBorder=Y;
|
|
2
2
|
//# sourceMappingURL=serpentine-border.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serpentine-border.cjs","sources":["../src/constants.js","../src/serpentineCore.js","../src/SerpentineBorder.jsx"],"sourcesContent":["export const DEFAULT_COLORS = ['#ffffff', '#000000']\n","/**\n * Vanilla JS core for serpentine border SVG generation.\n * Single export: call with measured dimensions and options to get everything needed to render.\n */\n\nimport { DEFAULT_COLORS } from './constants.js'\n\nfunction resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH) {\n if (typeof horizontalOverflow === 'number') return horizontalOverflow\n const totalBorderWidth = N * STROKE_WIDTH\n if (horizontalOverflow === 'borderWidth') return totalBorderWidth\n if (horizontalOverflow === 'halfBorderWidth') return totalBorderWidth / 2\n return 0\n}\n\nfunction styleObjectToCss(obj) {\n return Object.entries(obj)\n .map(([k, v]) => {\n const key = k.replace(/([A-Z])/g, '-$1').toLowerCase()\n const val = typeof v === 'number' && !Number.isNaN(v) ? `${v}px` : String(v)\n return `${key}: ${val}`\n })\n .join('; ')\n}\n\nfunction buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA) {\n const R1 = STROKE_WIDTH * (N - 1)\n const RIGHT_EXTEND = STROKE_WIDTH / 2\n const n = Y.length - 1\n const parts = []\n for (let i = 0; i < N; i++) {\n const o = i * STROKE_WIDTH\n const j = N - 1 - i\n const oj = j * STROKE_WIDTH\n const r = R - o\n const rj = R - oj\n const r1 = R1 - o\n const rj1 = R1 - oj\n\n const leftOffset = O_TOTAL - BORDER_EXTRA + RIGHT_EXTEND\n const xLeft = o - O_TOTAL + leftOffset\n const rightExt = BORDER_EXTRA\n const xRight = W + rightExt - oj - RIGHT_EXTEND\n const xLeftArc = xLeft + (R - o)\n const xRightArc = W + rightExt - R - RIGHT_EXTEND\n const xRightR1 = W + rightExt - R1 - RIGHT_EXTEND\n\n const yCurrTop = O_TOTAL + Y_OFFSET\n const segs = [\n `M ${xRight} ${yCurrTop - R1 - STROKE_WIDTH / 2 - TOP_ARC_SHIFT}`,\n `L ${xRight} ${yCurrTop - R1 - TOP_ARC_SHIFT}`,\n `A ${rj1} ${rj1} 0 0 1 ${xRightR1} ${yCurrTop - oj - TOP_ARC_SHIFT}`,\n `L ${xLeftArc} ${o + Y_OFFSET - TOP_ARC_SHIFT}`,\n `A ${r} ${r} 0 0 0 ${xLeft} ${R + Y_OFFSET - TOP_ARC_SHIFT}`,\n `L ${xLeft} ${R + Y_OFFSET}`,\n ]\n\n for (let t = 0; t < n - 1; t++) {\n const yCurr = Y[t + 1]\n const yNext = Y[t + 2]\n const yExit = yCurr + R - O_TOTAL + Y_OFFSET\n\n if (t % 2 === 0) {\n segs.push(`L ${xLeft} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeftArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`L ${xRightArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRight} ${yExit}`)\n segs.push(`L ${xRight} ${yNext - R + Y_OFFSET}`)\n } else {\n segs.push(`L ${xRight} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRightArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`L ${xLeftArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeft} ${yExit}`)\n segs.push(`L ${xLeft} ${yNext - R + Y_OFFSET}`)\n }\n }\n\n const lastY = Y[n]\n if ((n - 2) % 2 === 0) {\n segs.push(`L ${xRight} ${lastY}`)\n } else {\n segs.push(`L ${xLeft} ${lastY}`)\n }\n parts.push({\n d: segs.join(' '),\n stroke: COLORS[i % COLORS.length],\n 'stroke-width': STROKE_WIDTH,\n fill: 'none',\n })\n }\n return parts\n}\n\nconst DEFAULT_SVG_CLASS = 'serpentine-border-svg'\n\nconst DEFAULTS = {\n strokeCount: 5,\n strokeWidth: 8,\n radius: 50,\n horizontalOverflow: 0,\n layoutMode: 'border',\n}\n\n/**\n * Compute everything needed to render the serpentine border.\n * Accepts either (width + sectionBottomYs) for pure/custom use, or wrapperEl to measure from the DOM.\n * When using wrapperEl, returns null in non-DOM environments (e.g. SSR) or when measurement fails.\n *\n * @param {{\n * width?: number\n * sectionBottomYs?: number[]\n * wrapperEl?: HTMLElement\n * strokeCount?: number\n * strokeWidth?: number\n * radius?: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * colors?: string[]\n * layoutMode?: 'content' | 'border'\n * svgClassName?: string\n * }} options\n * @returns {{\n * wrapperStyle: Record<string, unknown>\n * svgAttributes: { class?: string, viewBox: string, style: string }\n * paths: Array<{ d: string, stroke: string, 'stroke-width': number, fill: string }>\n * } | null}\n */\nexport function serpentineBorder(options) {\n const N = options.strokeCount ?? DEFAULTS.strokeCount\n const STROKE_WIDTH = options.strokeWidth ?? DEFAULTS.strokeWidth\n const R = options.radius ?? DEFAULTS.radius\n const horizontalOverflow = options.horizontalOverflow ?? DEFAULTS.horizontalOverflow\n const COLORS = options.colors ?? DEFAULT_COLORS\n const layoutMode = options.layoutMode ?? DEFAULTS.layoutMode\n const svgClassName = options.svgClassName ?? DEFAULT_SVG_CLASS\n\n let W, Y\n if (options.wrapperEl != null) {\n const wrapperEl = options.wrapperEl\n const hasDOM = typeof document !== 'undefined' && typeof wrapperEl.getBoundingClientRect === 'function'\n if (!hasDOM) return null\n const measured = measureSections(wrapperEl, {\n layoutMode,\n horizontalOverflow,\n strokeCount: N,\n strokeWidth: STROKE_WIDTH,\n excludeClassName: svgClassName,\n })\n if (!measured) return null\n W = measured.width\n Y = measured.sectionBottomYs\n } else {\n if (options.width == null || options.sectionBottomYs == null) return null\n W = options.width\n Y = options.sectionBottomYs\n }\n\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const O_TOTAL = (N - 1) * STROKE_WIDTH\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n const TOP_OFFSET = 2 * STROKE_WIDTH\n const Y_OFFSET = O_TOTAL / 2\n const TOP_ARC_SHIFT = ((N - 1) / 2) * STROKE_WIDTH + Y_OFFSET\n\n const wrapperStyle =\n layoutMode === 'border'\n ? {\n boxSizing: 'border-box',\n position: 'relative',\n marginTop: `${TOTAL_BORDER_WIDTH / 2}px`,\n ...(BORDER_EXTRA > 0 && {\n paddingLeft: `${BORDER_EXTRA}px`,\n paddingRight: `${BORDER_EXTRA}px`,\n }),\n }\n : {\n position: 'relative',\n boxSizing: 'border-box',\n }\n\n const svgStyleObj =\n layoutMode === 'border'\n ? {\n position: 'absolute',\n overflow: 'hidden',\n ...(BORDER_EXTRA > 0\n ? { width: '100%', left: 0 }\n : BORDER_EXTRA < 0\n ? { width: `calc(100% + ${2 * BORDER_EXTRA}px)`, left: -BORDER_EXTRA }\n : { width: '100%', left: 0 }),\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n : {\n position: 'absolute',\n overflow: 'hidden',\n width: `calc(100% + ${2 * BORDER_EXTRA}px)`,\n left: -BORDER_EXTRA,\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n const svgStyle = styleObjectToCss(svgStyleObj)\n\n const paths = buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA)\n\n const totalHeight = Y[Y.length - 1] ?? 0\n const totalWidth = Math.max(1, W + 2 * BORDER_EXTRA)\n const viewBoxHeight = totalHeight + TOP_OFFSET + TOP_ARC_SHIFT\n const viewBoxMinX = -BORDER_EXTRA\n const viewBoxMinY = -STROKE_WIDTH * 2 - TOP_ARC_SHIFT\n const viewBoxStr = `${viewBoxMinX} ${viewBoxMinY} ${totalWidth} ${viewBoxHeight}`\n\n return {\n wrapperStyle,\n svgAttributes: {\n class: svgClassName,\n viewBox: viewBoxStr,\n style: svgStyle,\n },\n paths,\n }\n}\n\n/**\n * Compute padding for each section so content does not overlap the border.\n * Returns an object with even, odd, and last: use for even-indexed sections, odd-indexed sections, and the last section respectively.\n *\n * @param {{\n * sectionCount: number\n * strokeCount: number\n * strokeWidth: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * }} options\n * @returns {{ even: { top: number, right: number, bottom: number, left: number }, odd: { top: number, right: number, bottom: number, left: number }, last: { top: number, right: number, bottom: number, left: number } }}\n */\nexport function getSectionsPadding(options) {\n const { sectionCount: n, strokeCount: N, strokeWidth: STROKE_WIDTH } = options\n const horizontalOverflow = options.horizontalOverflow ?? 0\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const halfBorderWidth = TOTAL_BORDER_WIDTH / 2\n const insetSide = TOTAL_BORDER_WIDTH - BORDER_EXTRA\n const even = { top: halfBorderWidth, right: 0, bottom: halfBorderWidth, left: insetSide }\n const odd = { top: halfBorderWidth, right: insetSide, bottom: halfBorderWidth, left: 0 }\n const lastIsEven = n > 0 && (n - 1) % 2 === 0\n const last = {\n top: halfBorderWidth,\n right: lastIsEven ? 0 : insetSide,\n bottom: 0,\n left: lastIsEven ? insetSide : 0,\n }\n return { even, odd, last }\n}\n\n/**\n * Measure wrapper and section elements to get width and section bottom Ys.\n * Children with the excludeClassName (default: same class used on the SVG by serpentineBorder) are excluded.\n * horizontalOverflow is resolved to pixels using strokeCount and strokeWidth.\n *\n * @param {HTMLElement} wrapperEl\n * @param {{\n * layoutMode: 'content' | 'border'\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * strokeCount: number\n * strokeWidth: number\n * excludeClassName?: string\n * }} options\n * @returns {{ width: number, sectionBottomYs: number[] } | null}\n */\nexport function measureSections(wrapperEl, options) {\n const { layoutMode, horizontalOverflow = 0, strokeCount, strokeWidth, excludeClassName = DEFAULT_SVG_CLASS } = options\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, strokeCount, strokeWidth)\n if (!wrapperEl) return null\n\n const sectionEls = excludeClassName\n ? Array.from(wrapperEl.children).filter((el) => !el.classList.contains(excludeClassName))\n : Array.from(wrapperEl.children)\n\n if (sectionEls.length === 0) return null\n\n const rect = wrapperEl.getBoundingClientRect()\n const baseWidth = rect.width\n\n const W =\n layoutMode === 'border' && BORDER_EXTRA > 0\n ? Math.max(1, baseWidth - 2 * BORDER_EXTRA)\n : Math.max(1, baseWidth)\n\n const Y = [0]\n for (let i = 0; i < sectionEls.length; i++) {\n const r = sectionEls[i].getBoundingClientRect()\n Y.push(r.top - rect.top + r.height)\n }\n\n return { width: W, sectionBottomYs: Y }\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { measureSections, serpentineBorder } from './serpentineCore.js'\n\nfunction cssStringToStyleObject(css) {\n const obj = {}\n if (!css || typeof css !== 'string') return obj\n for (const decl of css.split(';')) {\n const colon = decl.indexOf(':')\n if (colon === -1) continue\n const key = decl.slice(0, colon).trim().replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n const value = decl.slice(colon + 1).trim()\n if (key) obj[key] = value\n }\n return obj\n}\n\nfunction pathAttrsForReact(attrs) {\n return Object.fromEntries(\n Object.entries(attrs).map(([k, v]) => [k === 'stroke-width' ? 'strokeWidth' : k, v])\n )\n}\n\nfunction SerpentineBorder({\n children,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n}) {\n const wrapperRef = useRef(null)\n const [borderData, setBorderData] = useState(null)\n\n useEffect(() => {\n const wrapper = wrapperRef.current\n if (!wrapper) return\n\n const measure = () => {\n const measured = measureSections(wrapper, {\n layoutMode,\n horizontalOverflow,\n strokeCount,\n strokeWidth,\n })\n if (!measured) return\n\n const data = serpentineBorder({\n width: measured.width,\n sectionBottomYs: measured.sectionBottomYs,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n })\n setBorderData(data)\n }\n\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(wrapper)\n return () => ro.disconnect()\n }, [strokeCount, strokeWidth, radius, horizontalOverflow, colors, layoutMode])\n\n return (\n <div\n ref={wrapperRef}\n className=\"serpentine-wrapper\"\n style={borderData?.wrapperStyle ?? { position: 'relative', boxSizing: 'border-box' }}\n data-testid=\"serpentine-wrapper\"\n >\n {borderData && (() => {\n const { class: className, style: styleStr, ...restSvgAttrs } = borderData.svgAttributes\n const style = typeof styleStr === 'string' ? cssStringToStyleObject(styleStr) : styleStr\n return (\n <svg\n data-testid=\"serpentine-svg\"\n className={className}\n style={style}\n {...restSvgAttrs}\n >\n {borderData.paths.map((pathAttributes, i) => (\n <path key={i} {...pathAttrsForReact(pathAttributes)} />\n ))}\n </svg>\n )\n })()}\n {children}\n </div>\n )\n}\n\nexport default SerpentineBorder\n"],"names":["DEFAULT_COLORS","resolveOverflowToPixels","horizontalOverflow","N","STROKE_WIDTH","totalBorderWidth","styleObjectToCss","obj","k","v","key","val","buildPathD","W","Y","R","COLORS","TOP_ARC_SHIFT","Y_OFFSET","O_TOTAL","BORDER_EXTRA","R1","RIGHT_EXTEND","n","parts","i","o","oj","r","rj","rj1","leftOffset","xLeft","rightExt","xRight","xLeftArc","xRightArc","xRightR1","yCurrTop","segs","t","yCurr","yNext","yExit","lastY","DEFAULT_SVG_CLASS","DEFAULTS","serpentineBorder","options","layoutMode","svgClassName","wrapperEl","measured","measureSections","TOTAL_BORDER_WIDTH","TOP_OFFSET","wrapperStyle","svgStyleObj","svgStyle","paths","totalHeight","totalWidth","viewBoxHeight","viewBoxMinX","viewBoxMinY","viewBoxStr","getSectionsPadding","halfBorderWidth","insetSide","even","odd","lastIsEven","strokeCount","strokeWidth","excludeClassName","sectionEls","el","rect","baseWidth","cssStringToStyleObject","css","decl","colon","_","c","value","pathAttrsForReact","attrs","SerpentineBorder","children","radius","colors","wrapperRef","useRef","borderData","setBorderData","useState","useEffect","wrapper","measure","data","ro","jsxs","className","styleStr","restSvgAttrs","style","jsx","pathAttributes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"serpentine-border.cjs","sources":["../src/constants.js","../src/serpentineCore.js","../src/SerpentineBorder.jsx"],"sourcesContent":["export const DEFAULT_COLORS = ['#ffffff', '#000000']\n","/**\n * Vanilla JS core for serpentine border SVG generation.\n * Single export: call with measured dimensions and options to get everything needed to render.\n */\n\nimport { DEFAULT_COLORS } from './constants.js'\n\nfunction resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH) {\n if (typeof horizontalOverflow === 'number') return horizontalOverflow\n const totalBorderWidth = N * STROKE_WIDTH\n if (horizontalOverflow === 'borderWidth') return totalBorderWidth\n if (horizontalOverflow === 'halfBorderWidth') return totalBorderWidth / 2\n return 0\n}\n\nfunction styleObjectToCss(obj) {\n return Object.entries(obj)\n .map(([k, v]) => {\n const key = k.replace(/([A-Z])/g, '-$1').toLowerCase()\n const val = typeof v === 'number' && !Number.isNaN(v) ? `${v}px` : String(v)\n return `${key}: ${val}`\n })\n .join('; ')\n}\n\nfunction buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA) {\n const R1 = STROKE_WIDTH * (N - 1)\n const RIGHT_EXTEND = STROKE_WIDTH / 2\n const n = Y.length - 1\n const parts = []\n for (let i = 0; i < N; i++) {\n const o = i * STROKE_WIDTH\n const j = N - 1 - i\n const oj = j * STROKE_WIDTH\n const r = R - o\n const rj = R - oj\n const r1 = R1 - o\n const rj1 = R1 - oj\n\n const leftOffset = O_TOTAL - BORDER_EXTRA + RIGHT_EXTEND\n const xLeft = o - O_TOTAL + leftOffset\n const rightExt = BORDER_EXTRA\n const xRight = W + rightExt - oj - RIGHT_EXTEND\n const xLeftArc = xLeft + (R - o)\n const xRightArc = W + rightExt - R - RIGHT_EXTEND\n const xRightR1 = W + rightExt - R1 - RIGHT_EXTEND\n\n const yCurrTop = O_TOTAL + Y_OFFSET\n const segs = [\n `M ${xRight} ${yCurrTop - R1 - STROKE_WIDTH / 2 - TOP_ARC_SHIFT}`,\n `L ${xRight} ${yCurrTop - R1 - TOP_ARC_SHIFT}`,\n `A ${rj1} ${rj1} 0 0 1 ${xRightR1} ${yCurrTop - oj - TOP_ARC_SHIFT}`,\n `L ${xLeftArc} ${o + Y_OFFSET - TOP_ARC_SHIFT}`,\n `A ${r} ${r} 0 0 0 ${xLeft} ${R + Y_OFFSET - TOP_ARC_SHIFT}`,\n `L ${xLeft} ${R + Y_OFFSET}`,\n ]\n\n for (let t = 0; t < n - 1; t++) {\n const yCurr = Y[t + 1]\n const yNext = Y[t + 2]\n const yExit = yCurr + R - O_TOTAL + Y_OFFSET\n\n if (t % 2 === 0) {\n segs.push(`L ${xLeft} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeftArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`L ${xRightArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRight} ${yExit}`)\n segs.push(`L ${xRight} ${yNext - R + Y_OFFSET}`)\n } else {\n segs.push(`L ${xRight} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRightArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`L ${xLeftArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeft} ${yExit}`)\n segs.push(`L ${xLeft} ${yNext - R + Y_OFFSET}`)\n }\n }\n\n const lastY = Y[n]\n if ((n - 2) % 2 === 0) {\n segs.push(`L ${xRight} ${lastY}`)\n } else {\n segs.push(`L ${xLeft} ${lastY}`)\n }\n parts.push({\n d: segs.join(' '),\n stroke: COLORS[i % COLORS.length],\n 'stroke-width': STROKE_WIDTH,\n fill: 'none',\n })\n }\n return parts\n}\n\nconst DEFAULT_SVG_CLASS = 'serpentine-border-svg'\n\nconst DEFAULTS = {\n strokeCount: 5,\n strokeWidth: 8,\n radius: 50,\n horizontalOverflow: 0,\n layoutMode: 'border',\n}\n\n/**\n * Wrapper margin/padding that do not depend on measured width or section heights.\n * Use on first paint so the column does not jump before the SVG paths exist.\n */\nexport function getWrapperBoxStyle(options = {}) {\n const N = options.strokeCount ?? DEFAULTS.strokeCount\n const STROKE_WIDTH = options.strokeWidth ?? DEFAULTS.strokeWidth\n const horizontalOverflow = options.horizontalOverflow ?? DEFAULTS.horizontalOverflow\n const layoutMode = options.layoutMode ?? DEFAULTS.layoutMode\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n\n if (layoutMode === 'border') {\n return {\n boxSizing: 'border-box',\n position: 'relative',\n marginTop: `${TOTAL_BORDER_WIDTH / 2}px`,\n ...(BORDER_EXTRA > 0 && {\n paddingLeft: `${BORDER_EXTRA}px`,\n paddingRight: `${BORDER_EXTRA}px`,\n }),\n }\n }\n return {\n position: 'relative',\n boxSizing: 'border-box',\n }\n}\n\n/**\n * Compute everything needed to render the serpentine border.\n * Accepts either (width + sectionBottomYs) for pure/custom use, or wrapperEl to measure from the DOM.\n * When using wrapperEl, returns null in non-DOM environments (e.g. SSR) or when measurement fails.\n *\n * @param {{\n * width?: number\n * sectionBottomYs?: number[]\n * wrapperEl?: HTMLElement\n * strokeCount?: number\n * strokeWidth?: number\n * radius?: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * colors?: string[]\n * layoutMode?: 'content' | 'border'\n * svgClassName?: string\n * }} options\n * @returns {{\n * wrapperStyle: Record<string, unknown>\n * svgAttributes: { class?: string, viewBox: string, style: string }\n * paths: Array<{ d: string, stroke: string, 'stroke-width': number, fill: string }>\n * } | null}\n */\nexport function serpentineBorder(options) {\n const N = options.strokeCount ?? DEFAULTS.strokeCount\n const STROKE_WIDTH = options.strokeWidth ?? DEFAULTS.strokeWidth\n const R = options.radius ?? DEFAULTS.radius\n const horizontalOverflow = options.horizontalOverflow ?? DEFAULTS.horizontalOverflow\n const COLORS = options.colors ?? DEFAULT_COLORS\n const layoutMode = options.layoutMode ?? DEFAULTS.layoutMode\n const svgClassName = options.svgClassName ?? DEFAULT_SVG_CLASS\n\n let W, Y\n if (options.wrapperEl != null) {\n const wrapperEl = options.wrapperEl\n const hasDOM = typeof document !== 'undefined' && typeof wrapperEl.getBoundingClientRect === 'function'\n if (!hasDOM) return null\n const measured = measureSections(wrapperEl, {\n layoutMode,\n horizontalOverflow,\n strokeCount: N,\n strokeWidth: STROKE_WIDTH,\n excludeClassName: svgClassName,\n })\n if (!measured) return null\n W = measured.width\n Y = measured.sectionBottomYs\n } else {\n if (options.width == null || options.sectionBottomYs == null) return null\n W = options.width\n Y = options.sectionBottomYs\n }\n\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const O_TOTAL = (N - 1) * STROKE_WIDTH\n const TOP_OFFSET = 2 * STROKE_WIDTH\n const Y_OFFSET = O_TOTAL / 2\n const TOP_ARC_SHIFT = ((N - 1) / 2) * STROKE_WIDTH + Y_OFFSET\n\n const wrapperStyle = getWrapperBoxStyle({\n strokeCount: N,\n strokeWidth: STROKE_WIDTH,\n horizontalOverflow,\n layoutMode,\n })\n\n const svgStyleObj =\n layoutMode === 'border'\n ? {\n position: 'absolute',\n overflow: 'hidden',\n ...(BORDER_EXTRA > 0\n ? { width: '100%', left: 0 }\n : BORDER_EXTRA < 0\n ? { width: `calc(100% + ${2 * BORDER_EXTRA}px)`, left: -BORDER_EXTRA }\n : { width: '100%', left: 0 }),\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n : {\n position: 'absolute',\n overflow: 'hidden',\n width: `calc(100% + ${2 * BORDER_EXTRA}px)`,\n left: -BORDER_EXTRA,\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n const svgStyle = styleObjectToCss(svgStyleObj)\n\n const paths = buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA)\n\n const totalHeight = Y[Y.length - 1] ?? 0\n const totalWidth = Math.max(1, W + 2 * BORDER_EXTRA)\n const viewBoxHeight = totalHeight + TOP_OFFSET + TOP_ARC_SHIFT\n const viewBoxMinX = -BORDER_EXTRA\n const viewBoxMinY = -STROKE_WIDTH * 2 - TOP_ARC_SHIFT\n const viewBoxStr = `${viewBoxMinX} ${viewBoxMinY} ${totalWidth} ${viewBoxHeight}`\n\n return {\n wrapperStyle,\n svgAttributes: {\n class: svgClassName,\n viewBox: viewBoxStr,\n style: svgStyle,\n },\n paths,\n }\n}\n\n/**\n * Compute padding for each section so content does not overlap the border.\n * Returns an object with even, odd, and last: use for even-indexed sections, odd-indexed sections, and the last section respectively.\n *\n * @param {{\n * sectionCount: number\n * strokeCount: number\n * strokeWidth: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * }} options\n * @returns {{ even: { top: number, right: number, bottom: number, left: number }, odd: { top: number, right: number, bottom: number, left: number }, last: { top: number, right: number, bottom: number, left: number } }}\n */\nexport function getSectionsPadding(options) {\n const { sectionCount: n, strokeCount: N, strokeWidth: STROKE_WIDTH } = options\n const horizontalOverflow = options.horizontalOverflow ?? 0\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const halfBorderWidth = TOTAL_BORDER_WIDTH / 2\n const insetSide = TOTAL_BORDER_WIDTH - BORDER_EXTRA\n const even = { top: halfBorderWidth, right: 0, bottom: halfBorderWidth, left: insetSide }\n const odd = { top: halfBorderWidth, right: insetSide, bottom: halfBorderWidth, left: 0 }\n const lastIsEven = n > 0 && (n - 1) % 2 === 0\n const last = {\n top: halfBorderWidth,\n right: lastIsEven ? 0 : insetSide,\n bottom: 0,\n left: lastIsEven ? insetSide : 0,\n }\n return { even, odd, last }\n}\n\n/**\n * Measure wrapper and section elements to get width and section bottom Ys.\n * Children with the excludeClassName (default: same class used on the SVG by serpentineBorder) are excluded.\n * horizontalOverflow is resolved to pixels using strokeCount and strokeWidth.\n *\n * @param {HTMLElement} wrapperEl\n * @param {{\n * layoutMode: 'content' | 'border'\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * strokeCount: number\n * strokeWidth: number\n * excludeClassName?: string\n * }} options\n * @returns {{ width: number, sectionBottomYs: number[] } | null}\n */\nexport function measureSections(wrapperEl, options) {\n const { layoutMode, horizontalOverflow = 0, strokeCount, strokeWidth, excludeClassName = DEFAULT_SVG_CLASS } = options\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, strokeCount, strokeWidth)\n if (!wrapperEl) return null\n\n const sectionEls = excludeClassName\n ? Array.from(wrapperEl.children).filter((el) => !el.classList.contains(excludeClassName))\n : Array.from(wrapperEl.children)\n\n if (sectionEls.length === 0) return null\n\n const rect = wrapperEl.getBoundingClientRect()\n const baseWidth = rect.width\n\n const W =\n layoutMode === 'border' && BORDER_EXTRA > 0\n ? Math.max(1, baseWidth - 2 * BORDER_EXTRA)\n : Math.max(1, baseWidth)\n\n const Y = [0]\n for (let i = 0; i < sectionEls.length; i++) {\n const r = sectionEls[i].getBoundingClientRect()\n Y.push(r.top - rect.top + r.height)\n }\n\n return { width: W, sectionBottomYs: Y }\n}\n","import { useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { getWrapperBoxStyle, measureSections, serpentineBorder } from './serpentineCore.js'\n\nfunction cssStringToStyleObject(css) {\n const obj = {}\n if (!css || typeof css !== 'string') return obj\n for (const decl of css.split(';')) {\n const colon = decl.indexOf(':')\n if (colon === -1) continue\n const key = decl.slice(0, colon).trim().replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n const value = decl.slice(colon + 1).trim()\n if (key) obj[key] = value\n }\n return obj\n}\n\nfunction pathAttrsForReact(attrs) {\n return Object.fromEntries(\n Object.entries(attrs).map(([k, v]) => [k === 'stroke-width' ? 'strokeWidth' : k, v])\n )\n}\n\n/** Ignore subpixel / float noise from getBoundingClientRect so ResizeObserver does not thrash setState. */\nfunction measurementCloseEnough(a, b, eps = 0.75) {\n if (!a || !b) return false\n if (Math.abs(a.width - b.width) > eps) return false\n if (a.sectionBottomYs.length !== b.sectionBottomYs.length) return false\n for (let i = 0; i < a.sectionBottomYs.length; i++) {\n if (Math.abs(a.sectionBottomYs[i] - b.sectionBottomYs[i]) > eps) return false\n }\n return true\n}\n\nfunction SerpentineBorder({\n children,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n}) {\n const wrapperRef = useRef(null)\n const lastMeasuredRef = useRef(null)\n const [borderData, setBorderData] = useState(null)\n\n const wrapperStyle = useMemo(\n () =>\n getWrapperBoxStyle({\n strokeCount,\n strokeWidth,\n horizontalOverflow,\n layoutMode,\n }),\n [strokeCount, strokeWidth, horizontalOverflow, layoutMode]\n )\n\n useLayoutEffect(() => {\n lastMeasuredRef.current = null\n const wrapper = wrapperRef.current\n if (!wrapper) return\n\n const measure = () => {\n const measured = measureSections(wrapper, {\n layoutMode,\n horizontalOverflow,\n strokeCount,\n strokeWidth,\n })\n if (!measured) return\n\n const prev = lastMeasuredRef.current\n if (prev && measurementCloseEnough(prev, measured)) {\n return\n }\n lastMeasuredRef.current = {\n width: measured.width,\n sectionBottomYs: [...measured.sectionBottomYs],\n }\n\n const data = serpentineBorder({\n width: measured.width,\n sectionBottomYs: measured.sectionBottomYs,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n })\n setBorderData(data)\n }\n\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(wrapper)\n return () => ro.disconnect()\n }, [strokeCount, strokeWidth, radius, horizontalOverflow, colors, layoutMode])\n\n return (\n <div\n ref={wrapperRef}\n className=\"serpentine-wrapper\"\n style={wrapperStyle}\n data-testid=\"serpentine-wrapper\"\n >\n {borderData && (() => {\n const { class: className, style: styleStr, ...restSvgAttrs } = borderData.svgAttributes\n const style = typeof styleStr === 'string' ? cssStringToStyleObject(styleStr) : styleStr\n return (\n <svg\n data-testid=\"serpentine-svg\"\n className={className}\n style={style}\n preserveAspectRatio=\"none\"\n {...restSvgAttrs}\n >\n {borderData.paths.map((pathAttributes, i) => (\n <path key={i} {...pathAttrsForReact(pathAttributes)} />\n ))}\n </svg>\n )\n })()}\n {children}\n </div>\n )\n}\n\nexport default SerpentineBorder\n"],"names":["DEFAULT_COLORS","resolveOverflowToPixels","horizontalOverflow","N","STROKE_WIDTH","totalBorderWidth","styleObjectToCss","obj","k","v","key","val","buildPathD","W","Y","R","COLORS","TOP_ARC_SHIFT","Y_OFFSET","O_TOTAL","BORDER_EXTRA","R1","RIGHT_EXTEND","n","parts","i","o","oj","r","rj","rj1","leftOffset","xLeft","rightExt","xRight","xLeftArc","xRightArc","xRightR1","yCurrTop","segs","t","yCurr","yNext","yExit","lastY","DEFAULT_SVG_CLASS","DEFAULTS","getWrapperBoxStyle","options","layoutMode","TOTAL_BORDER_WIDTH","serpentineBorder","svgClassName","wrapperEl","measured","measureSections","TOP_OFFSET","wrapperStyle","svgStyleObj","svgStyle","paths","totalHeight","totalWidth","viewBoxHeight","viewBoxMinX","viewBoxMinY","viewBoxStr","getSectionsPadding","halfBorderWidth","insetSide","even","odd","lastIsEven","strokeCount","strokeWidth","excludeClassName","sectionEls","el","rect","baseWidth","cssStringToStyleObject","css","decl","colon","_","c","value","pathAttrsForReact","attrs","measurementCloseEnough","a","b","eps","SerpentineBorder","children","radius","colors","wrapperRef","useRef","lastMeasuredRef","borderData","setBorderData","useState","useMemo","useLayoutEffect","wrapper","measure","prev","data","ro","jsxs","className","styleStr","restSvgAttrs","style","jsx","pathAttributes"],"mappings":"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"}
|
|
@@ -1,194 +1,226 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useRef as
|
|
3
|
-
const
|
|
4
|
-
function
|
|
1
|
+
import { jsxs as P, jsx as O } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as X, useState as V, useMemo as Z, useLayoutEffect as q } from "react";
|
|
3
|
+
const H = ["#ffffff", "#000000"];
|
|
4
|
+
function k(t, o, e) {
|
|
5
5
|
if (typeof t == "number") return t;
|
|
6
6
|
const n = o * e;
|
|
7
7
|
return t === "borderWidth" ? n : t === "halfBorderWidth" ? n / 2 : 0;
|
|
8
8
|
}
|
|
9
9
|
function J(t) {
|
|
10
10
|
return Object.entries(t).map(([o, e]) => {
|
|
11
|
-
const n = o.replace(/([A-Z])/g, "-$1").toLowerCase(),
|
|
12
|
-
return `${n}: ${
|
|
11
|
+
const n = o.replace(/([A-Z])/g, "-$1").toLowerCase(), r = typeof e == "number" && !Number.isNaN(e) ? `${e}px` : String(e);
|
|
12
|
+
return `${n}: ${r}`;
|
|
13
13
|
}).join("; ");
|
|
14
14
|
}
|
|
15
|
-
function Q(t, o, e, n,
|
|
16
|
-
const
|
|
17
|
-
for (let
|
|
18
|
-
const
|
|
19
|
-
`M ${
|
|
20
|
-
`L ${
|
|
21
|
-
`A ${
|
|
22
|
-
`L ${A} ${
|
|
23
|
-
`A ${b} ${b} 0 0 0 ${
|
|
24
|
-
`L ${
|
|
15
|
+
function Q(t, o, e, n, r, f, c, s, i, d) {
|
|
16
|
+
const l = r * (e - 1), $ = r / 2, a = o.length - 1, h = [];
|
|
17
|
+
for (let u = 0; u < e; u++) {
|
|
18
|
+
const p = u * r, m = (e - 1 - u) * r, b = n - p, L = n - m, R = l - m, D = i - d + $, w = p - i + D, C = d, B = t + C - m - $, A = w + (n - p), W = t + C - n - $, j = t + C - l - $, N = i + s, g = [
|
|
19
|
+
`M ${B} ${N - l - r / 2 - c}`,
|
|
20
|
+
`L ${B} ${N - l - c}`,
|
|
21
|
+
`A ${R} ${R} 0 0 1 ${j} ${N - m - c}`,
|
|
22
|
+
`L ${A} ${p + s - c}`,
|
|
23
|
+
`A ${b} ${b} 0 0 0 ${w} ${n + s - c}`,
|
|
24
|
+
`L ${w} ${n + s}`
|
|
25
25
|
];
|
|
26
|
-
for (let
|
|
27
|
-
const
|
|
28
|
-
|
|
26
|
+
for (let M = 0; M < a - 1; M++) {
|
|
27
|
+
const v = o[M + 1], S = o[M + 2], z = v + n - i + s;
|
|
28
|
+
M % 2 === 0 ? (g.push(`L ${w} ${v - n + s}`), g.push(`A ${b} ${b} 0 0 0 ${A} ${v - p + s}`), g.push(`L ${W} ${v - p + s}`), g.push(`A ${L} ${L} 0 0 1 ${B} ${z}`), g.push(`L ${B} ${S - n + s}`)) : (g.push(`L ${B} ${v - n + s}`), g.push(`A ${L} ${L} 0 0 1 ${W} ${v - m + s}`), g.push(`L ${A} ${v - m + s}`), g.push(`A ${b} ${b} 0 0 0 ${w} ${z}`), g.push(`L ${w} ${S - n + s}`));
|
|
29
29
|
}
|
|
30
|
-
const
|
|
31
|
-
(
|
|
30
|
+
const E = o[a];
|
|
31
|
+
(a - 2) % 2 === 0 ? g.push(`L ${B} ${E}`) : g.push(`L ${w} ${E}`), h.push({
|
|
32
32
|
d: g.join(" "),
|
|
33
|
-
stroke:
|
|
34
|
-
"stroke-width":
|
|
33
|
+
stroke: f[u % f.length],
|
|
34
|
+
"stroke-width": r,
|
|
35
35
|
fill: "none"
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
return
|
|
38
|
+
return h;
|
|
39
39
|
}
|
|
40
|
-
const
|
|
40
|
+
const U = "serpentine-border-svg", x = {
|
|
41
41
|
strokeCount: 5,
|
|
42
42
|
strokeWidth: 8,
|
|
43
43
|
radius: 50,
|
|
44
44
|
horizontalOverflow: 0,
|
|
45
45
|
layoutMode: "border"
|
|
46
46
|
};
|
|
47
|
-
function
|
|
48
|
-
const o = t.strokeCount ??
|
|
49
|
-
|
|
47
|
+
function Y(t = {}) {
|
|
48
|
+
const o = t.strokeCount ?? x.strokeCount, e = t.strokeWidth ?? x.strokeWidth, n = t.horizontalOverflow ?? x.horizontalOverflow, r = t.layoutMode ?? x.layoutMode, f = k(n, o, e), c = o * e;
|
|
49
|
+
return r === "border" ? {
|
|
50
|
+
boxSizing: "border-box",
|
|
51
|
+
position: "relative",
|
|
52
|
+
marginTop: `${c / 2}px`,
|
|
53
|
+
...f > 0 && {
|
|
54
|
+
paddingLeft: `${f}px`,
|
|
55
|
+
paddingRight: `${f}px`
|
|
56
|
+
}
|
|
57
|
+
} : {
|
|
58
|
+
position: "relative",
|
|
59
|
+
boxSizing: "border-box"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function I(t) {
|
|
63
|
+
const o = t.strokeCount ?? x.strokeCount, e = t.strokeWidth ?? x.strokeWidth, n = t.radius ?? x.radius, r = t.horizontalOverflow ?? x.horizontalOverflow, f = t.colors ?? H, c = t.layoutMode ?? x.layoutMode, s = t.svgClassName ?? U;
|
|
64
|
+
let i, d;
|
|
50
65
|
if (t.wrapperEl != null) {
|
|
51
|
-
const
|
|
52
|
-
if (!(typeof document < "u" && typeof
|
|
53
|
-
const
|
|
54
|
-
layoutMode:
|
|
55
|
-
horizontalOverflow:
|
|
66
|
+
const A = t.wrapperEl;
|
|
67
|
+
if (!(typeof document < "u" && typeof A.getBoundingClientRect == "function")) return null;
|
|
68
|
+
const j = G(A, {
|
|
69
|
+
layoutMode: c,
|
|
70
|
+
horizontalOverflow: r,
|
|
56
71
|
strokeCount: o,
|
|
57
72
|
strokeWidth: e,
|
|
58
73
|
excludeClassName: s
|
|
59
74
|
});
|
|
60
|
-
if (!
|
|
61
|
-
|
|
75
|
+
if (!j) return null;
|
|
76
|
+
i = j.width, d = j.sectionBottomYs;
|
|
62
77
|
} else {
|
|
63
78
|
if (t.width == null || t.sectionBottomYs == null) return null;
|
|
64
|
-
|
|
79
|
+
i = t.width, d = t.sectionBottomYs;
|
|
65
80
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
paddingRight: `${r}px`
|
|
73
|
-
}
|
|
74
|
-
} : {
|
|
75
|
-
position: "relative",
|
|
76
|
-
boxSizing: "border-box"
|
|
77
|
-
}, x = u === "border" ? {
|
|
81
|
+
const l = k(r, o, e), $ = (o - 1) * e, a = 2 * e, h = $ / 2, u = (o - 1) / 2 * e + h, p = Y({
|
|
82
|
+
strokeCount: o,
|
|
83
|
+
strokeWidth: e,
|
|
84
|
+
horizontalOverflow: r,
|
|
85
|
+
layoutMode: c
|
|
86
|
+
}), y = c === "border" ? {
|
|
78
87
|
position: "absolute",
|
|
79
88
|
overflow: "hidden",
|
|
80
|
-
...
|
|
81
|
-
top: -(
|
|
82
|
-
height: `calc(100% + ${
|
|
89
|
+
...l > 0 ? { width: "100%", left: 0 } : l < 0 ? { width: `calc(100% + ${2 * l}px)`, left: -l } : { width: "100%", left: 0 },
|
|
90
|
+
top: -(a + u),
|
|
91
|
+
height: `calc(100% + ${a + u}px)`
|
|
83
92
|
} : {
|
|
84
93
|
position: "absolute",
|
|
85
94
|
overflow: "hidden",
|
|
86
|
-
width: `calc(100% + ${2 *
|
|
87
|
-
left: -
|
|
88
|
-
top: -(
|
|
89
|
-
height: `calc(100% + ${
|
|
90
|
-
},
|
|
95
|
+
width: `calc(100% + ${2 * l}px)`,
|
|
96
|
+
left: -l,
|
|
97
|
+
top: -(a + u),
|
|
98
|
+
height: `calc(100% + ${a + u}px)`
|
|
99
|
+
}, m = J(y), b = Q(i, d, o, n, e, f, u, h, $, l), L = d[d.length - 1] ?? 0, R = Math.max(1, i + 2 * l), D = L + a + u, w = -l, C = -e * 2 - u, B = `${w} ${C} ${R} ${D}`;
|
|
91
100
|
return {
|
|
92
|
-
wrapperStyle:
|
|
101
|
+
wrapperStyle: p,
|
|
93
102
|
svgAttributes: {
|
|
94
103
|
class: s,
|
|
95
|
-
viewBox:
|
|
96
|
-
style:
|
|
104
|
+
viewBox: B,
|
|
105
|
+
style: m
|
|
97
106
|
},
|
|
98
|
-
paths:
|
|
107
|
+
paths: b
|
|
99
108
|
};
|
|
100
109
|
}
|
|
101
|
-
function
|
|
102
|
-
const { sectionCount: o, strokeCount: e, strokeWidth: n } = t,
|
|
103
|
-
return { even:
|
|
110
|
+
function et(t) {
|
|
111
|
+
const { sectionCount: o, strokeCount: e, strokeWidth: n } = t, r = t.horizontalOverflow ?? 0, f = e * n, c = k(r, e, n), s = f / 2, i = f - c, d = { top: s, right: 0, bottom: s, left: i }, l = { top: s, right: i, bottom: s, left: 0 }, $ = o > 0 && (o - 1) % 2 === 0;
|
|
112
|
+
return { even: d, odd: l, last: {
|
|
104
113
|
top: s,
|
|
105
|
-
right:
|
|
114
|
+
right: $ ? 0 : i,
|
|
106
115
|
bottom: 0,
|
|
107
|
-
left:
|
|
116
|
+
left: $ ? i : 0
|
|
108
117
|
} };
|
|
109
118
|
}
|
|
110
|
-
function
|
|
111
|
-
const { layoutMode: e, horizontalOverflow: n = 0, strokeCount:
|
|
119
|
+
function G(t, o) {
|
|
120
|
+
const { layoutMode: e, horizontalOverflow: n = 0, strokeCount: r, strokeWidth: f, excludeClassName: c = U } = o, s = k(n, r, f);
|
|
112
121
|
if (!t) return null;
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
const
|
|
116
|
-
for (let
|
|
117
|
-
const
|
|
118
|
-
|
|
122
|
+
const i = c ? Array.from(t.children).filter((h) => !h.classList.contains(c)) : Array.from(t.children);
|
|
123
|
+
if (i.length === 0) return null;
|
|
124
|
+
const d = t.getBoundingClientRect(), l = d.width, $ = e === "border" && s > 0 ? Math.max(1, l - 2 * s) : Math.max(1, l), a = [0];
|
|
125
|
+
for (let h = 0; h < i.length; h++) {
|
|
126
|
+
const u = i[h].getBoundingClientRect();
|
|
127
|
+
a.push(u.top - d.top + u.height);
|
|
119
128
|
}
|
|
120
|
-
return { width:
|
|
129
|
+
return { width: $, sectionBottomYs: a };
|
|
121
130
|
}
|
|
122
|
-
function
|
|
131
|
+
function K(t) {
|
|
123
132
|
const o = {};
|
|
124
133
|
if (!t || typeof t != "string") return o;
|
|
125
134
|
for (const e of t.split(";")) {
|
|
126
135
|
const n = e.indexOf(":");
|
|
127
136
|
if (n === -1) continue;
|
|
128
|
-
const
|
|
129
|
-
|
|
137
|
+
const r = e.slice(0, n).trim().replace(/-([a-z])/g, (c, s) => s.toUpperCase()), f = e.slice(n + 1).trim();
|
|
138
|
+
r && (o[r] = f);
|
|
130
139
|
}
|
|
131
140
|
return o;
|
|
132
141
|
}
|
|
133
|
-
function
|
|
142
|
+
function T(t) {
|
|
134
143
|
return Object.fromEntries(
|
|
135
144
|
Object.entries(t).map(([o, e]) => [o === "stroke-width" ? "strokeWidth" : o, e])
|
|
136
145
|
);
|
|
137
146
|
}
|
|
138
|
-
function
|
|
147
|
+
function _(t, o, e = 0.75) {
|
|
148
|
+
if (!t || !o || Math.abs(t.width - o.width) > e || t.sectionBottomYs.length !== o.sectionBottomYs.length) return !1;
|
|
149
|
+
for (let n = 0; n < t.sectionBottomYs.length; n++)
|
|
150
|
+
if (Math.abs(t.sectionBottomYs[n] - o.sectionBottomYs[n]) > e) return !1;
|
|
151
|
+
return !0;
|
|
152
|
+
}
|
|
153
|
+
function ot({
|
|
139
154
|
children: t,
|
|
140
155
|
strokeCount: o,
|
|
141
156
|
strokeWidth: e,
|
|
142
157
|
radius: n,
|
|
143
|
-
horizontalOverflow:
|
|
144
|
-
colors:
|
|
145
|
-
layoutMode:
|
|
158
|
+
horizontalOverflow: r,
|
|
159
|
+
colors: f,
|
|
160
|
+
layoutMode: c
|
|
146
161
|
}) {
|
|
147
|
-
const s =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
162
|
+
const s = X(null), i = X(null), [d, l] = V(null), $ = Z(
|
|
163
|
+
() => Y({
|
|
164
|
+
strokeCount: o,
|
|
165
|
+
strokeWidth: e,
|
|
166
|
+
horizontalOverflow: r,
|
|
167
|
+
layoutMode: c
|
|
168
|
+
}),
|
|
169
|
+
[o, e, r, c]
|
|
170
|
+
);
|
|
171
|
+
return q(() => {
|
|
172
|
+
i.current = null;
|
|
173
|
+
const a = s.current;
|
|
174
|
+
if (!a) return;
|
|
175
|
+
const h = () => {
|
|
176
|
+
const p = G(a, {
|
|
177
|
+
layoutMode: c,
|
|
178
|
+
horizontalOverflow: r,
|
|
155
179
|
strokeCount: o,
|
|
156
180
|
strokeWidth: e
|
|
157
181
|
});
|
|
158
|
-
if (!
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
182
|
+
if (!p) return;
|
|
183
|
+
const y = i.current;
|
|
184
|
+
if (y && _(y, p))
|
|
185
|
+
return;
|
|
186
|
+
i.current = {
|
|
187
|
+
width: p.width,
|
|
188
|
+
sectionBottomYs: [...p.sectionBottomYs]
|
|
189
|
+
};
|
|
190
|
+
const m = I({
|
|
191
|
+
width: p.width,
|
|
192
|
+
sectionBottomYs: p.sectionBottomYs,
|
|
162
193
|
strokeCount: o,
|
|
163
194
|
strokeWidth: e,
|
|
164
195
|
radius: n,
|
|
165
|
-
horizontalOverflow:
|
|
166
|
-
colors:
|
|
167
|
-
layoutMode:
|
|
196
|
+
horizontalOverflow: r,
|
|
197
|
+
colors: f,
|
|
198
|
+
layoutMode: c
|
|
168
199
|
});
|
|
169
|
-
|
|
200
|
+
l(m);
|
|
170
201
|
};
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
return
|
|
174
|
-
}, [o, e, n,
|
|
202
|
+
h();
|
|
203
|
+
const u = new ResizeObserver(h);
|
|
204
|
+
return u.observe(a), () => u.disconnect();
|
|
205
|
+
}, [o, e, n, r, f, c]), /* @__PURE__ */ P(
|
|
175
206
|
"div",
|
|
176
207
|
{
|
|
177
208
|
ref: s,
|
|
178
209
|
className: "serpentine-wrapper",
|
|
179
|
-
style:
|
|
210
|
+
style: $,
|
|
180
211
|
"data-testid": "serpentine-wrapper",
|
|
181
212
|
children: [
|
|
182
|
-
|
|
183
|
-
const { class:
|
|
184
|
-
return /* @__PURE__ */
|
|
213
|
+
d && (() => {
|
|
214
|
+
const { class: a, style: h, ...u } = d.svgAttributes, p = typeof h == "string" ? K(h) : h;
|
|
215
|
+
return /* @__PURE__ */ O(
|
|
185
216
|
"svg",
|
|
186
217
|
{
|
|
187
218
|
"data-testid": "serpentine-svg",
|
|
188
|
-
className:
|
|
189
|
-
style:
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
className: a,
|
|
220
|
+
style: p,
|
|
221
|
+
preserveAspectRatio: "none",
|
|
222
|
+
...u,
|
|
223
|
+
children: d.paths.map((y, m) => /* @__PURE__ */ O("path", { ...T(y) }, m))
|
|
192
224
|
}
|
|
193
225
|
);
|
|
194
226
|
})(),
|
|
@@ -198,8 +230,9 @@ function F({
|
|
|
198
230
|
);
|
|
199
231
|
}
|
|
200
232
|
export {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
233
|
+
ot as SerpentineBorder,
|
|
234
|
+
et as getSectionsPadding,
|
|
235
|
+
Y as getWrapperBoxStyle,
|
|
236
|
+
I as serpentineBorder
|
|
204
237
|
};
|
|
205
238
|
//# sourceMappingURL=serpentine-border.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serpentine-border.js","sources":["../src/constants.js","../src/serpentineCore.js","../src/SerpentineBorder.jsx"],"sourcesContent":["export const DEFAULT_COLORS = ['#ffffff', '#000000']\n","/**\n * Vanilla JS core for serpentine border SVG generation.\n * Single export: call with measured dimensions and options to get everything needed to render.\n */\n\nimport { DEFAULT_COLORS } from './constants.js'\n\nfunction resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH) {\n if (typeof horizontalOverflow === 'number') return horizontalOverflow\n const totalBorderWidth = N * STROKE_WIDTH\n if (horizontalOverflow === 'borderWidth') return totalBorderWidth\n if (horizontalOverflow === 'halfBorderWidth') return totalBorderWidth / 2\n return 0\n}\n\nfunction styleObjectToCss(obj) {\n return Object.entries(obj)\n .map(([k, v]) => {\n const key = k.replace(/([A-Z])/g, '-$1').toLowerCase()\n const val = typeof v === 'number' && !Number.isNaN(v) ? `${v}px` : String(v)\n return `${key}: ${val}`\n })\n .join('; ')\n}\n\nfunction buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA) {\n const R1 = STROKE_WIDTH * (N - 1)\n const RIGHT_EXTEND = STROKE_WIDTH / 2\n const n = Y.length - 1\n const parts = []\n for (let i = 0; i < N; i++) {\n const o = i * STROKE_WIDTH\n const j = N - 1 - i\n const oj = j * STROKE_WIDTH\n const r = R - o\n const rj = R - oj\n const r1 = R1 - o\n const rj1 = R1 - oj\n\n const leftOffset = O_TOTAL - BORDER_EXTRA + RIGHT_EXTEND\n const xLeft = o - O_TOTAL + leftOffset\n const rightExt = BORDER_EXTRA\n const xRight = W + rightExt - oj - RIGHT_EXTEND\n const xLeftArc = xLeft + (R - o)\n const xRightArc = W + rightExt - R - RIGHT_EXTEND\n const xRightR1 = W + rightExt - R1 - RIGHT_EXTEND\n\n const yCurrTop = O_TOTAL + Y_OFFSET\n const segs = [\n `M ${xRight} ${yCurrTop - R1 - STROKE_WIDTH / 2 - TOP_ARC_SHIFT}`,\n `L ${xRight} ${yCurrTop - R1 - TOP_ARC_SHIFT}`,\n `A ${rj1} ${rj1} 0 0 1 ${xRightR1} ${yCurrTop - oj - TOP_ARC_SHIFT}`,\n `L ${xLeftArc} ${o + Y_OFFSET - TOP_ARC_SHIFT}`,\n `A ${r} ${r} 0 0 0 ${xLeft} ${R + Y_OFFSET - TOP_ARC_SHIFT}`,\n `L ${xLeft} ${R + Y_OFFSET}`,\n ]\n\n for (let t = 0; t < n - 1; t++) {\n const yCurr = Y[t + 1]\n const yNext = Y[t + 2]\n const yExit = yCurr + R - O_TOTAL + Y_OFFSET\n\n if (t % 2 === 0) {\n segs.push(`L ${xLeft} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeftArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`L ${xRightArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRight} ${yExit}`)\n segs.push(`L ${xRight} ${yNext - R + Y_OFFSET}`)\n } else {\n segs.push(`L ${xRight} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRightArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`L ${xLeftArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeft} ${yExit}`)\n segs.push(`L ${xLeft} ${yNext - R + Y_OFFSET}`)\n }\n }\n\n const lastY = Y[n]\n if ((n - 2) % 2 === 0) {\n segs.push(`L ${xRight} ${lastY}`)\n } else {\n segs.push(`L ${xLeft} ${lastY}`)\n }\n parts.push({\n d: segs.join(' '),\n stroke: COLORS[i % COLORS.length],\n 'stroke-width': STROKE_WIDTH,\n fill: 'none',\n })\n }\n return parts\n}\n\nconst DEFAULT_SVG_CLASS = 'serpentine-border-svg'\n\nconst DEFAULTS = {\n strokeCount: 5,\n strokeWidth: 8,\n radius: 50,\n horizontalOverflow: 0,\n layoutMode: 'border',\n}\n\n/**\n * Compute everything needed to render the serpentine border.\n * Accepts either (width + sectionBottomYs) for pure/custom use, or wrapperEl to measure from the DOM.\n * When using wrapperEl, returns null in non-DOM environments (e.g. SSR) or when measurement fails.\n *\n * @param {{\n * width?: number\n * sectionBottomYs?: number[]\n * wrapperEl?: HTMLElement\n * strokeCount?: number\n * strokeWidth?: number\n * radius?: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * colors?: string[]\n * layoutMode?: 'content' | 'border'\n * svgClassName?: string\n * }} options\n * @returns {{\n * wrapperStyle: Record<string, unknown>\n * svgAttributes: { class?: string, viewBox: string, style: string }\n * paths: Array<{ d: string, stroke: string, 'stroke-width': number, fill: string }>\n * } | null}\n */\nexport function serpentineBorder(options) {\n const N = options.strokeCount ?? DEFAULTS.strokeCount\n const STROKE_WIDTH = options.strokeWidth ?? DEFAULTS.strokeWidth\n const R = options.radius ?? DEFAULTS.radius\n const horizontalOverflow = options.horizontalOverflow ?? DEFAULTS.horizontalOverflow\n const COLORS = options.colors ?? DEFAULT_COLORS\n const layoutMode = options.layoutMode ?? DEFAULTS.layoutMode\n const svgClassName = options.svgClassName ?? DEFAULT_SVG_CLASS\n\n let W, Y\n if (options.wrapperEl != null) {\n const wrapperEl = options.wrapperEl\n const hasDOM = typeof document !== 'undefined' && typeof wrapperEl.getBoundingClientRect === 'function'\n if (!hasDOM) return null\n const measured = measureSections(wrapperEl, {\n layoutMode,\n horizontalOverflow,\n strokeCount: N,\n strokeWidth: STROKE_WIDTH,\n excludeClassName: svgClassName,\n })\n if (!measured) return null\n W = measured.width\n Y = measured.sectionBottomYs\n } else {\n if (options.width == null || options.sectionBottomYs == null) return null\n W = options.width\n Y = options.sectionBottomYs\n }\n\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const O_TOTAL = (N - 1) * STROKE_WIDTH\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n const TOP_OFFSET = 2 * STROKE_WIDTH\n const Y_OFFSET = O_TOTAL / 2\n const TOP_ARC_SHIFT = ((N - 1) / 2) * STROKE_WIDTH + Y_OFFSET\n\n const wrapperStyle =\n layoutMode === 'border'\n ? {\n boxSizing: 'border-box',\n position: 'relative',\n marginTop: `${TOTAL_BORDER_WIDTH / 2}px`,\n ...(BORDER_EXTRA > 0 && {\n paddingLeft: `${BORDER_EXTRA}px`,\n paddingRight: `${BORDER_EXTRA}px`,\n }),\n }\n : {\n position: 'relative',\n boxSizing: 'border-box',\n }\n\n const svgStyleObj =\n layoutMode === 'border'\n ? {\n position: 'absolute',\n overflow: 'hidden',\n ...(BORDER_EXTRA > 0\n ? { width: '100%', left: 0 }\n : BORDER_EXTRA < 0\n ? { width: `calc(100% + ${2 * BORDER_EXTRA}px)`, left: -BORDER_EXTRA }\n : { width: '100%', left: 0 }),\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n : {\n position: 'absolute',\n overflow: 'hidden',\n width: `calc(100% + ${2 * BORDER_EXTRA}px)`,\n left: -BORDER_EXTRA,\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n const svgStyle = styleObjectToCss(svgStyleObj)\n\n const paths = buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA)\n\n const totalHeight = Y[Y.length - 1] ?? 0\n const totalWidth = Math.max(1, W + 2 * BORDER_EXTRA)\n const viewBoxHeight = totalHeight + TOP_OFFSET + TOP_ARC_SHIFT\n const viewBoxMinX = -BORDER_EXTRA\n const viewBoxMinY = -STROKE_WIDTH * 2 - TOP_ARC_SHIFT\n const viewBoxStr = `${viewBoxMinX} ${viewBoxMinY} ${totalWidth} ${viewBoxHeight}`\n\n return {\n wrapperStyle,\n svgAttributes: {\n class: svgClassName,\n viewBox: viewBoxStr,\n style: svgStyle,\n },\n paths,\n }\n}\n\n/**\n * Compute padding for each section so content does not overlap the border.\n * Returns an object with even, odd, and last: use for even-indexed sections, odd-indexed sections, and the last section respectively.\n *\n * @param {{\n * sectionCount: number\n * strokeCount: number\n * strokeWidth: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * }} options\n * @returns {{ even: { top: number, right: number, bottom: number, left: number }, odd: { top: number, right: number, bottom: number, left: number }, last: { top: number, right: number, bottom: number, left: number } }}\n */\nexport function getSectionsPadding(options) {\n const { sectionCount: n, strokeCount: N, strokeWidth: STROKE_WIDTH } = options\n const horizontalOverflow = options.horizontalOverflow ?? 0\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const halfBorderWidth = TOTAL_BORDER_WIDTH / 2\n const insetSide = TOTAL_BORDER_WIDTH - BORDER_EXTRA\n const even = { top: halfBorderWidth, right: 0, bottom: halfBorderWidth, left: insetSide }\n const odd = { top: halfBorderWidth, right: insetSide, bottom: halfBorderWidth, left: 0 }\n const lastIsEven = n > 0 && (n - 1) % 2 === 0\n const last = {\n top: halfBorderWidth,\n right: lastIsEven ? 0 : insetSide,\n bottom: 0,\n left: lastIsEven ? insetSide : 0,\n }\n return { even, odd, last }\n}\n\n/**\n * Measure wrapper and section elements to get width and section bottom Ys.\n * Children with the excludeClassName (default: same class used on the SVG by serpentineBorder) are excluded.\n * horizontalOverflow is resolved to pixels using strokeCount and strokeWidth.\n *\n * @param {HTMLElement} wrapperEl\n * @param {{\n * layoutMode: 'content' | 'border'\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * strokeCount: number\n * strokeWidth: number\n * excludeClassName?: string\n * }} options\n * @returns {{ width: number, sectionBottomYs: number[] } | null}\n */\nexport function measureSections(wrapperEl, options) {\n const { layoutMode, horizontalOverflow = 0, strokeCount, strokeWidth, excludeClassName = DEFAULT_SVG_CLASS } = options\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, strokeCount, strokeWidth)\n if (!wrapperEl) return null\n\n const sectionEls = excludeClassName\n ? Array.from(wrapperEl.children).filter((el) => !el.classList.contains(excludeClassName))\n : Array.from(wrapperEl.children)\n\n if (sectionEls.length === 0) return null\n\n const rect = wrapperEl.getBoundingClientRect()\n const baseWidth = rect.width\n\n const W =\n layoutMode === 'border' && BORDER_EXTRA > 0\n ? Math.max(1, baseWidth - 2 * BORDER_EXTRA)\n : Math.max(1, baseWidth)\n\n const Y = [0]\n for (let i = 0; i < sectionEls.length; i++) {\n const r = sectionEls[i].getBoundingClientRect()\n Y.push(r.top - rect.top + r.height)\n }\n\n return { width: W, sectionBottomYs: Y }\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { measureSections, serpentineBorder } from './serpentineCore.js'\n\nfunction cssStringToStyleObject(css) {\n const obj = {}\n if (!css || typeof css !== 'string') return obj\n for (const decl of css.split(';')) {\n const colon = decl.indexOf(':')\n if (colon === -1) continue\n const key = decl.slice(0, colon).trim().replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n const value = decl.slice(colon + 1).trim()\n if (key) obj[key] = value\n }\n return obj\n}\n\nfunction pathAttrsForReact(attrs) {\n return Object.fromEntries(\n Object.entries(attrs).map(([k, v]) => [k === 'stroke-width' ? 'strokeWidth' : k, v])\n )\n}\n\nfunction SerpentineBorder({\n children,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n}) {\n const wrapperRef = useRef(null)\n const [borderData, setBorderData] = useState(null)\n\n useEffect(() => {\n const wrapper = wrapperRef.current\n if (!wrapper) return\n\n const measure = () => {\n const measured = measureSections(wrapper, {\n layoutMode,\n horizontalOverflow,\n strokeCount,\n strokeWidth,\n })\n if (!measured) return\n\n const data = serpentineBorder({\n width: measured.width,\n sectionBottomYs: measured.sectionBottomYs,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n })\n setBorderData(data)\n }\n\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(wrapper)\n return () => ro.disconnect()\n }, [strokeCount, strokeWidth, radius, horizontalOverflow, colors, layoutMode])\n\n return (\n <div\n ref={wrapperRef}\n className=\"serpentine-wrapper\"\n style={borderData?.wrapperStyle ?? { position: 'relative', boxSizing: 'border-box' }}\n data-testid=\"serpentine-wrapper\"\n >\n {borderData && (() => {\n const { class: className, style: styleStr, ...restSvgAttrs } = borderData.svgAttributes\n const style = typeof styleStr === 'string' ? cssStringToStyleObject(styleStr) : styleStr\n return (\n <svg\n data-testid=\"serpentine-svg\"\n className={className}\n style={style}\n {...restSvgAttrs}\n >\n {borderData.paths.map((pathAttributes, i) => (\n <path key={i} {...pathAttrsForReact(pathAttributes)} />\n ))}\n </svg>\n )\n })()}\n {children}\n </div>\n )\n}\n\nexport default SerpentineBorder\n"],"names":["DEFAULT_COLORS","resolveOverflowToPixels","horizontalOverflow","N","STROKE_WIDTH","totalBorderWidth","styleObjectToCss","obj","k","v","key","val","buildPathD","W","Y","R","COLORS","TOP_ARC_SHIFT","Y_OFFSET","O_TOTAL","BORDER_EXTRA","R1","RIGHT_EXTEND","n","parts","i","o","oj","r","rj","rj1","leftOffset","xLeft","rightExt","xRight","xLeftArc","xRightArc","xRightR1","yCurrTop","segs","t","yCurr","yNext","yExit","lastY","DEFAULT_SVG_CLASS","DEFAULTS","serpentineBorder","options","layoutMode","svgClassName","wrapperEl","measured","measureSections","TOTAL_BORDER_WIDTH","TOP_OFFSET","wrapperStyle","svgStyleObj","svgStyle","paths","totalHeight","totalWidth","viewBoxHeight","viewBoxMinX","viewBoxMinY","viewBoxStr","getSectionsPadding","halfBorderWidth","insetSide","even","odd","lastIsEven","strokeCount","strokeWidth","excludeClassName","sectionEls","el","rect","baseWidth","cssStringToStyleObject","css","decl","colon","_","c","value","pathAttrsForReact","attrs","SerpentineBorder","children","radius","colors","wrapperRef","useRef","borderData","setBorderData","useState","useEffect","wrapper","measure","data","ro","jsxs","className","styleStr","restSvgAttrs","style","jsx","pathAttributes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"serpentine-border.js","sources":["../src/constants.js","../src/serpentineCore.js","../src/SerpentineBorder.jsx"],"sourcesContent":["export const DEFAULT_COLORS = ['#ffffff', '#000000']\n","/**\n * Vanilla JS core for serpentine border SVG generation.\n * Single export: call with measured dimensions and options to get everything needed to render.\n */\n\nimport { DEFAULT_COLORS } from './constants.js'\n\nfunction resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH) {\n if (typeof horizontalOverflow === 'number') return horizontalOverflow\n const totalBorderWidth = N * STROKE_WIDTH\n if (horizontalOverflow === 'borderWidth') return totalBorderWidth\n if (horizontalOverflow === 'halfBorderWidth') return totalBorderWidth / 2\n return 0\n}\n\nfunction styleObjectToCss(obj) {\n return Object.entries(obj)\n .map(([k, v]) => {\n const key = k.replace(/([A-Z])/g, '-$1').toLowerCase()\n const val = typeof v === 'number' && !Number.isNaN(v) ? `${v}px` : String(v)\n return `${key}: ${val}`\n })\n .join('; ')\n}\n\nfunction buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA) {\n const R1 = STROKE_WIDTH * (N - 1)\n const RIGHT_EXTEND = STROKE_WIDTH / 2\n const n = Y.length - 1\n const parts = []\n for (let i = 0; i < N; i++) {\n const o = i * STROKE_WIDTH\n const j = N - 1 - i\n const oj = j * STROKE_WIDTH\n const r = R - o\n const rj = R - oj\n const r1 = R1 - o\n const rj1 = R1 - oj\n\n const leftOffset = O_TOTAL - BORDER_EXTRA + RIGHT_EXTEND\n const xLeft = o - O_TOTAL + leftOffset\n const rightExt = BORDER_EXTRA\n const xRight = W + rightExt - oj - RIGHT_EXTEND\n const xLeftArc = xLeft + (R - o)\n const xRightArc = W + rightExt - R - RIGHT_EXTEND\n const xRightR1 = W + rightExt - R1 - RIGHT_EXTEND\n\n const yCurrTop = O_TOTAL + Y_OFFSET\n const segs = [\n `M ${xRight} ${yCurrTop - R1 - STROKE_WIDTH / 2 - TOP_ARC_SHIFT}`,\n `L ${xRight} ${yCurrTop - R1 - TOP_ARC_SHIFT}`,\n `A ${rj1} ${rj1} 0 0 1 ${xRightR1} ${yCurrTop - oj - TOP_ARC_SHIFT}`,\n `L ${xLeftArc} ${o + Y_OFFSET - TOP_ARC_SHIFT}`,\n `A ${r} ${r} 0 0 0 ${xLeft} ${R + Y_OFFSET - TOP_ARC_SHIFT}`,\n `L ${xLeft} ${R + Y_OFFSET}`,\n ]\n\n for (let t = 0; t < n - 1; t++) {\n const yCurr = Y[t + 1]\n const yNext = Y[t + 2]\n const yExit = yCurr + R - O_TOTAL + Y_OFFSET\n\n if (t % 2 === 0) {\n segs.push(`L ${xLeft} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeftArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`L ${xRightArc} ${yCurr - o + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRight} ${yExit}`)\n segs.push(`L ${xRight} ${yNext - R + Y_OFFSET}`)\n } else {\n segs.push(`L ${xRight} ${yCurr - R + Y_OFFSET}`)\n segs.push(`A ${rj} ${rj} 0 0 1 ${xRightArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`L ${xLeftArc} ${yCurr - oj + Y_OFFSET}`)\n segs.push(`A ${r} ${r} 0 0 0 ${xLeft} ${yExit}`)\n segs.push(`L ${xLeft} ${yNext - R + Y_OFFSET}`)\n }\n }\n\n const lastY = Y[n]\n if ((n - 2) % 2 === 0) {\n segs.push(`L ${xRight} ${lastY}`)\n } else {\n segs.push(`L ${xLeft} ${lastY}`)\n }\n parts.push({\n d: segs.join(' '),\n stroke: COLORS[i % COLORS.length],\n 'stroke-width': STROKE_WIDTH,\n fill: 'none',\n })\n }\n return parts\n}\n\nconst DEFAULT_SVG_CLASS = 'serpentine-border-svg'\n\nconst DEFAULTS = {\n strokeCount: 5,\n strokeWidth: 8,\n radius: 50,\n horizontalOverflow: 0,\n layoutMode: 'border',\n}\n\n/**\n * Wrapper margin/padding that do not depend on measured width or section heights.\n * Use on first paint so the column does not jump before the SVG paths exist.\n */\nexport function getWrapperBoxStyle(options = {}) {\n const N = options.strokeCount ?? DEFAULTS.strokeCount\n const STROKE_WIDTH = options.strokeWidth ?? DEFAULTS.strokeWidth\n const horizontalOverflow = options.horizontalOverflow ?? DEFAULTS.horizontalOverflow\n const layoutMode = options.layoutMode ?? DEFAULTS.layoutMode\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n\n if (layoutMode === 'border') {\n return {\n boxSizing: 'border-box',\n position: 'relative',\n marginTop: `${TOTAL_BORDER_WIDTH / 2}px`,\n ...(BORDER_EXTRA > 0 && {\n paddingLeft: `${BORDER_EXTRA}px`,\n paddingRight: `${BORDER_EXTRA}px`,\n }),\n }\n }\n return {\n position: 'relative',\n boxSizing: 'border-box',\n }\n}\n\n/**\n * Compute everything needed to render the serpentine border.\n * Accepts either (width + sectionBottomYs) for pure/custom use, or wrapperEl to measure from the DOM.\n * When using wrapperEl, returns null in non-DOM environments (e.g. SSR) or when measurement fails.\n *\n * @param {{\n * width?: number\n * sectionBottomYs?: number[]\n * wrapperEl?: HTMLElement\n * strokeCount?: number\n * strokeWidth?: number\n * radius?: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * colors?: string[]\n * layoutMode?: 'content' | 'border'\n * svgClassName?: string\n * }} options\n * @returns {{\n * wrapperStyle: Record<string, unknown>\n * svgAttributes: { class?: string, viewBox: string, style: string }\n * paths: Array<{ d: string, stroke: string, 'stroke-width': number, fill: string }>\n * } | null}\n */\nexport function serpentineBorder(options) {\n const N = options.strokeCount ?? DEFAULTS.strokeCount\n const STROKE_WIDTH = options.strokeWidth ?? DEFAULTS.strokeWidth\n const R = options.radius ?? DEFAULTS.radius\n const horizontalOverflow = options.horizontalOverflow ?? DEFAULTS.horizontalOverflow\n const COLORS = options.colors ?? DEFAULT_COLORS\n const layoutMode = options.layoutMode ?? DEFAULTS.layoutMode\n const svgClassName = options.svgClassName ?? DEFAULT_SVG_CLASS\n\n let W, Y\n if (options.wrapperEl != null) {\n const wrapperEl = options.wrapperEl\n const hasDOM = typeof document !== 'undefined' && typeof wrapperEl.getBoundingClientRect === 'function'\n if (!hasDOM) return null\n const measured = measureSections(wrapperEl, {\n layoutMode,\n horizontalOverflow,\n strokeCount: N,\n strokeWidth: STROKE_WIDTH,\n excludeClassName: svgClassName,\n })\n if (!measured) return null\n W = measured.width\n Y = measured.sectionBottomYs\n } else {\n if (options.width == null || options.sectionBottomYs == null) return null\n W = options.width\n Y = options.sectionBottomYs\n }\n\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const O_TOTAL = (N - 1) * STROKE_WIDTH\n const TOP_OFFSET = 2 * STROKE_WIDTH\n const Y_OFFSET = O_TOTAL / 2\n const TOP_ARC_SHIFT = ((N - 1) / 2) * STROKE_WIDTH + Y_OFFSET\n\n const wrapperStyle = getWrapperBoxStyle({\n strokeCount: N,\n strokeWidth: STROKE_WIDTH,\n horizontalOverflow,\n layoutMode,\n })\n\n const svgStyleObj =\n layoutMode === 'border'\n ? {\n position: 'absolute',\n overflow: 'hidden',\n ...(BORDER_EXTRA > 0\n ? { width: '100%', left: 0 }\n : BORDER_EXTRA < 0\n ? { width: `calc(100% + ${2 * BORDER_EXTRA}px)`, left: -BORDER_EXTRA }\n : { width: '100%', left: 0 }),\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n : {\n position: 'absolute',\n overflow: 'hidden',\n width: `calc(100% + ${2 * BORDER_EXTRA}px)`,\n left: -BORDER_EXTRA,\n top: -(TOP_OFFSET + TOP_ARC_SHIFT),\n height: `calc(100% + ${TOP_OFFSET + TOP_ARC_SHIFT}px)`,\n }\n const svgStyle = styleObjectToCss(svgStyleObj)\n\n const paths = buildPathD(W, Y, N, R, STROKE_WIDTH, COLORS, TOP_ARC_SHIFT, Y_OFFSET, O_TOTAL, BORDER_EXTRA)\n\n const totalHeight = Y[Y.length - 1] ?? 0\n const totalWidth = Math.max(1, W + 2 * BORDER_EXTRA)\n const viewBoxHeight = totalHeight + TOP_OFFSET + TOP_ARC_SHIFT\n const viewBoxMinX = -BORDER_EXTRA\n const viewBoxMinY = -STROKE_WIDTH * 2 - TOP_ARC_SHIFT\n const viewBoxStr = `${viewBoxMinX} ${viewBoxMinY} ${totalWidth} ${viewBoxHeight}`\n\n return {\n wrapperStyle,\n svgAttributes: {\n class: svgClassName,\n viewBox: viewBoxStr,\n style: svgStyle,\n },\n paths,\n }\n}\n\n/**\n * Compute padding for each section so content does not overlap the border.\n * Returns an object with even, odd, and last: use for even-indexed sections, odd-indexed sections, and the last section respectively.\n *\n * @param {{\n * sectionCount: number\n * strokeCount: number\n * strokeWidth: number\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * }} options\n * @returns {{ even: { top: number, right: number, bottom: number, left: number }, odd: { top: number, right: number, bottom: number, left: number }, last: { top: number, right: number, bottom: number, left: number } }}\n */\nexport function getSectionsPadding(options) {\n const { sectionCount: n, strokeCount: N, strokeWidth: STROKE_WIDTH } = options\n const horizontalOverflow = options.horizontalOverflow ?? 0\n const TOTAL_BORDER_WIDTH = N * STROKE_WIDTH\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, N, STROKE_WIDTH)\n const halfBorderWidth = TOTAL_BORDER_WIDTH / 2\n const insetSide = TOTAL_BORDER_WIDTH - BORDER_EXTRA\n const even = { top: halfBorderWidth, right: 0, bottom: halfBorderWidth, left: insetSide }\n const odd = { top: halfBorderWidth, right: insetSide, bottom: halfBorderWidth, left: 0 }\n const lastIsEven = n > 0 && (n - 1) % 2 === 0\n const last = {\n top: halfBorderWidth,\n right: lastIsEven ? 0 : insetSide,\n bottom: 0,\n left: lastIsEven ? insetSide : 0,\n }\n return { even, odd, last }\n}\n\n/**\n * Measure wrapper and section elements to get width and section bottom Ys.\n * Children with the excludeClassName (default: same class used on the SVG by serpentineBorder) are excluded.\n * horizontalOverflow is resolved to pixels using strokeCount and strokeWidth.\n *\n * @param {HTMLElement} wrapperEl\n * @param {{\n * layoutMode: 'content' | 'border'\n * horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'\n * strokeCount: number\n * strokeWidth: number\n * excludeClassName?: string\n * }} options\n * @returns {{ width: number, sectionBottomYs: number[] } | null}\n */\nexport function measureSections(wrapperEl, options) {\n const { layoutMode, horizontalOverflow = 0, strokeCount, strokeWidth, excludeClassName = DEFAULT_SVG_CLASS } = options\n const BORDER_EXTRA = resolveOverflowToPixels(horizontalOverflow, strokeCount, strokeWidth)\n if (!wrapperEl) return null\n\n const sectionEls = excludeClassName\n ? Array.from(wrapperEl.children).filter((el) => !el.classList.contains(excludeClassName))\n : Array.from(wrapperEl.children)\n\n if (sectionEls.length === 0) return null\n\n const rect = wrapperEl.getBoundingClientRect()\n const baseWidth = rect.width\n\n const W =\n layoutMode === 'border' && BORDER_EXTRA > 0\n ? Math.max(1, baseWidth - 2 * BORDER_EXTRA)\n : Math.max(1, baseWidth)\n\n const Y = [0]\n for (let i = 0; i < sectionEls.length; i++) {\n const r = sectionEls[i].getBoundingClientRect()\n Y.push(r.top - rect.top + r.height)\n }\n\n return { width: W, sectionBottomYs: Y }\n}\n","import { useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { getWrapperBoxStyle, measureSections, serpentineBorder } from './serpentineCore.js'\n\nfunction cssStringToStyleObject(css) {\n const obj = {}\n if (!css || typeof css !== 'string') return obj\n for (const decl of css.split(';')) {\n const colon = decl.indexOf(':')\n if (colon === -1) continue\n const key = decl.slice(0, colon).trim().replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n const value = decl.slice(colon + 1).trim()\n if (key) obj[key] = value\n }\n return obj\n}\n\nfunction pathAttrsForReact(attrs) {\n return Object.fromEntries(\n Object.entries(attrs).map(([k, v]) => [k === 'stroke-width' ? 'strokeWidth' : k, v])\n )\n}\n\n/** Ignore subpixel / float noise from getBoundingClientRect so ResizeObserver does not thrash setState. */\nfunction measurementCloseEnough(a, b, eps = 0.75) {\n if (!a || !b) return false\n if (Math.abs(a.width - b.width) > eps) return false\n if (a.sectionBottomYs.length !== b.sectionBottomYs.length) return false\n for (let i = 0; i < a.sectionBottomYs.length; i++) {\n if (Math.abs(a.sectionBottomYs[i] - b.sectionBottomYs[i]) > eps) return false\n }\n return true\n}\n\nfunction SerpentineBorder({\n children,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n}) {\n const wrapperRef = useRef(null)\n const lastMeasuredRef = useRef(null)\n const [borderData, setBorderData] = useState(null)\n\n const wrapperStyle = useMemo(\n () =>\n getWrapperBoxStyle({\n strokeCount,\n strokeWidth,\n horizontalOverflow,\n layoutMode,\n }),\n [strokeCount, strokeWidth, horizontalOverflow, layoutMode]\n )\n\n useLayoutEffect(() => {\n lastMeasuredRef.current = null\n const wrapper = wrapperRef.current\n if (!wrapper) return\n\n const measure = () => {\n const measured = measureSections(wrapper, {\n layoutMode,\n horizontalOverflow,\n strokeCount,\n strokeWidth,\n })\n if (!measured) return\n\n const prev = lastMeasuredRef.current\n if (prev && measurementCloseEnough(prev, measured)) {\n return\n }\n lastMeasuredRef.current = {\n width: measured.width,\n sectionBottomYs: [...measured.sectionBottomYs],\n }\n\n const data = serpentineBorder({\n width: measured.width,\n sectionBottomYs: measured.sectionBottomYs,\n strokeCount,\n strokeWidth,\n radius,\n horizontalOverflow,\n colors,\n layoutMode,\n })\n setBorderData(data)\n }\n\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(wrapper)\n return () => ro.disconnect()\n }, [strokeCount, strokeWidth, radius, horizontalOverflow, colors, layoutMode])\n\n return (\n <div\n ref={wrapperRef}\n className=\"serpentine-wrapper\"\n style={wrapperStyle}\n data-testid=\"serpentine-wrapper\"\n >\n {borderData && (() => {\n const { class: className, style: styleStr, ...restSvgAttrs } = borderData.svgAttributes\n const style = typeof styleStr === 'string' ? cssStringToStyleObject(styleStr) : styleStr\n return (\n <svg\n data-testid=\"serpentine-svg\"\n className={className}\n style={style}\n preserveAspectRatio=\"none\"\n {...restSvgAttrs}\n >\n {borderData.paths.map((pathAttributes, i) => (\n <path key={i} {...pathAttrsForReact(pathAttributes)} />\n ))}\n </svg>\n )\n })()}\n {children}\n </div>\n )\n}\n\nexport default SerpentineBorder\n"],"names":["DEFAULT_COLORS","resolveOverflowToPixels","horizontalOverflow","N","STROKE_WIDTH","totalBorderWidth","styleObjectToCss","obj","k","v","key","val","buildPathD","W","Y","R","COLORS","TOP_ARC_SHIFT","Y_OFFSET","O_TOTAL","BORDER_EXTRA","R1","RIGHT_EXTEND","n","parts","i","o","oj","r","rj","rj1","leftOffset","xLeft","rightExt","xRight","xLeftArc","xRightArc","xRightR1","yCurrTop","segs","t","yCurr","yNext","yExit","lastY","DEFAULT_SVG_CLASS","DEFAULTS","getWrapperBoxStyle","options","layoutMode","TOTAL_BORDER_WIDTH","serpentineBorder","svgClassName","wrapperEl","measured","measureSections","TOP_OFFSET","wrapperStyle","svgStyleObj","svgStyle","paths","totalHeight","totalWidth","viewBoxHeight","viewBoxMinX","viewBoxMinY","viewBoxStr","getSectionsPadding","halfBorderWidth","insetSide","even","odd","lastIsEven","strokeCount","strokeWidth","excludeClassName","sectionEls","el","rect","baseWidth","cssStringToStyleObject","css","decl","colon","_","c","value","pathAttrsForReact","attrs","measurementCloseEnough","a","b","eps","SerpentineBorder","children","radius","colors","wrapperRef","useRef","lastMeasuredRef","borderData","setBorderData","useState","useMemo","useLayoutEffect","wrapper","measure","prev","data","ro","jsxs","className","styleStr","restSvgAttrs","style","jsx","pathAttributes"],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -57,4 +57,11 @@ export interface SectionsPaddingMap {
|
|
|
57
57
|
|
|
58
58
|
export declare function getSectionsPadding(options: GetSectionsPaddingOptions): SectionsPaddingMap
|
|
59
59
|
|
|
60
|
+
export declare function getWrapperBoxStyle(options?: {
|
|
61
|
+
strokeCount?: number
|
|
62
|
+
strokeWidth?: number
|
|
63
|
+
horizontalOverflow?: number | 'borderWidth' | 'halfBorderWidth'
|
|
64
|
+
layoutMode?: 'content' | 'border'
|
|
65
|
+
}): Record<string, unknown>
|
|
66
|
+
|
|
60
67
|
export declare function serpentineBorder(options: SerpentineBorderOptions): SerpentineBorderResult | null
|