serpentine-border 1.0.0 → 1.0.1
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/README.md +2 -28
- package/dist/serpentine-border.cjs +1 -1
- package/dist/serpentine-border.js +0 -1
- package/index.d.ts +0 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,10 +54,6 @@ Returns `wrapperStyle`, `svgAttributes` (class, viewBox, style), and `paths`. Pa
|
|
|
54
54
|
| `layoutMode` | `'content' \| 'border'` | `'content'` | `'content'`: layout from content; `'border'`: outer border edge defines box. |
|
|
55
55
|
| `svgClassName` | `string` | `'serpentine-border-svg'` | Class applied to the SVG (and used to exclude it when measuring). |
|
|
56
56
|
|
|
57
|
-
### measureSections(wrapperEl, options)
|
|
58
|
-
|
|
59
|
-
Measures a wrapper and its section children; returns `{ width, sectionBottomYs }` or `null`. Options: `layoutMode`, `horizontalOverlap`, `strokeCount`, `strokeWidth`, and optional `excludeClassName` (default: same as `serpentineBorder`’s `svgClassName`). Use when you want to measure once and pass dimensions into `serpentineBorder`, or in environments without a DOM.
|
|
60
|
-
|
|
61
57
|
### React: SerpentineBorder
|
|
62
58
|
|
|
63
59
|
Wrap your content with the React component; it accepts the same options as `serpentineBorder` as props.
|
|
@@ -81,31 +77,9 @@ function App() {
|
|
|
81
77
|
}
|
|
82
78
|
```
|
|
83
79
|
|
|
84
|
-
##
|
|
85
|
-
|
|
86
|
-
When you need to measure once and reuse, or run without a DOM (SSR, workers), use `measureSections` then call `serpentineBorder` with `width` and `sectionBottomYs`:
|
|
87
|
-
|
|
88
|
-
```js
|
|
89
|
-
import { measureSections, serpentineBorder } from 'serpentine-border'
|
|
80
|
+
## SSR
|
|
90
81
|
|
|
91
|
-
|
|
92
|
-
const measured = measureSections(wrapper, {
|
|
93
|
-
layoutMode: 'content',
|
|
94
|
-
horizontalOverlap: 20,
|
|
95
|
-
strokeCount: 5,
|
|
96
|
-
strokeWidth: 8,
|
|
97
|
-
})
|
|
98
|
-
if (measured) {
|
|
99
|
-
const result = serpentineBorder({
|
|
100
|
-
width: measured.width,
|
|
101
|
-
sectionBottomYs: measured.sectionBottomYs,
|
|
102
|
-
horizontalOverlap: 20,
|
|
103
|
-
})
|
|
104
|
-
if (result) {
|
|
105
|
-
// Apply result.wrapperStyle, result.svgAttributes, result.paths
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
```
|
|
82
|
+
When DOM is unavailable (e.g. server-side), pass `width` and `sectionBottomYs` into `serpentineBorder({ width, sectionBottomYs, ... })` instead of `wrapperEl`.
|
|
109
83
|
|
|
110
84
|
## License
|
|
111
85
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const W=require("react/jsx-runtime"),z=require("react"),J=["#ffffff","#000000"];function G(t,c,o){if(typeof t=="number")return t;const s=c*o;return t==="borderWidth"?s:t==="halfBorderWidth"?s/2:0}function Q(t,c,o,s,u,g,l,n,r,h){const e=u*(o-1),a=u/2,$=c.length-1,i=[];for(let f=0;f<o;f++){const d=f*u,m=(o-1-f)*u,v=s-d,B=s-m,C=e-m,k=r-h+a,x=d-r+k,L=h,b=t+L-m-a,y=x+(s-d),N=t+L-s-a,A=t+L-e-a,D=r+n,p=[`M ${b} ${D-e-u/2-l}`,`L ${b} ${D-e-l}`,`A ${C} ${C} 0 0 1 ${A} ${D-m-l}`,`L ${y} ${d+n-l}`,`A ${v} ${v} 0 0 0 ${x} ${s+n-l}`,`L ${x} ${s+n}`];for(let M=0;M<$-1;M++){const w=c[M+1],X=c[M+2],q=w+s-r+n;M%2===0?(p.push(`L ${x} ${w-s+n}`),p.push(`A ${v} ${v} 0 0 0 ${y} ${w-d+n}`),p.push(`L ${N} ${w-d+n}`),p.push(`A ${B} ${B} 0 0 1 ${b} ${q}`),p.push(`L ${b} ${X-s+n}`)):(p.push(`L ${b} ${w-s+n}`),p.push(`A ${B} ${B} 0 0 1 ${N} ${w-m+n}`),p.push(`L ${y} ${w-m+n}`),p.push(`A ${v} ${v} 0 0 0 ${x} ${q}`),p.push(`L ${x} ${X-s+n}`))}const U=c[$];($-2)%2===0?p.push(`L ${b} ${U}`):p.push(`L ${x} ${U}`),i.push({d:p.join(" "),stroke:g[f%g.length],strokeWidth:u,fill:"none"})}return i}const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const W=require("react/jsx-runtime"),z=require("react"),J=["#ffffff","#000000"];function G(t,c,o){if(typeof t=="number")return t;const s=c*o;return t==="borderWidth"?s:t==="halfBorderWidth"?s/2:0}function Q(t,c,o,s,u,g,l,n,r,h){const e=u*(o-1),a=u/2,$=c.length-1,i=[];for(let f=0;f<o;f++){const d=f*u,m=(o-1-f)*u,v=s-d,B=s-m,C=e-m,k=r-h+a,x=d-r+k,L=h,b=t+L-m-a,y=x+(s-d),N=t+L-s-a,A=t+L-e-a,D=r+n,p=[`M ${b} ${D-e-u/2-l}`,`L ${b} ${D-e-l}`,`A ${C} ${C} 0 0 1 ${A} ${D-m-l}`,`L ${y} ${d+n-l}`,`A ${v} ${v} 0 0 0 ${x} ${s+n-l}`,`L ${x} ${s+n}`];for(let M=0;M<$-1;M++){const w=c[M+1],X=c[M+2],q=w+s-r+n;M%2===0?(p.push(`L ${x} ${w-s+n}`),p.push(`A ${v} ${v} 0 0 0 ${y} ${w-d+n}`),p.push(`L ${N} ${w-d+n}`),p.push(`A ${B} ${B} 0 0 1 ${b} ${q}`),p.push(`L ${b} ${X-s+n}`)):(p.push(`L ${b} ${w-s+n}`),p.push(`A ${B} ${B} 0 0 1 ${N} ${w-m+n}`),p.push(`L ${y} ${w-m+n}`),p.push(`A ${v} ${v} 0 0 0 ${x} ${q}`),p.push(`L ${x} ${X-s+n}`))}const U=c[$];($-2)%2===0?p.push(`L ${b} ${U}`):p.push(`L ${x} ${U}`),i.push({d:p.join(" "),stroke:g[f%g.length],strokeWidth:u,fill:"none"})}return i}const S="serpentine-border-svg",j={strokeCount:5,strokeWidth:8,radius:50,horizontalOverlap:0,layoutMode:"content"};function P(t){const c=t.strokeCount??j.strokeCount,o=t.strokeWidth??j.strokeWidth,s=t.radius??j.radius,u=t.horizontalOverlap??j.horizontalOverlap,g=t.colors??J,l=t.layoutMode??j.layoutMode,n=t.svgClassName??S;let r,h;if(t.wrapperEl!=null){const y=t.wrapperEl;if(!(typeof document<"u"&&typeof y.getBoundingClientRect=="function"))return null;const A=V(y,{layoutMode:l,horizontalOverlap:u,strokeCount:c,strokeWidth:o,excludeClassName:n});if(!A)return null;r=A.width,h=A.sectionBottomYs}else{if(t.width==null||t.sectionBottomYs==null)return null;r=t.width,h=t.sectionBottomYs}const e=G(u,c,o),a=(c-1)*o,$=c*o,i=2*o,f=a/2,d=(c-1)/2*o+f,R=l==="border"?{boxSizing:"border-box",position:"relative",marginTop:$/2,...e>0&&{paddingLeft:e,paddingRight:e}}:{position:"relative",boxSizing:"border-box"},m=l==="border"?{position:"absolute",overflow:"hidden",width:"100%",left:0,top:-(i+d),height:`calc(100% + ${i+d}px)`}:{position:"absolute",overflow:"hidden",width:`calc(100% + ${2*e}px)`,left:-e,top:-(i+d),height:`calc(100% + ${i+d}px)`},v=Q(r,h,c,s,o,g,d,f,a,e),B=h[h.length-1]??0,C=Math.max(1,r+2*e),k=B+i+d,x=e>0?-e:0,L=-o*2-d,b=`${x} ${L} ${C} ${k}`;return{wrapperStyle:R,svgAttributes:{class:n,viewBox:b,style:m},paths:v}}function V(t,c){const{layoutMode:o,horizontalOverlap:s=0,strokeCount:u,strokeWidth:g,excludeClassName:l=S}=c,n=G(s,u,g);if(!t)return null;const r=l?Array.from(t.children).filter(i=>!i.classList.contains(l)):Array.from(t.children);if(r.length===0)return null;const h=t.getBoundingClientRect(),e=h.width,a=o==="border"?Math.max(1,e-2*n):Math.max(1,e),$=[0];for(let i=0;i<r.length;i++){const f=r[i].getBoundingClientRect();$.push(f.top-h.top+f.height)}return{width:a,sectionBottomYs:$}}function Z({children:t,strokeCount:c,strokeWidth:o,radius:s,horizontalOverlap:u,colors:g,layoutMode:l}){const n=z.useRef(null),[r,h]=z.useState(null);return z.useEffect(()=>{const e=n.current;if(!e)return;const a=()=>{const i=V(e,{layoutMode:l,horizontalOverlap:u,strokeCount:c,strokeWidth:o});if(!i)return;const f=P({width:i.width,sectionBottomYs:i.sectionBottomYs,strokeCount:c,strokeWidth:o,radius:s,horizontalOverlap:u,colors:g,layoutMode:l});h(f)};a();const $=new ResizeObserver(a);return $.observe(e),()=>$.disconnect()},[c,o,s,u,g,l]),W.jsxs("div",{ref:n,className:"serpentine-wrapper",style:(r==null?void 0:r.wrapperStyle)??{position:"relative",boxSizing:"border-box"},"data-testid":"serpentine-wrapper",children:[r&&(()=>{const{class:e,...a}=r.svgAttributes;return W.jsx("svg",{"data-testid":"serpentine-svg",className:e,...a,children:r.paths.map(($,i)=>W.jsx("path",{...$},i))})})(),t]})}exports.SerpentineBorder=Z;exports.serpentineBorder=P;
|
|
2
2
|
//# sourceMappingURL=serpentine-border.cjs.map
|
package/index.d.ts
CHANGED
|
@@ -36,14 +36,3 @@ export interface SerpentineBorderResult {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export declare function serpentineBorder(options: SerpentineBorderOptions): SerpentineBorderResult | null
|
|
39
|
-
|
|
40
|
-
export declare function measureSections(
|
|
41
|
-
wrapperEl: HTMLElement,
|
|
42
|
-
options: {
|
|
43
|
-
layoutMode: 'content' | 'border'
|
|
44
|
-
horizontalOverlap?: number | 'borderWidth' | 'halfBorderWidth'
|
|
45
|
-
strokeCount: number
|
|
46
|
-
strokeWidth: number
|
|
47
|
-
excludeClassName?: string
|
|
48
|
-
}
|
|
49
|
-
): { width: number; sectionBottomYs: number[] } | null
|