@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.
package/dist/core.mjs ADDED
@@ -0,0 +1,599 @@
1
+ // src/core/squircle-path.ts
2
+ function toRadians(deg) {
3
+ return deg * Math.PI / 180;
4
+ }
5
+ function rounded(strings, ...values) {
6
+ return strings.reduce((acc, str, i) => {
7
+ const value = values[i];
8
+ return typeof value === "number" ? acc + str + value.toFixed(4) : acc + str + (value ?? "");
9
+ }, "");
10
+ }
11
+ function getPathParamsForCorner({
12
+ cornerRadius,
13
+ cornerSmoothing,
14
+ preserveSmoothing,
15
+ roundingAndSmoothingBudget
16
+ }) {
17
+ if (cornerRadius <= 0 || roundingAndSmoothingBudget <= 0) {
18
+ return { a: 0, b: 0, c: 0, d: 0, p: 0, cornerRadius: 0, arcSectionLength: 0 };
19
+ }
20
+ let p = (1 + cornerSmoothing) * cornerRadius;
21
+ if (!preserveSmoothing) {
22
+ const maxCornerSmoothing = roundingAndSmoothingBudget / cornerRadius - 1;
23
+ cornerSmoothing = Math.min(cornerSmoothing, maxCornerSmoothing);
24
+ p = Math.min(p, roundingAndSmoothingBudget);
25
+ }
26
+ const arcMeasure = 90 * (1 - cornerSmoothing);
27
+ const arcSectionLength = Math.sin(toRadians(arcMeasure / 2)) * cornerRadius * Math.sqrt(2);
28
+ const angleAlpha = (90 - arcMeasure) / 2;
29
+ const p3ToP4Distance = cornerRadius * Math.tan(toRadians(angleAlpha / 2));
30
+ const angleBeta = 45 * cornerSmoothing;
31
+ const c = p3ToP4Distance * Math.cos(toRadians(angleBeta));
32
+ const d = c * Math.tan(toRadians(angleBeta));
33
+ let b = (p - arcSectionLength - c - d) / 3;
34
+ let a = 2 * b;
35
+ if (preserveSmoothing && p > roundingAndSmoothingBudget) {
36
+ const p1ToP3MaxDistance = roundingAndSmoothingBudget - d - arcSectionLength - c;
37
+ const minA = p1ToP3MaxDistance / 6;
38
+ const maxB = p1ToP3MaxDistance - minA;
39
+ b = Math.min(b, maxB);
40
+ a = p1ToP3MaxDistance - b;
41
+ p = Math.min(p, roundingAndSmoothingBudget);
42
+ }
43
+ return { a, b, c, d, p, arcSectionLength, cornerRadius };
44
+ }
45
+ function drawTopRightPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
46
+ if (cornerRadius) {
47
+ 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}`;
48
+ }
49
+ return rounded`l ${p} 0`;
50
+ }
51
+ function drawBottomRightPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
52
+ if (cornerRadius) {
53
+ 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}`;
54
+ }
55
+ return rounded`l 0 ${p}`;
56
+ }
57
+ function drawBottomLeftPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
58
+ if (cornerRadius) {
59
+ 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)}`;
60
+ }
61
+ return rounded`l ${-p} 0`;
62
+ }
63
+ function drawTopLeftPath({ cornerRadius, a, b, c, d, p, arcSectionLength }) {
64
+ if (cornerRadius) {
65
+ 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}`;
66
+ }
67
+ return rounded`l 0 ${-p}`;
68
+ }
69
+ function getSVGPathFromPathParams({
70
+ width,
71
+ height,
72
+ topLeftPathParams,
73
+ topRightPathParams,
74
+ bottomRightPathParams,
75
+ bottomLeftPathParams
76
+ }) {
77
+ return `
78
+ M ${width - topRightPathParams.p} 0
79
+ ${drawTopRightPath(topRightPathParams)}
80
+ L ${width} ${height - bottomRightPathParams.p}
81
+ ${drawBottomRightPath(bottomRightPathParams)}
82
+ L ${bottomLeftPathParams.p} ${height}
83
+ ${drawBottomLeftPath(bottomLeftPathParams)}
84
+ L 0 ${topLeftPathParams.p}
85
+ ${drawTopLeftPath(topLeftPathParams)}
86
+ Z
87
+ `.replace(/[\t\s\n]+/g, " ").trim();
88
+ }
89
+ var adjacentsByCorner = {
90
+ topLeft: [
91
+ { corner: "topRight", side: "top" },
92
+ { corner: "bottomLeft", side: "left" }
93
+ ],
94
+ topRight: [
95
+ { corner: "topLeft", side: "top" },
96
+ { corner: "bottomRight", side: "right" }
97
+ ],
98
+ bottomLeft: [
99
+ { corner: "bottomRight", side: "bottom" },
100
+ { corner: "topLeft", side: "left" }
101
+ ],
102
+ bottomRight: [
103
+ { corner: "bottomLeft", side: "bottom" },
104
+ { corner: "topRight", side: "right" }
105
+ ]
106
+ };
107
+ function distributeAndNormalize({
108
+ topLeftCornerRadius,
109
+ topRightCornerRadius,
110
+ bottomRightCornerRadius,
111
+ bottomLeftCornerRadius,
112
+ width,
113
+ height
114
+ }) {
115
+ const roundingAndSmoothingBudgetMap = {
116
+ topLeft: -1,
117
+ topRight: -1,
118
+ bottomLeft: -1,
119
+ bottomRight: -1
120
+ };
121
+ const cornerRadiusMap = {
122
+ topLeft: topLeftCornerRadius,
123
+ topRight: topRightCornerRadius,
124
+ bottomLeft: bottomLeftCornerRadius,
125
+ bottomRight: bottomRightCornerRadius
126
+ };
127
+ Object.entries(cornerRadiusMap).sort(([, r1], [, r2]) => r2 - r1).forEach(([corner, radius]) => {
128
+ const adjacents = adjacentsByCorner[corner];
129
+ const budget = Math.min(
130
+ ...adjacents.map((adj) => {
131
+ const adjRadius = cornerRadiusMap[adj.corner];
132
+ if (radius === 0 && adjRadius === 0) return 0;
133
+ const adjBudget = roundingAndSmoothingBudgetMap[adj.corner];
134
+ const sideLen = adj.side === "top" || adj.side === "bottom" ? width : height;
135
+ return adjBudget >= 0 ? sideLen - roundingAndSmoothingBudgetMap[adj.corner] : radius / (radius + adjRadius) * sideLen;
136
+ })
137
+ );
138
+ roundingAndSmoothingBudgetMap[corner] = budget;
139
+ cornerRadiusMap[corner] = Math.min(radius, budget);
140
+ });
141
+ return {
142
+ topLeft: { radius: cornerRadiusMap.topLeft, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.topLeft },
143
+ topRight: { radius: cornerRadiusMap.topRight, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.topRight },
144
+ bottomLeft: { radius: cornerRadiusMap.bottomLeft, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.bottomLeft },
145
+ bottomRight: { radius: cornerRadiusMap.bottomRight, roundingAndSmoothingBudget: roundingAndSmoothingBudgetMap.bottomRight }
146
+ };
147
+ }
148
+ function drawPureBezierTopRight(r) {
149
+ if (r <= 0) return "";
150
+ 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}`;
151
+ }
152
+ function drawPureBezierBottomRight(r) {
153
+ if (r <= 0) return "";
154
+ 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}`;
155
+ }
156
+ function drawPureBezierBottomLeft(r) {
157
+ if (r <= 0) return "";
158
+ 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}`;
159
+ }
160
+ function drawPureBezierTopLeft(r) {
161
+ if (r <= 0) return "";
162
+ 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}`;
163
+ }
164
+ function getPureBezierSVGPath(width, height, tl, tr, br, bl) {
165
+ return `
166
+ M ${width - tr} 0
167
+ ${drawPureBezierTopRight(tr)}
168
+ L ${width} ${height - br}
169
+ ${drawPureBezierBottomRight(br)}
170
+ L ${bl} ${height}
171
+ ${drawPureBezierBottomLeft(bl)}
172
+ L 0 ${tl}
173
+ ${drawPureBezierTopLeft(tl)}
174
+ Z
175
+ `.replace(/[\t\s\n]+/g, " ").trim();
176
+ }
177
+ function getSvgPath({
178
+ cornerRadius = 0,
179
+ topLeftCornerRadius,
180
+ topRightCornerRadius,
181
+ bottomRightCornerRadius,
182
+ bottomLeftCornerRadius,
183
+ cornerSmoothing = 1,
184
+ width,
185
+ height,
186
+ preserveSmoothing = false,
187
+ g2Continuous = false
188
+ }) {
189
+ const tl = topLeftCornerRadius ?? cornerRadius;
190
+ const tr = topRightCornerRadius ?? cornerRadius;
191
+ const bl = bottomLeftCornerRadius ?? cornerRadius;
192
+ const br = bottomRightCornerRadius ?? cornerRadius;
193
+ if (g2Continuous) {
194
+ const { topLeft: topLeft2, topRight: topRight2, bottomLeft: bottomLeft2, bottomRight: bottomRight2 } = distributeAndNormalize({
195
+ topLeftCornerRadius: tl,
196
+ topRightCornerRadius: tr,
197
+ bottomRightCornerRadius: br,
198
+ bottomLeftCornerRadius: bl,
199
+ width,
200
+ height
201
+ });
202
+ return getPureBezierSVGPath(
203
+ width,
204
+ height,
205
+ topLeft2.radius,
206
+ topRight2.radius,
207
+ bottomRight2.radius,
208
+ bottomLeft2.radius
209
+ );
210
+ }
211
+ if (tl === tr && tr === br && br === bl) {
212
+ const roundingAndSmoothingBudget = Math.min(width, height) / 2;
213
+ const clampedRadius = Math.min(tl, roundingAndSmoothingBudget);
214
+ const params = getPathParamsForCorner({
215
+ cornerRadius: clampedRadius,
216
+ cornerSmoothing,
217
+ preserveSmoothing,
218
+ roundingAndSmoothingBudget
219
+ });
220
+ return getSVGPathFromPathParams({
221
+ width,
222
+ height,
223
+ topLeftPathParams: params,
224
+ topRightPathParams: params,
225
+ bottomLeftPathParams: params,
226
+ bottomRightPathParams: params
227
+ });
228
+ }
229
+ const { topLeft, topRight, bottomLeft, bottomRight } = distributeAndNormalize({
230
+ topLeftCornerRadius: tl,
231
+ topRightCornerRadius: tr,
232
+ bottomRightCornerRadius: br,
233
+ bottomLeftCornerRadius: bl,
234
+ width,
235
+ height
236
+ });
237
+ return getSVGPathFromPathParams({
238
+ width,
239
+ height,
240
+ topLeftPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: topLeft.radius, roundingAndSmoothingBudget: topLeft.roundingAndSmoothingBudget }),
241
+ topRightPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: topRight.radius, roundingAndSmoothingBudget: topRight.roundingAndSmoothingBudget }),
242
+ bottomRightPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: bottomRight.radius, roundingAndSmoothingBudget: bottomRight.roundingAndSmoothingBudget }),
243
+ bottomLeftPathParams: getPathParamsForCorner({ cornerSmoothing, preserveSmoothing, cornerRadius: bottomLeft.radius, roundingAndSmoothingBudget: bottomLeft.roundingAndSmoothingBudget })
244
+ });
245
+ }
246
+ function computeSquirclePath(width, height, cornerRadius, cornerSmoothing = 1, preserveSmoothing = false, g2Continuous = false) {
247
+ if (width <= 0 || height <= 0 || cornerRadius <= 0) return "";
248
+ return getSvgPath({ width, height, cornerRadius, cornerSmoothing, preserveSmoothing, g2Continuous });
249
+ }
250
+ function drawSquircleRect(ctx, x, y, width, height, cornerRadius, smoothing = 1) {
251
+ let tlR = 0, trR = 0, brR = 0, blR = 0;
252
+ if (typeof cornerRadius === "number") {
253
+ tlR = trR = brR = blR = cornerRadius;
254
+ } else if (cornerRadius) {
255
+ tlR = cornerRadius.tl ?? 0;
256
+ trR = cornerRadius.tr ?? 0;
257
+ brR = cornerRadius.br ?? 0;
258
+ blR = cornerRadius.bl ?? 0;
259
+ }
260
+ const { topLeft, topRight, bottomLeft, bottomRight } = distributeAndNormalize({
261
+ topLeftCornerRadius: tlR,
262
+ topRightCornerRadius: trR,
263
+ bottomRightCornerRadius: brR,
264
+ bottomLeftCornerRadius: blR,
265
+ width,
266
+ height
267
+ });
268
+ const rTL = topLeft.radius;
269
+ const rTR = topRight.radius;
270
+ const rBR = bottomRight.radius;
271
+ const rBL = bottomLeft.radius;
272
+ const left = x;
273
+ const top = y;
274
+ const right = x + width;
275
+ const bottom = y + height;
276
+ ctx.moveTo(left + rTL, top);
277
+ ctx.lineTo(right - rTR, top);
278
+ if (rTR > 0) {
279
+ ctx.bezierCurveTo(
280
+ right - rTR + 0.3 * rTR,
281
+ top,
282
+ right - rTR + 0.473 * rTR,
283
+ top,
284
+ right - rTR + 0.619 * rTR,
285
+ top + 0.039 * rTR
286
+ );
287
+ ctx.bezierCurveTo(
288
+ right - rTR + 0.804 * rTR,
289
+ top + 0.088 * rTR,
290
+ right - rTR + 0.961 * rTR,
291
+ top + 0.196 * rTR,
292
+ right,
293
+ top + rTR - 0.619 * rTR
294
+ );
295
+ ctx.bezierCurveTo(
296
+ right,
297
+ top + rTR - 0.319 * rTR,
298
+ right,
299
+ top + rTR - 0.146 * rTR,
300
+ right,
301
+ top + rTR
302
+ );
303
+ }
304
+ ctx.lineTo(right, bottom - rBR);
305
+ if (rBR > 0) {
306
+ ctx.bezierCurveTo(
307
+ right,
308
+ bottom - rBR + 0.3 * rBR,
309
+ right,
310
+ bottom - rBR + 0.473 * rBR,
311
+ right - 0.039 * rBR,
312
+ bottom - rBR + 0.619 * rBR
313
+ );
314
+ ctx.bezierCurveTo(
315
+ right - 0.088 * rBR,
316
+ bottom - rBR + 0.804 * rBR,
317
+ right - 0.196 * rBR,
318
+ bottom - rBR + 0.961 * rBR,
319
+ right - rBR + 0.619 * rBR,
320
+ bottom
321
+ );
322
+ ctx.bezierCurveTo(
323
+ right - rBR + 0.319 * rBR,
324
+ bottom,
325
+ right - rBR + 0.146 * rBR,
326
+ bottom,
327
+ right - rBR,
328
+ bottom
329
+ );
330
+ }
331
+ ctx.lineTo(left + rBL, bottom);
332
+ if (rBL > 0) {
333
+ ctx.bezierCurveTo(
334
+ left + rBL - 0.3 * rBL,
335
+ bottom,
336
+ left + rBL - 0.473 * rBL,
337
+ bottom,
338
+ left + rBL - 0.619 * rBL,
339
+ bottom - 0.039 * rBL
340
+ );
341
+ ctx.bezierCurveTo(
342
+ left + rBL - 0.804 * rBL,
343
+ bottom - 0.088 * rBL,
344
+ left + rBL - 0.961 * rBL,
345
+ bottom - 0.196 * rBL,
346
+ left,
347
+ bottom - rBL + 0.619 * rBL
348
+ );
349
+ ctx.bezierCurveTo(
350
+ left,
351
+ bottom - rBL + 0.319 * rBL,
352
+ left,
353
+ bottom - rBL + 0.146 * rBL,
354
+ left,
355
+ bottom - rBL
356
+ );
357
+ }
358
+ ctx.lineTo(left, top + rTL);
359
+ if (rTL > 0) {
360
+ ctx.bezierCurveTo(
361
+ left,
362
+ top + rTL - 0.3 * rTL,
363
+ left,
364
+ top + rTL - 0.473 * rTL,
365
+ left + 0.039 * rTL,
366
+ top + rTL - 0.619 * rTL
367
+ );
368
+ ctx.bezierCurveTo(
369
+ left + 0.088 * rTL,
370
+ top + rTL - 0.804 * rTL,
371
+ left + 0.196 * rTL,
372
+ top + rTL - 0.961 * rTL,
373
+ left + rTL - 0.619 * rTL,
374
+ top
375
+ );
376
+ ctx.bezierCurveTo(
377
+ left + rTL - 0.319 * rTL,
378
+ top,
379
+ left + rTL - 0.146 * rTL,
380
+ top,
381
+ left + rTL,
382
+ top
383
+ );
384
+ }
385
+ ctx.closePath();
386
+ }
387
+
388
+ // src/core/class-parser.ts
389
+ var ROUNDED_RE = /^rounded-squircle-(\d+)$/;
390
+ var SMOOTHING_RE = /^corner-smoothing-(\d{1,3})$/;
391
+ var SQUIRCLE_BORDER_WIDTH_RE = /^squircle-border-(\d+(\.\d+)?)$/;
392
+ var TW_BORDER_COLOR_RE = /^border-(\[.+\]|[\w-]+)(\/(\d+))?$/;
393
+ var TW_BORDER_WIDTH_RE = /^border-(\d+)$/;
394
+ var TW_BORDER_BASE_RE = /^border$/;
395
+ var BORDER_COLORS = {
396
+ "gray-50": "#f9fafb",
397
+ "gray-100": "#f3f4f6",
398
+ "gray-200": "#e5e7eb",
399
+ "gray-300": "#d1d5db",
400
+ "gray-400": "#9ca3af",
401
+ "gray-500": "#6b7280",
402
+ "gray-600": "#4b5563",
403
+ "gray-700": "#374151",
404
+ "gray-800": "#1f2937",
405
+ "gray-900": "#111827",
406
+ "slate-50": "#f8fafc",
407
+ "slate-100": "#f1f5f9",
408
+ "slate-200": "#e2e8f0",
409
+ "slate-300": "#cbd5e1",
410
+ "slate-400": "#94a3b8",
411
+ "slate-500": "#64748b",
412
+ "slate-600": "#475569",
413
+ "slate-700": "#334155",
414
+ "slate-800": "#1e293b",
415
+ "slate-900": "#0f172a",
416
+ "red-50": "#fef2f2",
417
+ "red-100": "#fee2e2",
418
+ "red-200": "#fecaca",
419
+ "red-300": "#fca5a5",
420
+ "red-400": "#f87171",
421
+ "red-500": "#ef4444",
422
+ "red-600": "#dc2626",
423
+ "red-700": "#b91c1c",
424
+ "red-800": "#991b1b",
425
+ "red-900": "#7f1d1d",
426
+ "blue-50": "#eff6ff",
427
+ "blue-100": "#dbeafe",
428
+ "blue-200": "#bfdbfe",
429
+ "blue-300": "#93c5fd",
430
+ "blue-400": "#60a5fa",
431
+ "blue-500": "#3b82f6",
432
+ "blue-600": "#2563eb",
433
+ "blue-700": "#1d4ed8",
434
+ "blue-800": "#1e40af",
435
+ "blue-900": "#1e3a8a",
436
+ "green-50": "#f0fdf4",
437
+ "green-100": "#dcfce7",
438
+ "green-200": "#bbf7d0",
439
+ "green-300": "#86efac",
440
+ "green-400": "#4ade80",
441
+ "green-500": "#22c55e",
442
+ "green-600": "#16a34a",
443
+ "green-700": "#15803d",
444
+ "green-800": "#166534",
445
+ "green-900": "#14532d",
446
+ "yellow-50": "#fefce8",
447
+ "yellow-100": "#fef9c3",
448
+ "yellow-200": "#fef08a",
449
+ "yellow-300": "#fde047",
450
+ "yellow-400": "#facc15",
451
+ "yellow-500": "#eab308",
452
+ "yellow-600": "#ca8a04",
453
+ "yellow-700": "#a16207",
454
+ "yellow-800": "#854d0e",
455
+ "yellow-900": "#713f12",
456
+ "purple-50": "#faf5ff",
457
+ "purple-100": "#f3e8ff",
458
+ "purple-200": "#e9d5ff",
459
+ "purple-300": "#d8b4fe",
460
+ "purple-400": "#c084fc",
461
+ "purple-500": "#a855f7",
462
+ "purple-600": "#9333ea",
463
+ "purple-700": "#7e22ce",
464
+ "purple-800": "#6b21a8",
465
+ "purple-900": "#581c87",
466
+ "pink-50": "#fdf2f8",
467
+ "pink-100": "#fce7f3",
468
+ "pink-200": "#fbcfe8",
469
+ "pink-300": "#f9a8d4",
470
+ "pink-400": "#f472b6",
471
+ "pink-500": "#ec4899",
472
+ "pink-600": "#db2777",
473
+ "pink-700": "#be185d",
474
+ "pink-800": "#9d174d",
475
+ "pink-900": "#831843",
476
+ "orange-50": "#fff7ed",
477
+ "orange-100": "#ffedd5",
478
+ "orange-200": "#fed7aa",
479
+ "orange-300": "#fdba74",
480
+ "orange-400": "#fb923c",
481
+ "orange-500": "#f97316",
482
+ "orange-600": "#ea580c",
483
+ "orange-700": "#c2410c",
484
+ "orange-800": "#9a3412",
485
+ "orange-900": "#7c2d12",
486
+ "teal-50": "#f0fdfa",
487
+ "teal-100": "#ccfbf1",
488
+ "teal-200": "#99f6e4",
489
+ "teal-300": "#5eead4",
490
+ "teal-400": "#2dd4bf",
491
+ "teal-500": "#14b8a6",
492
+ "teal-600": "#0d9488",
493
+ "teal-700": "#0f766e",
494
+ "teal-800": "#115e59",
495
+ "teal-900": "#134e4a",
496
+ white: "#ffffff",
497
+ black: "#000000",
498
+ transparent: "transparent"
499
+ };
500
+ function hexToRgba(hex, opacity) {
501
+ const h = hex.replace(/#/g, "");
502
+ const r = parseInt(h.substring(0, 2), 16);
503
+ const g = parseInt(h.substring(2, 4), 16);
504
+ const b = parseInt(h.substring(4, 6), 16);
505
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
506
+ }
507
+ function resolveColorToken(token, opacity) {
508
+ if (token.startsWith("[") && token.endsWith("]")) {
509
+ const raw = token.slice(1, -1);
510
+ if (opacity !== void 0 && raw.startsWith("#")) {
511
+ return hexToRgba(raw, opacity);
512
+ }
513
+ return raw;
514
+ }
515
+ const hex = BORDER_COLORS[token];
516
+ if (!hex) return null;
517
+ if (opacity !== void 0) return hexToRgba(hex, opacity);
518
+ return hex;
519
+ }
520
+ function parseSquircleClasses(className = "") {
521
+ const classes = className.split(/\s+/).filter(Boolean);
522
+ let cornerRadius = null;
523
+ let cornerSmoothing = 1;
524
+ let squircleBorder = false;
525
+ let borderColor = null;
526
+ let borderWidth = null;
527
+ let g2Continuous = false;
528
+ const rest = [];
529
+ for (const cls of classes) {
530
+ let matched = false;
531
+ let m = cls.match(ROUNDED_RE);
532
+ if (m) {
533
+ cornerRadius = Number(m[1]);
534
+ matched = true;
535
+ }
536
+ if (!matched) {
537
+ m = cls.match(SMOOTHING_RE);
538
+ if (m) {
539
+ cornerSmoothing = Math.min(100, Math.max(0, Number(m[1]))) / 100;
540
+ matched = true;
541
+ }
542
+ }
543
+ if (!matched && cls === "squircle-border") {
544
+ squircleBorder = true;
545
+ matched = true;
546
+ }
547
+ if (!matched && cls === "squircle-g2") {
548
+ g2Continuous = true;
549
+ matched = true;
550
+ }
551
+ if (!matched) {
552
+ m = cls.match(SQUIRCLE_BORDER_WIDTH_RE);
553
+ if (m) {
554
+ squircleBorder = true;
555
+ borderWidth = Number(m[1]);
556
+ matched = true;
557
+ }
558
+ }
559
+ if (!matched && squircleBorder) {
560
+ m = cls.match(TW_BORDER_WIDTH_RE);
561
+ if (m) {
562
+ borderWidth = Number(m[1]);
563
+ matched = true;
564
+ }
565
+ if (!matched && cls.match(TW_BORDER_BASE_RE)) {
566
+ borderWidth = 1;
567
+ matched = true;
568
+ }
569
+ if (!matched) {
570
+ m = cls.match(TW_BORDER_COLOR_RE);
571
+ if (m) {
572
+ const colorToken = m[1];
573
+ const opacityPercent = m[3] ? Number(m[3]) / 100 : void 0;
574
+ const resolved = resolveColorToken(colorToken, opacityPercent);
575
+ if (resolved) {
576
+ borderColor = resolved;
577
+ matched = true;
578
+ }
579
+ }
580
+ }
581
+ }
582
+ if (!matched) rest.push(cls);
583
+ }
584
+ return {
585
+ cornerRadius,
586
+ cornerSmoothing,
587
+ squircleBorder,
588
+ borderColor,
589
+ borderWidth: borderWidth ?? 1,
590
+ g2Continuous,
591
+ restClasses: rest.join(" ")
592
+ };
593
+ }
594
+ export {
595
+ computeSquirclePath,
596
+ drawSquircleRect,
597
+ getSvgPath,
598
+ parseSquircleClasses
599
+ };
@@ -0,0 +1,21 @@
1
+ import React, { ReactNode, ComponentType } from 'react';
2
+
3
+ interface SquircleProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children?: ReactNode;
5
+ cornerRadius?: number;
6
+ cornerSmoothing?: number;
7
+ preserveSmoothing?: boolean;
8
+ as?: ComponentType | string;
9
+ g2Continuous?: boolean;
10
+ }
11
+ /**
12
+ * Drop-in squircle wrapper. Uses Figma's algorithm (2 Bézier curves + 1 arc per corner).
13
+ */
14
+ declare function Squircle(props: SquircleProps): React.JSX.Element;
15
+ /**
16
+ * Noscript fallback — place once in root layout.
17
+ */
18
+ declare function SquircleNoScript(): React.JSX.Element;
19
+ declare function initGlobalSquircleObserver(): void;
20
+
21
+ export { Squircle, SquircleNoScript, type SquircleProps, Squircle as default, initGlobalSquircleObserver };
@@ -0,0 +1,21 @@
1
+ import React, { ReactNode, ComponentType } from 'react';
2
+
3
+ interface SquircleProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children?: ReactNode;
5
+ cornerRadius?: number;
6
+ cornerSmoothing?: number;
7
+ preserveSmoothing?: boolean;
8
+ as?: ComponentType | string;
9
+ g2Continuous?: boolean;
10
+ }
11
+ /**
12
+ * Drop-in squircle wrapper. Uses Figma's algorithm (2 Bézier curves + 1 arc per corner).
13
+ */
14
+ declare function Squircle(props: SquircleProps): React.JSX.Element;
15
+ /**
16
+ * Noscript fallback — place once in root layout.
17
+ */
18
+ declare function SquircleNoScript(): React.JSX.Element;
19
+ declare function initGlobalSquircleObserver(): void;
20
+
21
+ export { Squircle, SquircleNoScript, type SquircleProps, Squircle as default, initGlobalSquircleObserver };