@tetrax/squircle 1.0.0

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.
@@ -0,0 +1,773 @@
1
+ "use client";
2
+
3
+ // src/react/index.tsx
4
+ import React, { useRef, useState, useEffect, useMemo } from "react";
5
+
6
+ // src/core/squircle-path.ts
7
+ function toRadians(deg) {
8
+ return deg * Math.PI / 180;
9
+ }
10
+ function rounded(strings, ...values) {
11
+ return strings.reduce((acc, str, i) => {
12
+ const value = values[i];
13
+ return typeof value === "number" ? acc + str + value.toFixed(4) : acc + str + (value ?? "");
14
+ }, "");
15
+ }
16
+ function getPathParamsForCorner({
17
+ cornerRadius,
18
+ cornerSmoothing,
19
+ preserveSmoothing,
20
+ roundingAndSmoothingBudget
21
+ }) {
22
+ if (cornerRadius <= 0 || roundingAndSmoothingBudget <= 0) {
23
+ return { a: 0, b: 0, c: 0, d: 0, p: 0, cornerRadius: 0, arcSectionLength: 0 };
24
+ }
25
+ let p = (1 + cornerSmoothing) * cornerRadius;
26
+ if (!preserveSmoothing) {
27
+ const maxCornerSmoothing = roundingAndSmoothingBudget / cornerRadius - 1;
28
+ cornerSmoothing = Math.min(cornerSmoothing, maxCornerSmoothing);
29
+ p = Math.min(p, roundingAndSmoothingBudget);
30
+ }
31
+ const arcMeasure = 90 * (1 - cornerSmoothing);
32
+ const arcSectionLength = Math.sin(toRadians(arcMeasure / 2)) * cornerRadius * Math.sqrt(2);
33
+ const angleAlpha = (90 - arcMeasure) / 2;
34
+ const p3ToP4Distance = cornerRadius * Math.tan(toRadians(angleAlpha / 2));
35
+ const angleBeta = 45 * cornerSmoothing;
36
+ const c = p3ToP4Distance * Math.cos(toRadians(angleBeta));
37
+ const d = c * Math.tan(toRadians(angleBeta));
38
+ let b = (p - arcSectionLength - c - d) / 3;
39
+ let a = 2 * b;
40
+ if (preserveSmoothing && p > roundingAndSmoothingBudget) {
41
+ const p1ToP3MaxDistance = roundingAndSmoothingBudget - d - arcSectionLength - c;
42
+ const minA = p1ToP3MaxDistance / 6;
43
+ const maxB = p1ToP3MaxDistance - minA;
44
+ b = Math.min(b, maxB);
45
+ a = p1ToP3MaxDistance - b;
46
+ p = Math.min(p, roundingAndSmoothingBudget);
47
+ }
48
+ return { a, b, c, d, p, arcSectionLength, cornerRadius };
49
+ }
50
+ function drawTopRightPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
51
+ if (cornerRadius) {
52
+ return rounded`c ${a} 0 ${a + b} 0 ${a + b + c} ${d} a ${cornerRadius} ${cornerRadius} 0 0 1 ${arcSectionLength} ${arcSectionLength} c ${d} ${c} ${d} ${b + c} ${d} ${a + b + c}`;
53
+ }
54
+ return rounded`l ${p} 0`;
55
+ }
56
+ function drawBottomRightPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
57
+ if (cornerRadius) {
58
+ return rounded`c 0 ${a} 0 ${a + b} ${-d} ${a + b + c} a ${cornerRadius} ${cornerRadius} 0 0 1 ${-arcSectionLength} ${arcSectionLength} c ${-c} ${d} ${-(b + c)} ${d} ${-(a + b + c)} ${d}`;
59
+ }
60
+ return rounded`l 0 ${p}`;
61
+ }
62
+ function drawBottomLeftPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
63
+ if (cornerRadius) {
64
+ return rounded`c ${-a} 0 ${-(a + b)} 0 ${-(a + b + c)} ${-d} a ${cornerRadius} ${cornerRadius} 0 0 1 ${-arcSectionLength} ${-arcSectionLength} c ${-d} ${-c} ${-d} ${-(b + c)} ${-d} ${-(a + b + c)}`;
65
+ }
66
+ return rounded`l ${-p} 0`;
67
+ }
68
+ function drawTopLeftPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
69
+ if (cornerRadius) {
70
+ return rounded`c 0 ${-a} 0 ${-(a + b)} ${d} ${-(a + b + c)} a ${cornerRadius} ${cornerRadius} 0 0 1 ${arcSectionLength} ${-arcSectionLength} c ${c} ${-d} ${b + c} ${-d} ${a + b + c} ${-d}`;
71
+ }
72
+ return rounded`l 0 ${-p}`;
73
+ }
74
+ function getSVGPathFromPathParams({
75
+ width,
76
+ height,
77
+ topLeftPathParams,
78
+ topRightPathParams,
79
+ bottomRightPathParams,
80
+ bottomLeftPathParams
81
+ }) {
82
+ return `
83
+ M ${width - topRightPathParams.p} 0
84
+ ${drawTopRightPath(topRightPathParams)}
85
+ L ${width} ${height - bottomRightPathParams.p}
86
+ ${drawBottomRightPath(bottomRightPathParams)}
87
+ L ${bottomLeftPathParams.p} ${height}
88
+ ${drawBottomLeftPath(bottomLeftPathParams)}
89
+ L 0 ${topLeftPathParams.p}
90
+ ${drawTopLeftPath(topLeftPathParams)}
91
+ Z
92
+ `.replace(/[\t\s\n]+/g, " ").trim();
93
+ }
94
+ var adjacentsByCorner = {
95
+ topLeft: [
96
+ { corner: "topRight", side: "top" },
97
+ { corner: "bottomLeft", side: "left" }
98
+ ],
99
+ topRight: [
100
+ { corner: "topLeft", side: "top" },
101
+ { corner: "bottomRight", side: "right" }
102
+ ],
103
+ bottomLeft: [
104
+ { corner: "bottomRight", side: "bottom" },
105
+ { corner: "topLeft", side: "left" }
106
+ ],
107
+ bottomRight: [
108
+ { corner: "bottomLeft", side: "bottom" },
109
+ { corner: "topRight", side: "right" }
110
+ ]
111
+ };
112
+ function distributeAndNormalize({
113
+ topLeftCornerRadius,
114
+ topRightCornerRadius,
115
+ bottomRightCornerRadius,
116
+ bottomLeftCornerRadius,
117
+ width,
118
+ height
119
+ }) {
120
+ const roundingAndSmoothingBudgetMap = {
121
+ topLeft: -1,
122
+ topRight: -1,
123
+ bottomLeft: -1,
124
+ bottomRight: -1
125
+ };
126
+ const cornerRadiusMap = {
127
+ topLeft: topLeftCornerRadius,
128
+ topRight: topRightCornerRadius,
129
+ bottomLeft: bottomLeftCornerRadius,
130
+ bottomRight: bottomRightCornerRadius
131
+ };
132
+ Object.entries(cornerRadiusMap).sort(([, r1], [, r2]) => r2 - r1).forEach(([corner, radius]) => {
133
+ const adjacents = adjacentsByCorner[corner];
134
+ const budget = Math.min(
135
+ ...adjacents.map((adj) => {
136
+ const adjRadius = cornerRadiusMap[adj.corner];
137
+ if (radius === 0 && adjRadius === 0) return 0;
138
+ const adjBudget = roundingAndSmoothingBudgetMap[adj.corner];
139
+ const sideLen = adj.side === "top" || adj.side === "bottom" ? width : height;
140
+ return adjBudget >= 0 ? sideLen - roundingAndSmoothingBudgetMap[adj.corner] : radius / (radius + adjRadius) * sideLen;
141
+ })
142
+ );
143
+ roundingAndSmoothingBudgetMap[corner] = budget;
144
+ cornerRadiusMap[corner] = Math.min(radius, budget);
145
+ });
146
+ return {
147
+ topLeft: { radius: cornerRadiusMap.topLeft, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.topLeft },
148
+ topRight: { radius: cornerRadiusMap.topRight, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.topRight },
149
+ bottomLeft: { radius: cornerRadiusMap.bottomLeft, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.bottomLeft },
150
+ bottomRight: { radius: cornerRadiusMap.bottomRight, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.bottomRight }
151
+ };
152
+ }
153
+ function drawPureBezierTopRight(r) {
154
+ if (r <= 0) return "";
155
+ return rounded`c ${0.3 * r} 0 ${0.473 * r} 0 ${0.619 * r} ${0.039 * r} c ${0.185 * r} ${0.049 * r} ${0.293 * r} ${0.157 * r} ${0.342 * r} ${0.342 * r} c ${0.039 * r} ${0.146 * r} ${0.039 * r} ${0.319 * r} ${0.039 * r} ${0.619 * r}`;
156
+ }
157
+ function drawPureBezierBottomRight(r) {
158
+ if (r <= 0) return "";
159
+ return rounded`c 0 ${0.3 * r} 0 ${0.473 * r} ${-0.039 * r} ${0.619 * r} c ${-0.049 * r} ${0.185 * r} ${-0.157 * r} ${0.293 * r} ${-0.342 * r} ${0.342 * r} c ${-0.146 * r} ${0.039 * r} ${-0.319 * r} ${0.039 * r} ${-0.619 * r} ${0.039 * r}`;
160
+ }
161
+ function drawPureBezierBottomLeft(r) {
162
+ if (r <= 0) return "";
163
+ return rounded`c ${-0.3 * r} 0 ${-0.473 * r} 0 ${-0.619 * r} ${-0.039 * r} c ${-0.185 * r} ${-0.049 * r} ${-0.293 * r} ${-0.157 * r} ${-0.342 * r} ${-0.342 * r} c ${-0.039 * r} ${-0.146 * r} ${-0.039 * r} ${-0.319 * r} ${-0.039 * r} ${-0.619 * r}`;
164
+ }
165
+ function drawPureBezierTopLeft(r) {
166
+ if (r <= 0) return "";
167
+ return rounded`c 0 ${-0.3 * r} 0 ${-0.473 * r} ${0.039 * r} ${-0.619 * r} c ${0.049 * r} ${-0.185 * r} ${0.157 * r} ${-0.293 * r} ${0.342 * r} ${-0.342 * r} c ${0.146 * r} ${-0.039 * r} ${0.319 * r} ${-0.039 * r} ${0.619 * r} ${-0.039 * r}`;
168
+ }
169
+ function getPureBezierSVGPath(width, height, tl, tr, br, bl) {
170
+ return `
171
+ M ${width - tr} 0
172
+ ${drawPureBezierTopRight(tr)}
173
+ L ${width} ${height - br}
174
+ ${drawPureBezierBottomRight(br)}
175
+ L ${bl} ${height}
176
+ ${drawPureBezierBottomLeft(bl)}
177
+ L 0 ${tl}
178
+ ${drawPureBezierTopLeft(tl)}
179
+ Z
180
+ `.replace(/[\t\s\n]+/g, " ").trim();
181
+ }
182
+ function getSvgPath({
183
+ cornerRadius = 0,
184
+ topLeftCornerRadius,
185
+ topRightCornerRadius,
186
+ bottomRightCornerRadius,
187
+ bottomLeftCornerRadius,
188
+ cornerSmoothing = 1,
189
+ width,
190
+ height,
191
+ preserveSmoothing = false,
192
+ g2Continuous = false
193
+ }) {
194
+ const tl = topLeftCornerRadius ?? cornerRadius;
195
+ const tr = topRightCornerRadius ?? cornerRadius;
196
+ const bl = bottomLeftCornerRadius ?? cornerRadius;
197
+ const br = bottomRightCornerRadius ?? cornerRadius;
198
+ if (g2Continuous) {
199
+ const { topLeft: topLeft2, topRight: topRight2, bottomLeft: bottomLeft2, bottomRight: bottomRight2 } = distributeAndNormalize({
200
+ topLeftCornerRadius: tl,
201
+ topRightCornerRadius: tr,
202
+ bottomRightCornerRadius: br,
203
+ bottomLeftCornerRadius: bl,
204
+ width,
205
+ height
206
+ });
207
+ return getPureBezierSVGPath(
208
+ width,
209
+ height,
210
+ topLeft2.radius,
211
+ topRight2.radius,
212
+ bottomRight2.radius,
213
+ bottomLeft2.radius
214
+ );
215
+ }
216
+ if (tl === tr && tr === br && br === bl) {
217
+ const roundingAndSmoothingBudget = Math.min(width, height) / 2;
218
+ const clampedRadius = Math.min(tl, roundingAndSmoothingBudget);
219
+ const params = getPathParamsForCorner({
220
+ cornerRadius: clampedRadius,
221
+ cornerSmoothing,
222
+ preserveSmoothing,
223
+ roundingAndSmoothingBudget
224
+ });
225
+ return getSVGPathFromPathParams({
226
+ width,
227
+ height,
228
+ topLeftPathParams: params,
229
+ topRightPathParams: params,
230
+ bottomLeftPathParams: params,
231
+ bottomRightPathParams: params
232
+ });
233
+ }
234
+ const { topLeft, topRight, bottomLeft, bottomRight } = distributeAndNormalize({
235
+ topLeftCornerRadius: tl,
236
+ topRightCornerRadius: tr,
237
+ bottomRightCornerRadius: br,
238
+ bottomLeftCornerRadius: bl,
239
+ width,
240
+ height
241
+ });
242
+ return getSVGPathFromPathParams({
243
+ width,
244
+ height,
245
+ topLeftPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: topLeft.radius, roundingAndSmoothingBudget: topLeft.roundingAndSmoothingBudget }),
246
+ topRightPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: topRight.radius, roundingAndSmoothingBudget: topRight.roundingAndSmoothingBudget }),
247
+ bottomRightPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: bottomRight.radius, roundingAndSmoothingBudget: bottomRight.roundingAndSmoothingBudget }),
248
+ bottomLeftPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: bottomLeft.radius, roundingAndSmoothingBudget: bottomLeft.roundingAndSmoothingBudget })
249
+ });
250
+ }
251
+ function computeSquirclePath(width, height, cornerRadius, cornerSmoothing = 1, preserveSmoothing = false, g2Continuous = false) {
252
+ if (width <= 0 || height <= 0 || cornerRadius <= 0) return "";
253
+ return getSvgPath({ width, height, cornerRadius, cornerSmoothing, preserveSmoothing, g2Continuous });
254
+ }
255
+
256
+ // src/core/class-parser.ts
257
+ var ROUNDED_RE = /^rounded-squircle-(\d+)$/;
258
+ var SMOOTHING_RE = /^corner-smoothing-(\d{1,3})$/;
259
+ var SQUIRCLE_BORDER_WIDTH_RE = /^squircle-border-(\d+(\.\d+)?)$/;
260
+ var TW_BORDER_COLOR_RE = /^border-(\[.+\]|[\w-]+)(\/(\d+))?$/;
261
+ var TW_BORDER_WIDTH_RE = /^border-(\d+)$/;
262
+ var TW_BORDER_BASE_RE = /^border$/;
263
+ var BORDER_COLORS = {
264
+ "gray-50": "#f9fafb",
265
+ "gray-100": "#f3f4f6",
266
+ "gray-200": "#e5e7eb",
267
+ "gray-300": "#d1d5db",
268
+ "gray-400": "#9ca3af",
269
+ "gray-500": "#6b7280",
270
+ "gray-600": "#4b5563",
271
+ "gray-700": "#374151",
272
+ "gray-800": "#1f2937",
273
+ "gray-900": "#111827",
274
+ "slate-50": "#f8fafc",
275
+ "slate-100": "#f1f5f9",
276
+ "slate-200": "#e2e8f0",
277
+ "slate-300": "#cbd5e1",
278
+ "slate-400": "#94a3b8",
279
+ "slate-500": "#64748b",
280
+ "slate-600": "#475569",
281
+ "slate-700": "#334155",
282
+ "slate-800": "#1e293b",
283
+ "slate-900": "#0f172a",
284
+ "red-50": "#fef2f2",
285
+ "red-100": "#fee2e2",
286
+ "red-200": "#fecaca",
287
+ "red-300": "#fca5a5",
288
+ "red-400": "#f87171",
289
+ "red-500": "#ef4444",
290
+ "red-600": "#dc2626",
291
+ "red-700": "#b91c1c",
292
+ "red-800": "#991b1b",
293
+ "red-900": "#7f1d1d",
294
+ "blue-50": "#eff6ff",
295
+ "blue-100": "#dbeafe",
296
+ "blue-200": "#bfdbfe",
297
+ "blue-300": "#93c5fd",
298
+ "blue-400": "#60a5fa",
299
+ "blue-500": "#3b82f6",
300
+ "blue-600": "#2563eb",
301
+ "blue-700": "#1d4ed8",
302
+ "blue-800": "#1e40af",
303
+ "blue-900": "#1e3a8a",
304
+ "green-50": "#f0fdf4",
305
+ "green-100": "#dcfce7",
306
+ "green-200": "#bbf7d0",
307
+ "green-300": "#86efac",
308
+ "green-400": "#4ade80",
309
+ "green-500": "#22c55e",
310
+ "green-600": "#16a34a",
311
+ "green-700": "#15803d",
312
+ "green-800": "#166534",
313
+ "green-900": "#14532d",
314
+ "yellow-50": "#fefce8",
315
+ "yellow-100": "#fef9c3",
316
+ "yellow-200": "#fef08a",
317
+ "yellow-300": "#fde047",
318
+ "yellow-400": "#facc15",
319
+ "yellow-500": "#eab308",
320
+ "yellow-600": "#ca8a04",
321
+ "yellow-700": "#a16207",
322
+ "yellow-800": "#854d0e",
323
+ "yellow-900": "#713f12",
324
+ "purple-50": "#faf5ff",
325
+ "purple-100": "#f3e8ff",
326
+ "purple-200": "#e9d5ff",
327
+ "purple-300": "#d8b4fe",
328
+ "purple-400": "#c084fc",
329
+ "purple-500": "#a855f7",
330
+ "purple-600": "#9333ea",
331
+ "purple-700": "#7e22ce",
332
+ "purple-800": "#6b21a8",
333
+ "purple-900": "#581c87",
334
+ "pink-50": "#fdf2f8",
335
+ "pink-100": "#fce7f3",
336
+ "pink-200": "#fbcfe8",
337
+ "pink-300": "#f9a8d4",
338
+ "pink-400": "#f472b6",
339
+ "pink-500": "#ec4899",
340
+ "pink-600": "#db2777",
341
+ "pink-700": "#be185d",
342
+ "pink-800": "#9d174d",
343
+ "pink-900": "#831843",
344
+ "orange-50": "#fff7ed",
345
+ "orange-100": "#ffedd5",
346
+ "orange-200": "#fed7aa",
347
+ "orange-300": "#fdba74",
348
+ "orange-400": "#fb923c",
349
+ "orange-500": "#f97316",
350
+ "orange-600": "#ea580c",
351
+ "orange-700": "#c2410c",
352
+ "orange-800": "#9a3412",
353
+ "orange-900": "#7c2d12",
354
+ "teal-50": "#f0fdfa",
355
+ "teal-100": "#ccfbf1",
356
+ "teal-200": "#99f6e4",
357
+ "teal-300": "#5eead4",
358
+ "teal-400": "#2dd4bf",
359
+ "teal-500": "#14b8a6",
360
+ "teal-600": "#0d9488",
361
+ "teal-700": "#0f766e",
362
+ "teal-800": "#115e59",
363
+ "teal-900": "#134e4a",
364
+ white: "#ffffff",
365
+ black: "#000000",
366
+ transparent: "transparent"
367
+ };
368
+ function hexToRgba(hex, opacity) {
369
+ const h = hex.replace(/#/g, "");
370
+ const r = parseInt(h.substring(0, 2), 16);
371
+ const g = parseInt(h.substring(2, 4), 16);
372
+ const b = parseInt(h.substring(4, 6), 16);
373
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
374
+ }
375
+ function resolveColorToken(token, opacity) {
376
+ if (token.startsWith("[") && token.endsWith("]")) {
377
+ const raw = token.slice(1, -1);
378
+ if (opacity !== void 0 && raw.startsWith("#")) {
379
+ return hexToRgba(raw, opacity);
380
+ }
381
+ return raw;
382
+ }
383
+ const hex = BORDER_COLORS[token];
384
+ if (!hex) return null;
385
+ if (opacity !== void 0) return hexToRgba(hex, opacity);
386
+ return hex;
387
+ }
388
+ function parseSquircleClasses(className = "") {
389
+ const classes = className.split(/\s+/).filter(Boolean);
390
+ let cornerRadius = null;
391
+ let cornerSmoothing = 1;
392
+ let squircleBorder = false;
393
+ let borderColor = null;
394
+ let borderWidth = null;
395
+ let g2Continuous = false;
396
+ const rest = [];
397
+ for (const cls of classes) {
398
+ let matched = false;
399
+ let m = cls.match(ROUNDED_RE);
400
+ if (m) {
401
+ cornerRadius = Number(m[1]);
402
+ matched = true;
403
+ }
404
+ if (!matched) {
405
+ m = cls.match(SMOOTHING_RE);
406
+ if (m) {
407
+ cornerSmoothing = Math.min(100, Math.max(0, Number(m[1]))) / 100;
408
+ matched = true;
409
+ }
410
+ }
411
+ if (!matched && cls === "squircle-border") {
412
+ squircleBorder = true;
413
+ matched = true;
414
+ }
415
+ if (!matched && cls === "squircle-g2") {
416
+ g2Continuous = true;
417
+ matched = true;
418
+ }
419
+ if (!matched) {
420
+ m = cls.match(SQUIRCLE_BORDER_WIDTH_RE);
421
+ if (m) {
422
+ squircleBorder = true;
423
+ borderWidth = Number(m[1]);
424
+ matched = true;
425
+ }
426
+ }
427
+ if (!matched && squircleBorder) {
428
+ m = cls.match(TW_BORDER_WIDTH_RE);
429
+ if (m) {
430
+ borderWidth = Number(m[1]);
431
+ matched = true;
432
+ }
433
+ if (!matched && cls.match(TW_BORDER_BASE_RE)) {
434
+ borderWidth = 1;
435
+ matched = true;
436
+ }
437
+ if (!matched) {
438
+ m = cls.match(TW_BORDER_COLOR_RE);
439
+ if (m) {
440
+ const colorToken = m[1];
441
+ const opacityPercent = m[3] ? Number(m[3]) / 100 : void 0;
442
+ const resolved = resolveColorToken(colorToken, opacityPercent);
443
+ if (resolved) {
444
+ borderColor = resolved;
445
+ matched = true;
446
+ }
447
+ }
448
+ }
449
+ }
450
+ if (!matched) rest.push(cls);
451
+ }
452
+ return {
453
+ cornerRadius,
454
+ cornerSmoothing,
455
+ squircleBorder,
456
+ borderColor,
457
+ borderWidth: borderWidth ?? 1,
458
+ g2Continuous,
459
+ restClasses: rest.join(" ")
460
+ };
461
+ }
462
+
463
+ // src/react/index.tsx
464
+ import { jsx, jsxs } from "react/jsx-runtime";
465
+ function SquircleSvgBorder({
466
+ width,
467
+ height,
468
+ cornerRadius,
469
+ cornerSmoothing,
470
+ borderColor,
471
+ borderWidth,
472
+ g2Continuous
473
+ }) {
474
+ if (width <= 0 || height <= 0 || cornerRadius <= 0 || borderWidth <= 0) return null;
475
+ const inset = borderWidth / 2;
476
+ const innerW = Math.max(1, width - inset * 2);
477
+ const innerH = Math.max(1, height - inset * 2);
478
+ const innerR = Math.max(0, cornerRadius - inset);
479
+ const pathData = computeSquirclePath(innerW, innerH, innerR, cornerSmoothing, false, g2Continuous);
480
+ if (!pathData) return null;
481
+ return /* @__PURE__ */ jsx(
482
+ "svg",
483
+ {
484
+ width,
485
+ height,
486
+ viewBox: `0 0 ${width} ${height}`,
487
+ style: {
488
+ position: "absolute",
489
+ top: 0,
490
+ left: 0,
491
+ pointerEvents: "none",
492
+ overflow: "visible"
493
+ },
494
+ "aria-hidden": "true",
495
+ children: /* @__PURE__ */ jsx("g", { transform: `translate(${inset}, ${inset})`, children: /* @__PURE__ */ jsx(
496
+ "path",
497
+ {
498
+ d: pathData,
499
+ fill: "none",
500
+ stroke: borderColor,
501
+ strokeWidth: borderWidth,
502
+ strokeLinejoin: "round"
503
+ }
504
+ ) })
505
+ }
506
+ );
507
+ }
508
+ function Squircle(props) {
509
+ const {
510
+ children,
511
+ className = "",
512
+ cornerRadius: cornerRadiusProp,
513
+ cornerSmoothing: cornerSmoothingProp = 1,
514
+ preserveSmoothing: preserveSmoothingProp = false,
515
+ as: Tag = "div",
516
+ g2Continuous = false,
517
+ style,
518
+ ...rest
519
+ } = props;
520
+ const [mounted, setMounted] = useState(false);
521
+ const [el, setEl] = useState(null);
522
+ const [size, setSize] = useState({ width: 0, height: 0 });
523
+ const outerRef = useRef(null);
524
+ useEffect(() => {
525
+ setMounted(true);
526
+ }, []);
527
+ const parsed = useMemo(() => parseSquircleClasses(className), [className]);
528
+ const cornerRadius = cornerRadiusProp ?? parsed.cornerRadius ?? 0;
529
+ const cornerSmoothing = "cornerSmoothing" in props ? cornerSmoothingProp : parsed.cornerSmoothing ?? 1;
530
+ const preserveSmoothing = "preserveSmoothing" in props ? !!preserveSmoothingProp : false;
531
+ const setRef = React.useCallback((node) => {
532
+ outerRef.current = node;
533
+ setEl(node);
534
+ }, []);
535
+ useEffect(() => {
536
+ if (!el) return;
537
+ const update = () => setSize({ width: el.offsetWidth || 0, height: el.offsetHeight || 0 });
538
+ update();
539
+ const ro = new ResizeObserver(update);
540
+ ro.observe(el);
541
+ return () => ro.disconnect();
542
+ }, [el]);
543
+ const clipPath = useMemo(() => {
544
+ if (!mounted || !cornerRadius || size.width === 0 || size.height === 0) return "none";
545
+ const pathData = computeSquirclePath(size.width, size.height, cornerRadius, cornerSmoothing, preserveSmoothing, g2Continuous);
546
+ return pathData ? `path('${pathData}')` : "none";
547
+ }, [mounted, size.width, size.height, cornerRadius, cornerSmoothing, preserveSmoothing, g2Continuous]);
548
+ const squircleReady = mounted && cornerRadius > 0 && size.width > 0 && size.height > 0;
549
+ const showSvgBorder = squircleReady && parsed.squircleBorder;
550
+ const squircleStyle = {
551
+ ...style,
552
+ position: "relative",
553
+ opacity: squircleReady ? 1 : 0,
554
+ transition: squircleReady ? "opacity 80ms ease-in" : void 0
555
+ };
556
+ if (squircleReady && cornerRadius) {
557
+ squircleStyle.clipPath = clipPath;
558
+ squircleStyle.WebkitClipPath = clipPath;
559
+ } else if (cornerRadius) {
560
+ squircleStyle.borderRadius = cornerRadius;
561
+ }
562
+ const Component = Tag;
563
+ return /* @__PURE__ */ jsxs(
564
+ Component,
565
+ {
566
+ ref: setRef,
567
+ "data-squircle": cornerRadius || void 0,
568
+ className: parsed.restClasses,
569
+ style: squircleStyle,
570
+ ...rest,
571
+ children: [
572
+ children,
573
+ showSvgBorder && /* @__PURE__ */ jsx(
574
+ SquircleSvgBorder,
575
+ {
576
+ width: size.width,
577
+ height: size.height,
578
+ cornerRadius,
579
+ cornerSmoothing,
580
+ borderColor: parsed.borderColor || "currentColor",
581
+ borderWidth: parsed.borderWidth,
582
+ g2Continuous
583
+ }
584
+ )
585
+ ]
586
+ }
587
+ );
588
+ }
589
+ function SquircleNoScript() {
590
+ return /* @__PURE__ */ jsx("noscript", { children: /* @__PURE__ */ jsx(
591
+ "style",
592
+ {
593
+ type: "text/css",
594
+ dangerouslySetInnerHTML: {
595
+ __html: "[data-squircle]{clip-path:none!important;border-radius:attr(data-squircle)px!important;-webkit-clip-path:none!important}"
596
+ }
597
+ }
598
+ ) });
599
+ }
600
+ var globalObserverInitialized = false;
601
+ function initGlobalSquircleObserver() {
602
+ if (typeof window === "undefined" || globalObserverInitialized) return;
603
+ globalObserverInitialized = true;
604
+ const resizeObservers = /* @__PURE__ */ new Map();
605
+ const applySquircle = (el) => {
606
+ const className = String(el.className || "");
607
+ const hasSquircleClass = className.split(/\s+/).some((cls) => cls.startsWith("rounded-squircle-"));
608
+ const isVoid = ["INPUT", "IMG", "BR", "HR", "SELECT", "TEXTAREA"].includes(el.tagName);
609
+ if (!hasSquircleClass) {
610
+ if (resizeObservers.has(el)) {
611
+ resizeObservers.get(el)?.disconnect();
612
+ resizeObservers.delete(el);
613
+ el.style.clipPath = "";
614
+ el.style.WebkitClipPath = "";
615
+ el.style.borderRadius = "";
616
+ const svgEl = el.__squircleBorderSvg || el.querySelector("[data-squircle-border]");
617
+ if (svgEl) {
618
+ svgEl.remove();
619
+ }
620
+ }
621
+ return;
622
+ }
623
+ const parsed = parseSquircleClasses(className);
624
+ const cornerRadius = parsed.cornerRadius;
625
+ const cornerSmoothing = parsed.cornerSmoothing;
626
+ const g2Continuous = parsed.g2Continuous;
627
+ if (!cornerRadius) return;
628
+ el.__squircleConfig = parsed;
629
+ if (parsed.squircleBorder) {
630
+ if (isVoid) {
631
+ const parent = el.parentElement;
632
+ if (parent && parent.style.position !== "absolute" && parent.style.position !== "fixed" && parent.style.position !== "relative") {
633
+ parent.style.position = "relative";
634
+ }
635
+ } else if (el.style.position !== "absolute" && el.style.position !== "fixed" && el.style.position !== "relative") {
636
+ el.style.position = "relative";
637
+ }
638
+ }
639
+ if (resizeObservers.has(el)) {
640
+ const updateFn = el.__squircleUpdate;
641
+ if (updateFn) {
642
+ updateFn();
643
+ }
644
+ return;
645
+ }
646
+ if (!resizeObservers.has(el)) {
647
+ const updateSize = () => {
648
+ const config = el.__squircleConfig || parsed;
649
+ const width = el.offsetWidth || 0;
650
+ const height = el.offsetHeight || 0;
651
+ if (width === 0 || height === 0) return;
652
+ const pathData = computeSquirclePath(width, height, config.cornerRadius || 0, config.cornerSmoothing, false, config.g2Continuous);
653
+ const clipPath = pathData ? `path('${pathData}')` : "none";
654
+ el.style.clipPath = clipPath;
655
+ el.style.WebkitClipPath = clipPath;
656
+ el.style.borderRadius = "0px";
657
+ if (config.squircleBorder) {
658
+ let svgEl = el.__squircleBorderSvg;
659
+ if (!svgEl && isVoid && el.nextElementSibling?.getAttribute("data-squircle-border") === "true") {
660
+ svgEl = el.nextElementSibling;
661
+ el.__squircleBorderSvg = svgEl;
662
+ }
663
+ if (!svgEl) {
664
+ svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
665
+ svgEl.setAttribute("data-squircle-border", "true");
666
+ el.__squircleBorderSvg = svgEl;
667
+ if (isVoid) {
668
+ el.after(svgEl);
669
+ } else {
670
+ el.appendChild(svgEl);
671
+ }
672
+ }
673
+ const bw = config.borderWidth;
674
+ const inset = bw / 2;
675
+ const innerW = Math.max(1, width - inset * 2);
676
+ const innerH = Math.max(1, height - inset * 2);
677
+ const innerR = Math.max(0, (config.cornerRadius || 0) - inset);
678
+ const borderPathData = computeSquirclePath(innerW, innerH, innerR, config.cornerSmoothing, false, config.g2Continuous);
679
+ svgEl.setAttribute("width", String(width));
680
+ svgEl.setAttribute("height", String(height));
681
+ svgEl.setAttribute("viewBox", `0 0 ${width} ${height}`);
682
+ svgEl.setAttribute("aria-hidden", "true");
683
+ const posStyle = {
684
+ position: "absolute",
685
+ pointerEvents: "none",
686
+ overflow: "visible"
687
+ };
688
+ if (isVoid) {
689
+ posStyle.top = `${el.offsetTop}px`;
690
+ posStyle.left = `${el.offsetLeft}px`;
691
+ } else {
692
+ posStyle.top = `0px`;
693
+ posStyle.left = `0px`;
694
+ }
695
+ Object.assign(svgEl.style, posStyle);
696
+ let gEl = svgEl.querySelector("g");
697
+ if (!gEl) {
698
+ gEl = document.createElementNS("http://www.w3.org/2000/svg", "g");
699
+ svgEl.appendChild(gEl);
700
+ }
701
+ gEl.setAttribute("transform", `translate(${inset}, ${inset})`);
702
+ let pathEl = svgEl.querySelector("path");
703
+ if (!pathEl) {
704
+ pathEl = document.createElementNS("http://www.w3.org/2000/svg", "path");
705
+ gEl.appendChild(pathEl);
706
+ }
707
+ pathEl.setAttribute("d", borderPathData || "");
708
+ pathEl.setAttribute("fill", "none");
709
+ pathEl.setAttribute("stroke", config.borderColor || "currentColor");
710
+ pathEl.setAttribute("stroke-width", String(bw));
711
+ pathEl.setAttribute("stroke-linejoin", "round");
712
+ } else {
713
+ const svgEl = el.__squircleBorderSvg || el.querySelector("[data-squircle-border]");
714
+ if (svgEl) {
715
+ svgEl.remove();
716
+ el.__squircleBorderSvg = null;
717
+ }
718
+ }
719
+ };
720
+ el.__squircleUpdate = updateSize;
721
+ updateSize();
722
+ const ro = new ResizeObserver(updateSize);
723
+ ro.observe(el);
724
+ resizeObservers.set(el, ro);
725
+ }
726
+ };
727
+ const scan = () => {
728
+ const elements = document.querySelectorAll('[class*="rounded-squircle-"]');
729
+ elements.forEach((el) => applySquircle(el));
730
+ };
731
+ if (document.readyState === "loading") {
732
+ document.addEventListener("DOMContentLoaded", () => {
733
+ requestAnimationFrame(scan);
734
+ });
735
+ } else {
736
+ requestAnimationFrame(scan);
737
+ }
738
+ const mutationObserver = new MutationObserver((mutations) => {
739
+ for (const mutation of mutations) {
740
+ if (mutation.type === "childList") {
741
+ mutation.addedNodes.forEach((node) => {
742
+ if (node.nodeType === Node.ELEMENT_NODE) {
743
+ const el = node;
744
+ if (el.className && String(el.className).split(/\s+/).some((c) => c.startsWith("rounded-squircle-"))) {
745
+ applySquircle(el);
746
+ }
747
+ el.querySelectorAll('[class*="rounded-squircle-"]').forEach((child) => {
748
+ applySquircle(child);
749
+ });
750
+ }
751
+ });
752
+ } else if (mutation.type === "attributes" && mutation.attributeName === "class") {
753
+ applySquircle(mutation.target);
754
+ }
755
+ }
756
+ });
757
+ mutationObserver.observe(document.body, {
758
+ childList: true,
759
+ subtree: true,
760
+ attributes: true,
761
+ attributeFilter: ["class"]
762
+ });
763
+ }
764
+ if (typeof window !== "undefined") {
765
+ initGlobalSquircleObserver();
766
+ }
767
+ var react_default = Squircle;
768
+ export {
769
+ Squircle,
770
+ SquircleNoScript,
771
+ react_default as default,
772
+ initGlobalSquircleObserver
773
+ };