@opensite/ui 0.1.0 → 0.1.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/dist/{button-variants-CdXVl4bn.d.cts → button-variants-CbpzhUdJ.d.cts} +0 -6
- package/dist/{button-variants-CdXVl4bn.d.ts → button-variants-CbpzhUdJ.d.ts} +0 -6
- package/dist/button.cjs +139 -25
- package/dist/button.cjs.map +1 -1
- package/dist/button.d.cts +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.js +139 -25
- package/dist/button.js.map +1 -1
- package/dist/components.cjs +139 -25
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/components.js +139 -25
- package/dist/components.js.map +1 -1
- package/dist/feature-showcase.cjs +139 -25
- package/dist/feature-showcase.cjs.map +1 -1
- package/dist/feature-showcase.js +139 -25
- package/dist/feature-showcase.js.map +1 -1
- package/dist/index.cjs +139 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +139 -25
- package/dist/index.js.map +1 -1
- package/dist/pressable.cjs +139 -25
- package/dist/pressable.cjs.map +1 -1
- package/dist/pressable.d.cts +1 -1
- package/dist/pressable.d.ts +1 -1
- package/dist/pressable.js +139 -25
- package/dist/pressable.js.map +1 -1
- package/dist/registry.cjs +139 -25
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +139 -25
- package/dist/registry.js.map +1 -1
- package/package.json +1 -1
package/dist/pressable.cjs
CHANGED
|
@@ -208,34 +208,148 @@ function useNavigation({
|
|
|
208
208
|
handleClick
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
|
-
var
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
211
|
+
var baseStyles = [
|
|
212
|
+
// Layout
|
|
213
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
|
|
214
|
+
// Typography - using CSS variables with sensible defaults
|
|
215
|
+
"font-[var(--button-font-family,inherit)]",
|
|
216
|
+
"font-[var(--button-font-weight,500)]",
|
|
217
|
+
"tracking-[var(--button-letter-spacing,0)]",
|
|
218
|
+
"leading-[var(--button-line-height,1.25)]",
|
|
219
|
+
"[text-transform:var(--button-text-transform,none)]",
|
|
220
|
+
"text-sm",
|
|
221
|
+
// Border radius
|
|
222
|
+
"rounded-[var(--button-radius,var(--radius,0.375rem))]",
|
|
223
|
+
// Smooth transition - slower and smoother default
|
|
224
|
+
"transition-[var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
|
|
225
|
+
// Box shadow (master level)
|
|
226
|
+
"shadow-[var(--button-shadow,none)]",
|
|
227
|
+
"hover:shadow-[var(--button-shadow-hover,var(--button-shadow,none))]",
|
|
228
|
+
// Disabled state
|
|
229
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
230
|
+
// SVG handling
|
|
231
|
+
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
|
|
232
|
+
// Focus styles
|
|
233
|
+
"outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
234
|
+
// Invalid state
|
|
235
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
|
|
236
|
+
].join(" ");
|
|
237
|
+
var buttonVariants = classVarianceAuthority.cva(baseStyles, {
|
|
238
|
+
variants: {
|
|
239
|
+
variant: {
|
|
240
|
+
// Default (Primary) variant - full customization
|
|
241
|
+
default: [
|
|
242
|
+
"bg-[var(--button-default-bg,hsl(var(--primary)))]",
|
|
243
|
+
"text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
|
|
244
|
+
"border-[length:var(--button-default-border-width,0px)]",
|
|
245
|
+
"border-[color:var(--button-default-border,transparent)]",
|
|
246
|
+
"shadow-[var(--button-default-shadow,var(--button-shadow,none))]",
|
|
247
|
+
"hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
|
|
248
|
+
"hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
|
|
249
|
+
"hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
|
|
250
|
+
"hover:shadow-[var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
|
|
251
|
+
].join(" "),
|
|
252
|
+
// Destructive variant - full customization
|
|
253
|
+
destructive: [
|
|
254
|
+
"bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
|
|
255
|
+
"text-[var(--button-destructive-fg,white)]",
|
|
256
|
+
"border-[length:var(--button-destructive-border-width,0px)]",
|
|
257
|
+
"border-[color:var(--button-destructive-border,transparent)]",
|
|
258
|
+
"shadow-[var(--button-destructive-shadow,var(--button-shadow,none))]",
|
|
259
|
+
"hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
|
|
260
|
+
"hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
|
|
261
|
+
"hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
|
|
262
|
+
"hover:shadow-[var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
|
|
263
|
+
"focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
|
|
264
|
+
"dark:bg-destructive/60"
|
|
265
|
+
].join(" "),
|
|
266
|
+
// Outline variant - full customization with proper border handling
|
|
267
|
+
outline: [
|
|
268
|
+
"bg-[var(--button-outline-bg,hsl(var(--background)))]",
|
|
269
|
+
"text-[var(--button-outline-fg,inherit)]",
|
|
270
|
+
"border-[length:var(--button-outline-border-width,1px)]",
|
|
271
|
+
"border-[color:var(--button-outline-border,hsl(var(--border)))]",
|
|
272
|
+
"shadow-[var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
|
|
273
|
+
"hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
|
|
274
|
+
"hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
|
|
275
|
+
"hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
|
|
276
|
+
"hover:shadow-[var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
|
|
277
|
+
"dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
|
|
278
|
+
].join(" "),
|
|
279
|
+
// Secondary variant - full customization
|
|
280
|
+
secondary: [
|
|
281
|
+
"bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
|
|
282
|
+
"text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
|
|
283
|
+
"border-[length:var(--button-secondary-border-width,0px)]",
|
|
284
|
+
"border-[color:var(--button-secondary-border,transparent)]",
|
|
285
|
+
"shadow-[var(--button-secondary-shadow,var(--button-shadow,none))]",
|
|
286
|
+
"hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
|
|
287
|
+
"hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
|
|
288
|
+
"hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
|
|
289
|
+
"hover:shadow-[var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
|
|
290
|
+
].join(" "),
|
|
291
|
+
// Ghost variant - full customization
|
|
292
|
+
ghost: [
|
|
293
|
+
"bg-[var(--button-ghost-bg,transparent)]",
|
|
294
|
+
"text-[var(--button-ghost-fg,inherit)]",
|
|
295
|
+
"border-[length:var(--button-ghost-border-width,0px)]",
|
|
296
|
+
"border-[color:var(--button-ghost-border,transparent)]",
|
|
297
|
+
"shadow-[var(--button-ghost-shadow,var(--button-shadow,none))]",
|
|
298
|
+
"hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
|
|
299
|
+
"hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
|
|
300
|
+
"hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
|
|
301
|
+
"hover:shadow-[var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
|
|
302
|
+
"dark:hover:bg-accent/50"
|
|
303
|
+
].join(" "),
|
|
304
|
+
// Link variant - full customization
|
|
305
|
+
link: [
|
|
306
|
+
"bg-[var(--button-link-bg,transparent)]",
|
|
307
|
+
"text-[var(--button-link-fg,hsl(var(--primary)))]",
|
|
308
|
+
"border-[length:var(--button-link-border-width,0px)]",
|
|
309
|
+
"border-[color:var(--button-link-border,transparent)]",
|
|
310
|
+
"shadow-[var(--button-link-shadow,none)]",
|
|
311
|
+
"hover:bg-[var(--button-link-hover-bg,transparent)]",
|
|
312
|
+
"hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
|
|
313
|
+
"hover:shadow-[var(--button-link-shadow-hover,none)]",
|
|
314
|
+
"underline-offset-4 hover:underline"
|
|
315
|
+
].join(" ")
|
|
232
316
|
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
317
|
+
size: {
|
|
318
|
+
default: [
|
|
319
|
+
"h-[var(--button-height-md,2.25rem)]",
|
|
320
|
+
"px-[var(--button-padding-x-md,1rem)]",
|
|
321
|
+
"py-[var(--button-padding-y-md,0.5rem)]",
|
|
322
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
|
|
323
|
+
].join(" "),
|
|
324
|
+
sm: [
|
|
325
|
+
"h-[var(--button-height-sm,2rem)]",
|
|
326
|
+
"px-[var(--button-padding-x-sm,0.75rem)]",
|
|
327
|
+
"py-[var(--button-padding-y-sm,0.25rem)]",
|
|
328
|
+
"gap-1.5",
|
|
329
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
|
|
330
|
+
].join(" "),
|
|
331
|
+
md: [
|
|
332
|
+
"h-[var(--button-height-md,2.25rem)]",
|
|
333
|
+
"px-[var(--button-padding-x-md,1rem)]",
|
|
334
|
+
"py-[var(--button-padding-y-md,0.5rem)]",
|
|
335
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
|
|
336
|
+
].join(" "),
|
|
337
|
+
lg: [
|
|
338
|
+
"h-[var(--button-height-lg,2.5rem)]",
|
|
339
|
+
"px-[var(--button-padding-x-lg,1.5rem)]",
|
|
340
|
+
"py-[var(--button-padding-y-lg,0.5rem)]",
|
|
341
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
|
|
342
|
+
].join(" "),
|
|
343
|
+
icon: "size-[var(--button-height-md,2.25rem)]",
|
|
344
|
+
"icon-sm": "size-[var(--button-height-sm,2rem)]",
|
|
345
|
+
"icon-lg": "size-[var(--button-height-lg,2.5rem)]"
|
|
236
346
|
}
|
|
347
|
+
},
|
|
348
|
+
defaultVariants: {
|
|
349
|
+
variant: "default",
|
|
350
|
+
size: "default"
|
|
237
351
|
}
|
|
238
|
-
);
|
|
352
|
+
});
|
|
239
353
|
var Pressable = React__namespace.forwardRef(
|
|
240
354
|
({
|
|
241
355
|
children,
|
package/dist/pressable.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/utils.ts","../lib/useNavigation.ts","../lib/button-variants.ts","../lib/Pressable.tsx"],"names":["twMerge","clsx","React","cva","React2","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACyBA,SAAS,qBAAqB,KAAA,EAAuB;AACnD,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAG3B,EAAA,IAAI,OAAA,CAAQ,WAAA,EAAY,CAAE,UAAA,CAAW,MAAM,CAAA,EAAG;AAC5C,IAAA,OAAO,OAAA;AAAA,EACT;AAGA,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,gFAAgF,CAAA;AAE5G,EAAA,IAAI,KAAA,EAAO;AAET,IAAA,MAAM,aAAa,KAAA,CAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,iBAAiB,EAAE,CAAA;AACvD,IAAA,MAAM,SAAA,GAAY,MAAM,CAAC,CAAA;AAGzB,IAAA,MAAM,UAAA,GAAa,UAAA,CAAW,MAAA,IAAU,EAAA,IAAM,CAAC,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,GACjE,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,GACd,UAAA;AAGJ,IAAA,MAAM,gBAAgB,SAAA,GAAY,CAAA,EAAG,UAAU,CAAA,KAAA,EAAQ,SAAS,CAAA,CAAA,GAAK,UAAA;AAErE,IAAA,OAAO,OAAO,aAAa,CAAA,CAAA;AAAA,EAC7B;AAGA,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,eAAA,EAAiB,EAAE,CAAA;AACnD,EAAA,OAAO,OAAO,OAAO,CAAA,CAAA;AACvB;AAKA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAG3B,EAAA,IAAI,OAAA,CAAQ,WAAA,EAAY,CAAE,UAAA,CAAW,SAAS,CAAA,EAAG;AAC/C,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,UAAU,OAAO,CAAA,CAAA;AAC1B;AAKA,SAAS,QAAQ,KAAA,EAAwB;AACvC,EAAA,MAAM,UAAA,GAAa,4BAAA;AACnB,EAAA,OAAO,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,IAAA,EAAM,CAAA;AACrC;AAKA,SAAS,cAAc,KAAA,EAAwB;AAC7C,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAG3B,EAAA,IAAI,OAAA,CAAQ,WAAA,EAAY,CAAE,UAAA,CAAW,MAAM,CAAA,EAAG;AAC5C,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,MAAM,UAAA,GAAa,2EAAA;AACnB,EAAA,OAAO,UAAA,CAAW,KAAK,OAAO,CAAA;AAChC;AASA,SAAS,cAAc,IAAA,EAAuB;AAC5C,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEjC,IAAA,OAAO,KAAK,UAAA,CAAW,GAAG,KAAK,CAAC,IAAA,CAAK,WAAW,IAAI,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAG1B,EAAA,IAAI,OAAA,CAAQ,WAAW,GAAG,CAAA,IAAK,CAAC,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA,EAAG;AACxD,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,SAAS,IAAI,CAAA;AACjD,IAAA,MAAM,aAAA,GAAgB,OAAO,QAAA,CAAS,MAAA;AAGtC,IAAA,MAAM,kBAAkB,CAAC,MAAA,KACvB,MAAA,CAAO,OAAA,CAAQ,0BAA0B,IAAI,CAAA;AAE/C,IAAA,OAAO,eAAA,CAAgB,GAAA,CAAI,MAAM,CAAA,KAAM,gBAAgB,aAAa,CAAA;AAAA,EACtE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAKA,SAAS,eAAe,IAAA,EAAsB;AAC5C,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAG1B,EAAA,IAAI,OAAA,CAAQ,WAAW,GAAG,CAAA,IAAK,CAAC,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA,EAAG;AACxD,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,SAAS,IAAI,CAAA;AACjD,IAAA,MAAM,aAAA,GAAgB,OAAO,QAAA,CAAS,MAAA;AAGtC,IAAA,MAAM,kBAAkB,CAAC,MAAA,KACvB,MAAA,CAAO,OAAA,CAAQ,0BAA0B,IAAI,CAAA;AAE/C,IAAA,IAAI,gBAAgB,GAAA,CAAI,MAAM,CAAA,KAAM,eAAA,CAAgB,aAAa,CAAA,EAAG;AAElE,MAAA,OAAO,GAAA,CAAI,QAAA,GAAW,GAAA,CAAI,MAAA,GAAS,GAAA,CAAI,IAAA;AAAA,IACzC;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,OAAA;AACT;AA+BO,SAAS,aAAA,CAAc;AAAA,EAC5B,IAAA;AAAA,EACA;AACF,CAAA,GAAuB,EAAC,EAAwB;AAC9C,EAAA,MAAM,QAAA,GAAiBC,yBAAQ,MAAgB;AAC7C,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,IAAA,OAAW,EAAA,EAAI;AAC/B,MAAA,OAAO,UAAU,MAAA,GAAS,MAAA;AAAA,IAC5B;AAEA,IAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAG1B,IAAA,IAAI,OAAA,CAAQ,aAAY,CAAE,UAAA,CAAW,SAAS,CAAA,IAAK,OAAA,CAAQ,OAAO,CAAA,EAAG;AACnE,MAAA,OAAO,QAAA;AAAA,IACT;AAGA,IAAA,IAAI,OAAA,CAAQ,aAAY,CAAE,UAAA,CAAW,MAAM,CAAA,IAAK,aAAA,CAAc,OAAO,CAAA,EAAG;AACtE,MAAA,OAAO,KAAA;AAAA,IACT;AAGA,IAAA,IAAI,aAAA,CAAc,OAAO,CAAA,EAAG;AAC1B,MAAA,OAAO,UAAA;AAAA,IACT;AAGA,IAAA,IAAI;AACF,MAAA,IAAI,GAAA,CAAI,SAAS,OAAO,MAAA,KAAW,cAAc,MAAA,CAAO,QAAA,CAAS,OAAO,kBAAkB,CAAA;AAC1F,MAAA,OAAO,UAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AAEN,MAAA,OAAO,UAAA;AAAA,IACT;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,OAAO,CAAC,CAAA;AAElB,EAAA,MAAM,cAAA,GAAuBA,yBAAQ,MAA0B;AAC7D,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,IAAA,OAAW,EAAA,EAAI;AAC/B,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAE1B,IAAA,QAAQ,QAAA;AAAU,MAChB,KAAK,KAAA;AACH,QAAA,OAAO,qBAAqB,OAAO,CAAA;AAAA,MACrC,KAAK,QAAA;AACH,QAAA,OAAO,eAAe,OAAO,CAAA;AAAA,MAC/B,KAAK,UAAA;AACH,QAAA,OAAO,eAAe,OAAO,CAAA;AAAA,MAC/B,KAAK,UAAA;AACH,QAAA,OAAO,OAAA;AAAA,MACT;AACE,QAAA,OAAO,OAAA;AAAA;AACX,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,QAAQ,CAAC,CAAA;AAEnB,EAAA,MAAM,MAAA,GAAeA,yBAAQ,MAAsC;AACjE,IAAA,QAAQ,QAAA;AAAU,MAChB,KAAK,UAAA;AACH,QAAA,OAAO,QAAA;AAAA,MACT,KAAK,UAAA;AACH,QAAA,OAAO,OAAA;AAAA,MACT,KAAK,QAAA;AAAA,MACL,KAAK,KAAA;AAEH,QAAA,OAAO,MAAA;AAAA,MACT;AACE,QAAA,OAAO,MAAA;AAAA;AACX,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,GAAA,GAAYA,yBAAQ,MAA0B;AAClD,IAAA,IAAI,aAAa,UAAA,EAAY;AAC3B,MAAA,OAAO,qBAAA;AAAA,IACT;AACA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,aAAa,QAAA,KAAa,UAAA;AAChC,EAAA,MAAM,aAAa,QAAA,KAAa,UAAA;AAChC,EAAA,MAAM,kBAAkB,UAAA,IAAc,OAAO,mBAAmB,QAAA,IAAY,cAAA,CAAe,WAAW,GAAG,CAAA;AAEzG,EAAA,MAAM,WAAA,GAAoBA,gBAAA,CAAA,WAAA;AAAA,IACxB,CAAC,KAAA,KAAU;AAET,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,QACf,SAAS,KAAA,EAAO;AACd,UAAA,OAAA,CAAQ,KAAA,CAAM,kCAAkC,KAAK,CAAA;AAAA,QACvD;AAAA,MACF;AAGA,MAAA,IAAI,MAAM,gBAAA,EAAkB;AAC1B,QAAA;AAAA,MACF;AAGA,MAAA,IACE,eAAA,IACA,cAAA,IACA,KAAA,CAAM,MAAA,KAAW,CAAA;AAAA,MACjB,CAAC,KAAA,CAAM,OAAA,IACP,CAAC,KAAA,CAAM,MAAA,IACP,CAAC,KAAA,CAAM,OAAA,IACP,CAAC,KAAA,CAAM,QAAA,EACP;AAEA,QAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,UAAA,MAAM,UAAW,MAAA,CAAe,2BAAA;AAChC,UAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AACjC,YAAA,IAAI;AACF,cAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,cAAA,EAAgB,KAAA,CAAM,eAAe,KAAK,CAAA;AAClE,cAAA,IAAI,YAAY,KAAA,EAAO;AACrB,gBAAA,KAAA,CAAM,cAAA,EAAe;AAAA,cACvB;AAAA,YACF,SAAS,KAAA,EAAO;AACd,cAAA,OAAA,CAAQ,KAAA,CAAM,gCAAgC,KAAK,CAAA;AAAA,YACrD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IACA,CAAC,OAAA,EAAS,eAAA,EAAiB,cAAc;AAAA,GAC3C;AAEA,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,cAAA;AAAA,IACA,MAAA;AAAA,IACA,GAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF;AACF;ACxUO,IAAM,cAAA,GAAiBC,0BAAA;AAAA,EAC5B,icAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,OAAA,EAAS;AAAA,QACP,OAAA,EAAS,oLAAA;AAAA,QACT,WAAA,EACE,+QAAA;AAAA,QACF,OAAA,EACE,kZAAA;AAAA,QACF,SAAA,EACE,gMAAA;AAAA,QACF,KAAA,EACE,0OAAA;AAAA,QACF,IAAA,EAAM;AAAA,OACR;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,0IAAA;AAAA,QACT,EAAA,EAAI,+JAAA;AAAA,QACJ,EAAA,EAAI,0IAAA;AAAA,QACJ,EAAA,EAAI,uJAAA;AAAA,QACJ,IAAA,EAAM,wCAAA;AAAA,QACN,SAAA,EAAW,qCAAA;AAAA,QACX,SAAA,EAAW;AAAA;AACb,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,OAAA,EAAS,SAAA;AAAA,MACT,IAAA,EAAM;AAAA;AACR;AAEJ,CAAA;AC8GO,IAAM,SAAA,GAAkBC,gBAAA,CAAA,UAAA;AAAA,EAI7B,CACE;AAAA,IACE,QAAA;AAAA,IACA,SAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,IAAA;AAAA,IACA,QAAA,GAAW,KAAA;AAAA,IACX,qBAAA,GAAwB,MAAA;AAAA,IACxB,aAAA;AAAA,IACA,YAAA,EAAc,SAAA;AAAA,IACd,kBAAA,EAAoB,eAAA;AAAA,IACpB,EAAA;AAAA,IACA,GAAG;AAAA,KAEL,GAAA,KACG;AACH,IAAA,MAAM,UAAA,GAAa,aAAA,CAAc,EAAE,IAAA,EAAM,SAAS,CAAA;AAClD,IAAA,MAAM;AAAA,MACJ,cAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA;AAAA,MACA,QAAA;AAAA,MACA,UAAA;AAAA,MAEA;AAAA,KACF,GAAI,UAAA;AAGJ,IAAA,MAAM,gBAAA,GAAmB,kBAAkB,QAAA,KAAa,MAAA;AACxD,IAAA,MAAM,kBAAA,GAAqB,CAAC,gBAAA,IAAoB,OAAA;AAIhD,IAAA,MAAM,sBAAA,GAAyB,aAAA,KAC7B,gBAAA,GACI,GAAA,GACA,qBACE,QAAA,GACA,qBAAA,CAAA;AAIR,IAAA,MAAM,kBAAA,GAAqB,UAAA,IAAc,gBAAA,GACrC,GAAA,GACA,sBAAA;AAGJ,IAAA,MAAM,uBAAA,GAA0B,YAAY,OAAA,IAAW,IAAA;AAGvD,IAAA,MAAM,iBAAA,GAAoB,EAAA;AAAA,MACxB,uBAAA,IAA2B,cAAA,CAAe,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,MAC3D;AAAA,KACF;AAGA,IAAA,MAAM,WAAA,GAAc;AAAA,MAClB,SAAA,EAAW,iBAAA;AAAA,MACX,OAAA,EAAS,WAAA;AAAA,MACT,YAAA,EAAc,SAAA;AAAA,MACd,kBAAA,EAAoB,eAAA;AAAA,MACpB,EAAA;AAAA,MACA,GAAG,MAAA,CAAO,WAAA;AAAA,QACR,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,GAAA,CAAI,UAAA,CAAW,OAAO,CAAC;AAAA;AACjE,KACF;AAGA,IAAA,IAAI,kBAAA,KAAuB,OAAO,gBAAA,EAAkB;AAClD,MAAA,uBACEC,cAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACA,IAAA,EAAM,cAAA;AAAA,UACN,MAAA;AAAA,UACA,GAAA;AAAA,UACC,GAAG,WAAA;AAAA,UACH,GAAI,KAAA;AAAA,UAEJ;AAAA;AAAA,OACH;AAAA,IAEJ;AAGA,IAAA,IAAI,uBAAuB,QAAA,EAAU;AACnC,MAAA,uBACEA,cAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACA,IAAA,EAAO,MAAsB,IAAA,IAAQ,QAAA;AAAA,UACpC,GAAG,WAAA;AAAA,UACH,GAAI,KAAA;AAAA,UAEJ;AAAA;AAAA,OACH;AAAA,IAEJ;AAGA,IAAA,IAAI,uBAAuB,KAAA,EAAO;AAChC,MAAA,uBACEA,cAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACC,GAAG,WAAA;AAAA,UAEH;AAAA;AAAA,OACH;AAAA,IAEJ;AAGA,IAAA,uBACEA,cAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACC,GAAG,WAAA;AAAA,QAEH;AAAA;AAAA,KACH;AAAA,EAEJ;AACF;AAEA,SAAA,CAAU,WAAA,GAAc,WAAA","file":"pressable.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport type LinkType = \"internal\" | \"external\" | \"mailto\" | \"tel\" | \"unknown\" | \"none\";\n\nexport interface UseNavigationArgs {\n href?: string;\n onClick?: React.MouseEventHandler<HTMLElement>;\n}\n\nexport interface UseNavigationReturn {\n linkType: LinkType;\n normalizedHref: string | undefined;\n target: \"_blank\" | \"_self\" | undefined;\n rel: string | undefined;\n isExternal: boolean;\n isInternal: boolean;\n shouldUseRouter: boolean;\n handleClick: React.MouseEventHandler<HTMLElement>;\n}\n\n/**\n * Normalizes phone numbers to tel: format\n * Handles formats like:\n * - \"+14322386131\"\n * - \"(432) 238-6131\"\n * - \"512-232-2212x123\"\n * - \"tel:+14322386131\"\n */\nfunction normalizePhoneNumber(input: string): string {\n const trimmed = input.trim();\n\n // Already has tel: prefix\n if (trimmed.toLowerCase().startsWith(\"tel:\")) {\n return trimmed;\n }\n\n // Extract digits and extension\n const match = trimmed.match(/^[\\s\\+\\-\\(\\)]*(\\d[\\d\\s\\-\\(\\)\\.]*\\d)[\\s\\-]*(x|ext\\.?|extension)?[\\s\\-]*(\\d+)?$/i);\n\n if (match) {\n // Clean the main number (remove spaces, dashes, dots, parentheses)\n const mainNumber = match[1].replace(/[\\s\\-\\(\\)\\.]/g, \"\");\n const extension = match[3];\n\n // Add + prefix if not already there and number looks international (10+ digits)\n const normalized = mainNumber.length >= 10 && !trimmed.startsWith(\"+\")\n ? `+${mainNumber}`\n : mainNumber;\n\n // Add extension if present\n const withExtension = extension ? `${normalized};ext=${extension}` : normalized;\n\n return `tel:${withExtension}`;\n }\n\n // Fallback: just clean and prefix\n const cleaned = trimmed.replace(/[\\s\\-\\(\\)\\.]/g, \"\");\n return `tel:${cleaned}`;\n}\n\n/**\n * Normalizes email addresses to mailto: format\n */\nfunction normalizeEmail(input: string): string {\n const trimmed = input.trim();\n\n // Already has mailto: prefix\n if (trimmed.toLowerCase().startsWith(\"mailto:\")) {\n return trimmed;\n }\n\n return `mailto:${trimmed}`;\n}\n\n/**\n * Detects if a string is an email address\n */\nfunction isEmail(input: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(input.trim());\n}\n\n/**\n * Detects if a string is a phone number\n */\nfunction isPhoneNumber(input: string): boolean {\n const trimmed = input.trim();\n\n // Already has tel: prefix\n if (trimmed.toLowerCase().startsWith(\"tel:\")) {\n return true;\n }\n\n // Match various phone formats\n const phoneRegex = /^[\\s\\+\\-\\(\\)]*\\d[\\d\\s\\-\\(\\)\\.]*\\d[\\s\\-]*(x|ext\\.?|extension)?[\\s\\-]*\\d*$/i;\n return phoneRegex.test(trimmed);\n}\n\n/**\n * Detects if a URL is internal to the current site\n * Handles cases like:\n * - \"/blog-123\"\n * - \"https://jordansite.com/blog-123\"\n * - \"https://www.jordansite.com/blog-123\"\n */\nfunction isInternalUrl(href: string): boolean {\n if (typeof window === \"undefined\") {\n // SSR fallback: assume relative paths are internal\n return href.startsWith(\"/\") && !href.startsWith(\"//\");\n }\n\n const trimmed = href.trim();\n\n // Relative paths are internal\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return true;\n }\n\n // Check if full URL matches current origin\n try {\n const url = new URL(trimmed, window.location.href);\n const currentOrigin = window.location.origin;\n\n // Normalize both origins (remove www. for comparison)\n const normalizeOrigin = (origin: string) =>\n origin.replace(/^(https?:\\/\\/)(www\\.)?/, \"$1\");\n\n return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);\n } catch {\n return false;\n }\n}\n\n/**\n * Converts a full URL to a relative path if it's internal\n */\nfunction toRelativePath(href: string): string {\n if (typeof window === \"undefined\") {\n return href;\n }\n\n const trimmed = href.trim();\n\n // Already relative\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return trimmed;\n }\n\n try {\n const url = new URL(trimmed, window.location.href);\n const currentOrigin = window.location.origin;\n\n // Normalize both origins for comparison\n const normalizeOrigin = (origin: string) =>\n origin.replace(/^(https?:\\/\\/)(www\\.)?/, \"$1\");\n\n if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {\n // Return pathname + search + hash\n return url.pathname + url.search + url.hash;\n }\n } catch {\n // Invalid URL, return as-is\n }\n\n return trimmed;\n}\n\n/**\n * Hook for handling navigation with automatic link type detection,\n * URL normalization, and proper attributes for SEO and accessibility.\n *\n * Features:\n * - Detects link types: internal, external, mailto, tel\n * - Normalizes phone numbers (various formats to tel:)\n * - Normalizes email addresses to mailto:\n * - Converts full URLs matching current origin to relative paths\n * - Determines proper target and rel attributes\n * - Handles React Router-style internal navigation\n *\n * @example\n * ```tsx\n * const nav = useNavigation({ href: \"/about\" });\n * // nav.linkType === \"internal\"\n * // nav.normalizedHref === \"/about\"\n * // nav.target === \"_self\"\n *\n * const nav2 = useNavigation({ href: \"(432) 238-6131\" });\n * // nav2.linkType === \"tel\"\n * // nav2.normalizedHref === \"tel:+14322386131\"\n *\n * const nav3 = useNavigation({ href: \"https://google.com\" });\n * // nav3.linkType === \"external\"\n * // nav3.target === \"_blank\"\n * // nav3.rel === \"noopener noreferrer\"\n * ```\n */\nexport function useNavigation({\n href,\n onClick\n}: UseNavigationArgs = {}): UseNavigationReturn {\n const linkType = React.useMemo((): LinkType => {\n if (!href || href.trim() === \"\") {\n return onClick ? \"none\" : \"none\";\n }\n\n const trimmed = href.trim();\n\n // Check for mailto\n if (trimmed.toLowerCase().startsWith(\"mailto:\") || isEmail(trimmed)) {\n return \"mailto\";\n }\n\n // Check for tel\n if (trimmed.toLowerCase().startsWith(\"tel:\") || isPhoneNumber(trimmed)) {\n return \"tel\";\n }\n\n // Check for internal vs external\n if (isInternalUrl(trimmed)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(trimmed, typeof window !== \"undefined\" ? window.location.href : \"http://localhost\");\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick]);\n\n const normalizedHref = React.useMemo((): string | undefined => {\n if (!href || href.trim() === \"\") {\n return undefined;\n }\n\n const trimmed = href.trim();\n\n switch (linkType) {\n case \"tel\":\n return normalizePhoneNumber(trimmed);\n case \"mailto\":\n return normalizeEmail(trimmed);\n case \"internal\":\n return toRelativePath(trimmed);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType]);\n\n const target = React.useMemo((): \"_blank\" | \"_self\" | undefined => {\n switch (linkType) {\n case \"external\":\n return \"_blank\";\n case \"internal\":\n return \"_self\";\n case \"mailto\":\n case \"tel\":\n // Let browser handle default behavior\n return undefined;\n default:\n return undefined;\n }\n }, [linkType]);\n\n const rel = React.useMemo((): string | undefined => {\n if (linkType === \"external\") {\n return \"noopener noreferrer\";\n }\n return undefined;\n }, [linkType]);\n\n const isExternal = linkType === \"external\";\n const isInternal = linkType === \"internal\";\n const shouldUseRouter = isInternal && typeof normalizedHref === \"string\" && normalizedHref.startsWith(\"/\");\n\n const handleClick = React.useCallback<React.MouseEventHandler<HTMLElement>>(\n (event) => {\n // Call user's onClick first\n if (onClick) {\n try {\n onClick(event);\n } catch (error) {\n console.error(\"Error in user onClick handler:\", error);\n }\n }\n\n // If event was prevented, don't do anything else\n if (event.defaultPrevented) {\n return;\n }\n\n // Only handle internal navigation for left-clicks without modifiers\n if (\n shouldUseRouter &&\n normalizedHref &&\n event.button === 0 && // left-click only\n !event.metaKey &&\n !event.altKey &&\n !event.ctrlKey &&\n !event.shiftKey\n ) {\n // Check if there's a navigation handler (from opensite-blocks or similar)\n if (typeof window !== \"undefined\") {\n const handler = (window as any).__opensiteNavigationHandler;\n if (typeof handler === \"function\") {\n try {\n const handled = handler(normalizedHref, event.nativeEvent || event);\n if (handled !== false) {\n event.preventDefault();\n }\n } catch (error) {\n console.error(\"Error in navigation handler:\", error);\n }\n }\n }\n }\n },\n [onClick, shouldUseRouter, normalizedHref]\n );\n\n return {\n linkType,\n normalizedHref,\n target,\n rel,\n isExternal,\n isInternal,\n shouldUseRouter,\n handleClick,\n };\n}\n","import { cva } from \"class-variance-authority\";\n\n/**\n * Button variants using class-variance-authority (cva).\n * \n * This is extracted to a separate file to avoid importing @radix-ui/react-slot\n * when only the variants are needed (e.g., in Pressable component).\n */\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-[var(--button-default-bg,hsl(var(--primary)))] text-[var(--button-default-fg,hsl(var(--primary-foreground)))] hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]\",\n destructive:\n \"bg-[var(--button-destructive-bg,hsl(var(--destructive)))] text-[var(--button-destructive-fg,white)] hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border-[var(--button-outline-border-width,1px)] border-[var(--button-outline-border,hsl(var(--border)))] bg-[var(--button-outline-bg,hsl(var(--background)))] text-[var(--button-outline-fg,inherit)] shadow-xs hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))] dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-[var(--button-secondary-bg,hsl(var(--secondary)))] text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))] hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]\",\n ghost:\n \"bg-[var(--button-ghost-bg,transparent)] text-[var(--button-ghost-fg,inherit)] hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))] dark:hover:bg-accent/50\",\n link: \"text-[var(--button-link-fg,hsl(var(--primary)))] underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n sm: \"h-[var(--button-height-sm,2rem)] rounded-button gap-1.5 px-[var(--button-padding-x-sm,0.75rem)] has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]\",\n md: \"h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n lg: \"h-[var(--button-height-lg,2.5rem)] rounded-button px-[var(--button-padding-x-lg,1.5rem)] has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]\",\n icon: \"size-[var(--button-height-md,2.25rem)]\",\n \"icon-sm\": \"size-[var(--button-height-sm,2rem)]\",\n \"icon-lg\": \"size-[var(--button-height-lg,2.5rem)]\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\n","\"use client\";\n\nimport * as React from \"react\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"./utils\";\nimport { useNavigation } from \"./useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\n\ntype FallbackComponentType = \"span\" | \"div\" | \"button\";\n\ninterface PressableBaseProps {\n /**\n * Content inside the Pressable component\n */\n children: React.ReactNode;\n\n /**\n * Additional CSS classes\n */\n className?: string;\n\n /**\n * URL to navigate to (can be internal path, external URL, mailto:, tel:, or email/phone string)\n * Examples:\n * - \"/about\" - internal link\n * - \"https://google.com\" - external link\n * - \"mailto:hello@example.com\" or \"hello@example.com\" - email link\n * - \"tel:+14322386131\" or \"(432) 238-6131\" - phone link\n * - \"https://mysite.com/blog\" - will be converted to \"/blog\" if on mysite.com\n */\n href?: string;\n\n /**\n * Click handler\n */\n onClick?: React.MouseEventHandler<HTMLElement>;\n\n /**\n * The component type to render when there's no href or onClick\n * @default \"span\"\n */\n fallbackComponentType?: FallbackComponentType;\n\n /**\n * Explicit component type to render (overrides automatic selection)\n * Note: Internal links will ALWAYS render as <a> tags for SEO, even if componentType=\"button\"\n */\n componentType?: \"a\" | \"button\" | FallbackComponentType;\n\n /**\n * Whether to render as a button styled link (uses ShadCN button styles)\n * When true, will apply button variant classes even when rendering an <a> tag\n * @default false\n */\n asButton?: boolean;\n\n /**\n * ARIA label for accessibility\n */\n \"aria-label\"?: string;\n\n /**\n * ARIA describedby for accessibility\n */\n \"aria-describedby\"?: string;\n\n /**\n * ID attribute\n */\n id?: string;\n\n /**\n * Data attributes\n */\n [key: `data-${string}`]: any;\n}\n\n// Combine Pressable props with button variants\nexport interface PressableProps\n extends PressableBaseProps,\n VariantProps<typeof buttonVariants> {}\n\n// Type for link-specific props\ninterface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {}\n\n// Type for button-specific props\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\n/**\n * Universal link/button component with automatic URL detection and normalization.\n *\n * Features:\n * - Automatic link type detection (internal, external, mailto, tel)\n * - Phone number normalization (various formats to tel:)\n * - Email normalization to mailto:\n * - Internal URL normalization (full URLs to relative paths)\n * - Proper SEO attributes (always uses <a> for links, even when styled as buttons)\n * - ShadCN button variants and sizes\n * - Flexible layout support (icon+label or custom children)\n * - React Router-style internal navigation\n *\n * @example\n * Simple link\n * ```tsx\n * <Pressable href=\"/about\">About Us</Pressable>\n * ```\n *\n * @example\n * Button-styled link with icon\n * ```tsx\n * <Pressable href=\"/quotes\" variant=\"default\" size=\"lg\" asButton>\n * <DynamicIcon name=\"lucide/calculator\" size={20} />\n * Get a Free Quote\n * </Pressable>\n * ```\n *\n * @example\n * External link (automatically gets target=\"_blank\" and rel=\"noopener noreferrer\")\n * ```tsx\n * <Pressable href=\"https://google.com\">Visit Google</Pressable>\n * ```\n *\n * @example\n * Phone link (automatically normalized to tel: format)\n * ```tsx\n * <Pressable href=\"(432) 238-6131\">Call Us</Pressable>\n * // Renders: <a href=\"tel:+14322386131\">\n * ```\n *\n * @example\n * Custom layout with full children control\n * ```tsx\n * <Pressable href=\"/services\" className=\"custom-card\">\n * <div className=\"card-header\">\n * <DynamicIcon name=\"service-icon\" />\n * <h3>Our Services</h3>\n * </div>\n * <p>Learn more about what we offer</p>\n * </Pressable>\n * ```\n *\n * @example\n * Button with onClick (no href)\n * ```tsx\n * <Pressable onClick={() => alert(\"Clicked\")} variant=\"default\" size=\"md\" asButton>\n * Click Me\n * </Pressable>\n * ```\n */\nexport const Pressable = React.forwardRef<\n HTMLAnchorElement | HTMLButtonElement | HTMLSpanElement,\n PressableProps & Partial<LinkProps> & Partial<ButtonProps>\n>(\n (\n {\n children,\n className,\n href,\n onClick,\n variant,\n size,\n asButton = false,\n fallbackComponentType = \"span\",\n componentType,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedby,\n id,\n ...props\n },\n ref\n ) => {\n const navigation = useNavigation({ href, onClick });\n const {\n normalizedHref,\n target,\n rel,\n linkType,\n isInternal,\n isExternal,\n handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n const shouldRenderFallback = !shouldRenderLink && !shouldRenderButton;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType = componentType || (\n shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType\n );\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType = isInternal && shouldRenderLink\n ? \"a\"\n : effectiveComponentType;\n\n // Determine if we should apply button styles\n const shouldApplyButtonStyles = asButton || variant || size;\n\n // Build className\n const combinedClassName = cn(\n shouldApplyButtonStyles && buttonVariants({ variant, size }),\n className\n );\n\n // Build common props\n const commonProps = {\n className: combinedClassName,\n onClick: handleClick,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedby,\n id,\n ...Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n ),\n };\n\n // Render link\n if (finalComponentType === \"a\" && shouldRenderLink) {\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={normalizedHref}\n target={target}\n rel={rel}\n {...commonProps}\n {...(props as LinkProps)}\n >\n {children}\n </a>\n );\n }\n\n // Render button\n if (finalComponentType === \"button\") {\n return (\n <button\n ref={ref as React.Ref<HTMLButtonElement>}\n type={(props as ButtonProps).type || \"button\"}\n {...commonProps}\n {...(props as ButtonProps)}\n >\n {children}\n </button>\n );\n }\n\n // Render fallback (span or div)\n if (finalComponentType === \"div\") {\n return (\n <div\n ref={ref as React.Ref<HTMLDivElement>}\n {...commonProps}\n >\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span\n ref={ref as React.Ref<HTMLSpanElement>}\n {...commonProps}\n >\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../lib/utils.ts","../lib/useNavigation.ts","../lib/button-variants.ts","../lib/Pressable.tsx"],"names":["twMerge","clsx","React","cva","React2","jsx"],"mappings":"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","file":"pressable.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport type LinkType = \"internal\" | \"external\" | \"mailto\" | \"tel\" | \"unknown\" | \"none\";\n\nexport interface UseNavigationArgs {\n href?: string;\n onClick?: React.MouseEventHandler<HTMLElement>;\n}\n\nexport interface UseNavigationReturn {\n linkType: LinkType;\n normalizedHref: string | undefined;\n target: \"_blank\" | \"_self\" | undefined;\n rel: string | undefined;\n isExternal: boolean;\n isInternal: boolean;\n shouldUseRouter: boolean;\n handleClick: React.MouseEventHandler<HTMLElement>;\n}\n\n/**\n * Normalizes phone numbers to tel: format\n * Handles formats like:\n * - \"+14322386131\"\n * - \"(432) 238-6131\"\n * - \"512-232-2212x123\"\n * - \"tel:+14322386131\"\n */\nfunction normalizePhoneNumber(input: string): string {\n const trimmed = input.trim();\n\n // Already has tel: prefix\n if (trimmed.toLowerCase().startsWith(\"tel:\")) {\n return trimmed;\n }\n\n // Extract digits and extension\n const match = trimmed.match(/^[\\s\\+\\-\\(\\)]*(\\d[\\d\\s\\-\\(\\)\\.]*\\d)[\\s\\-]*(x|ext\\.?|extension)?[\\s\\-]*(\\d+)?$/i);\n\n if (match) {\n // Clean the main number (remove spaces, dashes, dots, parentheses)\n const mainNumber = match[1].replace(/[\\s\\-\\(\\)\\.]/g, \"\");\n const extension = match[3];\n\n // Add + prefix if not already there and number looks international (10+ digits)\n const normalized = mainNumber.length >= 10 && !trimmed.startsWith(\"+\")\n ? `+${mainNumber}`\n : mainNumber;\n\n // Add extension if present\n const withExtension = extension ? `${normalized};ext=${extension}` : normalized;\n\n return `tel:${withExtension}`;\n }\n\n // Fallback: just clean and prefix\n const cleaned = trimmed.replace(/[\\s\\-\\(\\)\\.]/g, \"\");\n return `tel:${cleaned}`;\n}\n\n/**\n * Normalizes email addresses to mailto: format\n */\nfunction normalizeEmail(input: string): string {\n const trimmed = input.trim();\n\n // Already has mailto: prefix\n if (trimmed.toLowerCase().startsWith(\"mailto:\")) {\n return trimmed;\n }\n\n return `mailto:${trimmed}`;\n}\n\n/**\n * Detects if a string is an email address\n */\nfunction isEmail(input: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(input.trim());\n}\n\n/**\n * Detects if a string is a phone number\n */\nfunction isPhoneNumber(input: string): boolean {\n const trimmed = input.trim();\n\n // Already has tel: prefix\n if (trimmed.toLowerCase().startsWith(\"tel:\")) {\n return true;\n }\n\n // Match various phone formats\n const phoneRegex = /^[\\s\\+\\-\\(\\)]*\\d[\\d\\s\\-\\(\\)\\.]*\\d[\\s\\-]*(x|ext\\.?|extension)?[\\s\\-]*\\d*$/i;\n return phoneRegex.test(trimmed);\n}\n\n/**\n * Detects if a URL is internal to the current site\n * Handles cases like:\n * - \"/blog-123\"\n * - \"https://jordansite.com/blog-123\"\n * - \"https://www.jordansite.com/blog-123\"\n */\nfunction isInternalUrl(href: string): boolean {\n if (typeof window === \"undefined\") {\n // SSR fallback: assume relative paths are internal\n return href.startsWith(\"/\") && !href.startsWith(\"//\");\n }\n\n const trimmed = href.trim();\n\n // Relative paths are internal\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return true;\n }\n\n // Check if full URL matches current origin\n try {\n const url = new URL(trimmed, window.location.href);\n const currentOrigin = window.location.origin;\n\n // Normalize both origins (remove www. for comparison)\n const normalizeOrigin = (origin: string) =>\n origin.replace(/^(https?:\\/\\/)(www\\.)?/, \"$1\");\n\n return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);\n } catch {\n return false;\n }\n}\n\n/**\n * Converts a full URL to a relative path if it's internal\n */\nfunction toRelativePath(href: string): string {\n if (typeof window === \"undefined\") {\n return href;\n }\n\n const trimmed = href.trim();\n\n // Already relative\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return trimmed;\n }\n\n try {\n const url = new URL(trimmed, window.location.href);\n const currentOrigin = window.location.origin;\n\n // Normalize both origins for comparison\n const normalizeOrigin = (origin: string) =>\n origin.replace(/^(https?:\\/\\/)(www\\.)?/, \"$1\");\n\n if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {\n // Return pathname + search + hash\n return url.pathname + url.search + url.hash;\n }\n } catch {\n // Invalid URL, return as-is\n }\n\n return trimmed;\n}\n\n/**\n * Hook for handling navigation with automatic link type detection,\n * URL normalization, and proper attributes for SEO and accessibility.\n *\n * Features:\n * - Detects link types: internal, external, mailto, tel\n * - Normalizes phone numbers (various formats to tel:)\n * - Normalizes email addresses to mailto:\n * - Converts full URLs matching current origin to relative paths\n * - Determines proper target and rel attributes\n * - Handles React Router-style internal navigation\n *\n * @example\n * ```tsx\n * const nav = useNavigation({ href: \"/about\" });\n * // nav.linkType === \"internal\"\n * // nav.normalizedHref === \"/about\"\n * // nav.target === \"_self\"\n *\n * const nav2 = useNavigation({ href: \"(432) 238-6131\" });\n * // nav2.linkType === \"tel\"\n * // nav2.normalizedHref === \"tel:+14322386131\"\n *\n * const nav3 = useNavigation({ href: \"https://google.com\" });\n * // nav3.linkType === \"external\"\n * // nav3.target === \"_blank\"\n * // nav3.rel === \"noopener noreferrer\"\n * ```\n */\nexport function useNavigation({\n href,\n onClick\n}: UseNavigationArgs = {}): UseNavigationReturn {\n const linkType = React.useMemo((): LinkType => {\n if (!href || href.trim() === \"\") {\n return onClick ? \"none\" : \"none\";\n }\n\n const trimmed = href.trim();\n\n // Check for mailto\n if (trimmed.toLowerCase().startsWith(\"mailto:\") || isEmail(trimmed)) {\n return \"mailto\";\n }\n\n // Check for tel\n if (trimmed.toLowerCase().startsWith(\"tel:\") || isPhoneNumber(trimmed)) {\n return \"tel\";\n }\n\n // Check for internal vs external\n if (isInternalUrl(trimmed)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(trimmed, typeof window !== \"undefined\" ? window.location.href : \"http://localhost\");\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick]);\n\n const normalizedHref = React.useMemo((): string | undefined => {\n if (!href || href.trim() === \"\") {\n return undefined;\n }\n\n const trimmed = href.trim();\n\n switch (linkType) {\n case \"tel\":\n return normalizePhoneNumber(trimmed);\n case \"mailto\":\n return normalizeEmail(trimmed);\n case \"internal\":\n return toRelativePath(trimmed);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType]);\n\n const target = React.useMemo((): \"_blank\" | \"_self\" | undefined => {\n switch (linkType) {\n case \"external\":\n return \"_blank\";\n case \"internal\":\n return \"_self\";\n case \"mailto\":\n case \"tel\":\n // Let browser handle default behavior\n return undefined;\n default:\n return undefined;\n }\n }, [linkType]);\n\n const rel = React.useMemo((): string | undefined => {\n if (linkType === \"external\") {\n return \"noopener noreferrer\";\n }\n return undefined;\n }, [linkType]);\n\n const isExternal = linkType === \"external\";\n const isInternal = linkType === \"internal\";\n const shouldUseRouter = isInternal && typeof normalizedHref === \"string\" && normalizedHref.startsWith(\"/\");\n\n const handleClick = React.useCallback<React.MouseEventHandler<HTMLElement>>(\n (event) => {\n // Call user's onClick first\n if (onClick) {\n try {\n onClick(event);\n } catch (error) {\n console.error(\"Error in user onClick handler:\", error);\n }\n }\n\n // If event was prevented, don't do anything else\n if (event.defaultPrevented) {\n return;\n }\n\n // Only handle internal navigation for left-clicks without modifiers\n if (\n shouldUseRouter &&\n normalizedHref &&\n event.button === 0 && // left-click only\n !event.metaKey &&\n !event.altKey &&\n !event.ctrlKey &&\n !event.shiftKey\n ) {\n // Check if there's a navigation handler (from opensite-blocks or similar)\n if (typeof window !== \"undefined\") {\n const handler = (window as any).__opensiteNavigationHandler;\n if (typeof handler === \"function\") {\n try {\n const handled = handler(normalizedHref, event.nativeEvent || event);\n if (handled !== false) {\n event.preventDefault();\n }\n } catch (error) {\n console.error(\"Error in navigation handler:\", error);\n }\n }\n }\n }\n },\n [onClick, shouldUseRouter, normalizedHref]\n );\n\n return {\n linkType,\n normalizedHref,\n target,\n rel,\n isExternal,\n isInternal,\n shouldUseRouter,\n handleClick,\n };\n}\n","import { cva } from \"class-variance-authority\";\n\n/**\n * Button variants using class-variance-authority (cva).\n *\n * This is extracted to a separate file to avoid importing @radix-ui/react-slot\n * when only the variants are needed (e.g., in Pressable component).\n *\n * ## CSS Variable Reference\n *\n * ### Master Button Variables (apply to all variants)\n * - `--button-font-family` - Font family (default: inherit)\n * - `--button-font-weight` - Font weight (default: 500)\n * - `--button-letter-spacing` - Letter spacing (default: 0)\n * - `--button-line-height` - Line height (default: 1.25)\n * - `--button-text-transform` - Text transform (default: none)\n * - `--button-transition` - Transition timing (default: all 250ms cubic-bezier(0.4, 0, 0.2, 1))\n * - `--button-radius` - Border radius (default: var(--radius, 0.375rem))\n * - `--button-shadow` - Default box shadow (default: none)\n * - `--button-shadow-hover` - Hover box shadow (default: none)\n *\n * ### Size Variables\n * - `--button-height-sm/md/lg` - Button heights\n * - `--button-padding-x-sm/md/lg` - Horizontal padding\n * - `--button-padding-y-sm/md/lg` - Vertical padding\n *\n * ### Per-Variant Variables (replace {variant} with: default, destructive, outline, secondary, ghost, link)\n * - `--button-{variant}-bg` - Background color\n * - `--button-{variant}-fg` - Text/foreground color\n * - `--button-{variant}-border` - Border color\n * - `--button-{variant}-border-width` - Border width\n * - `--button-{variant}-hover-bg` - Hover background color\n * - `--button-{variant}-hover-fg` - Hover text color\n * - `--button-{variant}-hover-border` - Hover border color\n * - `--button-{variant}-shadow` - Box shadow (overrides master)\n * - `--button-{variant}-shadow-hover` - Hover box shadow (overrides master)\n */\n\n// Base styles applied to all buttons - includes master typography, transition, and layout\nconst baseStyles = [\n // Layout\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0\",\n // Typography - using CSS variables with sensible defaults\n \"font-[var(--button-font-family,inherit)]\",\n \"font-[var(--button-font-weight,500)]\",\n \"tracking-[var(--button-letter-spacing,0)]\",\n \"leading-[var(--button-line-height,1.25)]\",\n \"[text-transform:var(--button-text-transform,none)]\",\n \"text-sm\",\n // Border radius\n \"rounded-[var(--button-radius,var(--radius,0.375rem))]\",\n // Smooth transition - slower and smoother default\n \"transition-[var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level)\n \"shadow-[var(--button-shadow,none)]\",\n \"hover:shadow-[var(--button-shadow-hover,var(--button-shadow,none))]\",\n // Disabled state\n \"disabled:pointer-events-none disabled:opacity-50\",\n // SVG handling\n \"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0\",\n // Focus styles\n \"outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n // Invalid state\n \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n].join(\" \");\n\nexport const buttonVariants = cva(baseStyles, {\n variants: {\n variant: {\n // Default (Primary) variant - full customization\n default: [\n \"bg-[var(--button-default-bg,hsl(var(--primary)))]\",\n \"text-[var(--button-default-fg,hsl(var(--primary-foreground)))]\",\n \"border-[length:var(--button-default-border-width,0px)]\",\n \"border-[color:var(--button-default-border,transparent)]\",\n \"shadow-[var(--button-default-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]\",\n \"hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]\",\n \"hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]\",\n \"hover:shadow-[var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]\",\n ].join(\" \"),\n\n // Destructive variant - full customization\n destructive: [\n \"bg-[var(--button-destructive-bg,hsl(var(--destructive)))]\",\n \"text-[var(--button-destructive-fg,white)]\",\n \"border-[length:var(--button-destructive-border-width,0px)]\",\n \"border-[color:var(--button-destructive-border,transparent)]\",\n \"shadow-[var(--button-destructive-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]\",\n \"hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]\",\n \"hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]\",\n \"hover:shadow-[var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]\",\n \"focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40\",\n \"dark:bg-destructive/60\",\n ].join(\" \"),\n\n // Outline variant - full customization with proper border handling\n outline: [\n \"bg-[var(--button-outline-bg,hsl(var(--background)))]\",\n \"text-[var(--button-outline-fg,inherit)]\",\n \"border-[length:var(--button-outline-border-width,1px)]\",\n \"border-[color:var(--button-outline-border,hsl(var(--border)))]\",\n \"shadow-[var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]\",\n \"hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]\",\n \"hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]\",\n \"hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]\",\n \"hover:shadow-[var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]\",\n \"dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n ].join(\" \"),\n\n // Secondary variant - full customization\n secondary: [\n \"bg-[var(--button-secondary-bg,hsl(var(--secondary)))]\",\n \"text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]\",\n \"border-[length:var(--button-secondary-border-width,0px)]\",\n \"border-[color:var(--button-secondary-border,transparent)]\",\n \"shadow-[var(--button-secondary-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]\",\n \"hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]\",\n \"hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]\",\n \"hover:shadow-[var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]\",\n ].join(\" \"),\n\n // Ghost variant - full customization\n ghost: [\n \"bg-[var(--button-ghost-bg,transparent)]\",\n \"text-[var(--button-ghost-fg,inherit)]\",\n \"border-[length:var(--button-ghost-border-width,0px)]\",\n \"border-[color:var(--button-ghost-border,transparent)]\",\n \"shadow-[var(--button-ghost-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]\",\n \"hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]\",\n \"hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]\",\n \"hover:shadow-[var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]\",\n \"dark:hover:bg-accent/50\",\n ].join(\" \"),\n\n // Link variant - full customization\n link: [\n \"bg-[var(--button-link-bg,transparent)]\",\n \"text-[var(--button-link-fg,hsl(var(--primary)))]\",\n \"border-[length:var(--button-link-border-width,0px)]\",\n \"border-[color:var(--button-link-border,transparent)]\",\n \"shadow-[var(--button-link-shadow,none)]\",\n \"hover:bg-[var(--button-link-hover-bg,transparent)]\",\n \"hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]\",\n \"hover:shadow-[var(--button-link-shadow-hover,none)]\",\n \"underline-offset-4 hover:underline\",\n ].join(\" \"),\n },\n size: {\n default: [\n \"h-[var(--button-height-md,2.25rem)]\",\n \"px-[var(--button-padding-x-md,1rem)]\",\n \"py-[var(--button-padding-y-md,0.5rem)]\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n ].join(\" \"),\n sm: [\n \"h-[var(--button-height-sm,2rem)]\",\n \"px-[var(--button-padding-x-sm,0.75rem)]\",\n \"py-[var(--button-padding-y-sm,0.25rem)]\",\n \"gap-1.5\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]\",\n ].join(\" \"),\n md: [\n \"h-[var(--button-height-md,2.25rem)]\",\n \"px-[var(--button-padding-x-md,1rem)]\",\n \"py-[var(--button-padding-y-md,0.5rem)]\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n ].join(\" \"),\n lg: [\n \"h-[var(--button-height-lg,2.5rem)]\",\n \"px-[var(--button-padding-x-lg,1.5rem)]\",\n \"py-[var(--button-padding-y-lg,0.5rem)]\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]\",\n ].join(\" \"),\n icon: \"size-[var(--button-height-md,2.25rem)]\",\n \"icon-sm\": \"size-[var(--button-height-sm,2rem)]\",\n \"icon-lg\": \"size-[var(--button-height-lg,2.5rem)]\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n});\n\n","\"use client\";\n\nimport * as React from \"react\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"./utils\";\nimport { useNavigation } from \"./useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\n\ntype FallbackComponentType = \"span\" | \"div\" | \"button\";\n\ninterface PressableBaseProps {\n /**\n * Content inside the Pressable component\n */\n children: React.ReactNode;\n\n /**\n * Additional CSS classes\n */\n className?: string;\n\n /**\n * URL to navigate to (can be internal path, external URL, mailto:, tel:, or email/phone string)\n * Examples:\n * - \"/about\" - internal link\n * - \"https://google.com\" - external link\n * - \"mailto:hello@example.com\" or \"hello@example.com\" - email link\n * - \"tel:+14322386131\" or \"(432) 238-6131\" - phone link\n * - \"https://mysite.com/blog\" - will be converted to \"/blog\" if on mysite.com\n */\n href?: string;\n\n /**\n * Click handler\n */\n onClick?: React.MouseEventHandler<HTMLElement>;\n\n /**\n * The component type to render when there's no href or onClick\n * @default \"span\"\n */\n fallbackComponentType?: FallbackComponentType;\n\n /**\n * Explicit component type to render (overrides automatic selection)\n * Note: Internal links will ALWAYS render as <a> tags for SEO, even if componentType=\"button\"\n */\n componentType?: \"a\" | \"button\" | FallbackComponentType;\n\n /**\n * Whether to render as a button styled link (uses ShadCN button styles)\n * When true, will apply button variant classes even when rendering an <a> tag\n * @default false\n */\n asButton?: boolean;\n\n /**\n * ARIA label for accessibility\n */\n \"aria-label\"?: string;\n\n /**\n * ARIA describedby for accessibility\n */\n \"aria-describedby\"?: string;\n\n /**\n * ID attribute\n */\n id?: string;\n\n /**\n * Data attributes\n */\n [key: `data-${string}`]: any;\n}\n\n// Combine Pressable props with button variants\nexport interface PressableProps\n extends PressableBaseProps,\n VariantProps<typeof buttonVariants> {}\n\n// Type for link-specific props\ninterface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {}\n\n// Type for button-specific props\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\n/**\n * Universal link/button component with automatic URL detection and normalization.\n *\n * Features:\n * - Automatic link type detection (internal, external, mailto, tel)\n * - Phone number normalization (various formats to tel:)\n * - Email normalization to mailto:\n * - Internal URL normalization (full URLs to relative paths)\n * - Proper SEO attributes (always uses <a> for links, even when styled as buttons)\n * - ShadCN button variants and sizes\n * - Flexible layout support (icon+label or custom children)\n * - React Router-style internal navigation\n *\n * @example\n * Simple link\n * ```tsx\n * <Pressable href=\"/about\">About Us</Pressable>\n * ```\n *\n * @example\n * Button-styled link with icon\n * ```tsx\n * <Pressable href=\"/quotes\" variant=\"default\" size=\"lg\" asButton>\n * <DynamicIcon name=\"lucide/calculator\" size={20} />\n * Get a Free Quote\n * </Pressable>\n * ```\n *\n * @example\n * External link (automatically gets target=\"_blank\" and rel=\"noopener noreferrer\")\n * ```tsx\n * <Pressable href=\"https://google.com\">Visit Google</Pressable>\n * ```\n *\n * @example\n * Phone link (automatically normalized to tel: format)\n * ```tsx\n * <Pressable href=\"(432) 238-6131\">Call Us</Pressable>\n * // Renders: <a href=\"tel:+14322386131\">\n * ```\n *\n * @example\n * Custom layout with full children control\n * ```tsx\n * <Pressable href=\"/services\" className=\"custom-card\">\n * <div className=\"card-header\">\n * <DynamicIcon name=\"service-icon\" />\n * <h3>Our Services</h3>\n * </div>\n * <p>Learn more about what we offer</p>\n * </Pressable>\n * ```\n *\n * @example\n * Button with onClick (no href)\n * ```tsx\n * <Pressable onClick={() => alert(\"Clicked\")} variant=\"default\" size=\"md\" asButton>\n * Click Me\n * </Pressable>\n * ```\n */\nexport const Pressable = React.forwardRef<\n HTMLAnchorElement | HTMLButtonElement | HTMLSpanElement,\n PressableProps & Partial<LinkProps> & Partial<ButtonProps>\n>(\n (\n {\n children,\n className,\n href,\n onClick,\n variant,\n size,\n asButton = false,\n fallbackComponentType = \"span\",\n componentType,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedby,\n id,\n ...props\n },\n ref\n ) => {\n const navigation = useNavigation({ href, onClick });\n const {\n normalizedHref,\n target,\n rel,\n linkType,\n isInternal,\n isExternal,\n handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n const shouldRenderFallback = !shouldRenderLink && !shouldRenderButton;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType = componentType || (\n shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType\n );\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType = isInternal && shouldRenderLink\n ? \"a\"\n : effectiveComponentType;\n\n // Determine if we should apply button styles\n const shouldApplyButtonStyles = asButton || variant || size;\n\n // Build className\n const combinedClassName = cn(\n shouldApplyButtonStyles && buttonVariants({ variant, size }),\n className\n );\n\n // Build common props\n const commonProps = {\n className: combinedClassName,\n onClick: handleClick,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedby,\n id,\n ...Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n ),\n };\n\n // Render link\n if (finalComponentType === \"a\" && shouldRenderLink) {\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={normalizedHref}\n target={target}\n rel={rel}\n {...commonProps}\n {...(props as LinkProps)}\n >\n {children}\n </a>\n );\n }\n\n // Render button\n if (finalComponentType === \"button\") {\n return (\n <button\n ref={ref as React.Ref<HTMLButtonElement>}\n type={(props as ButtonProps).type || \"button\"}\n {...commonProps}\n {...(props as ButtonProps)}\n >\n {children}\n </button>\n );\n }\n\n // Render fallback (span or div)\n if (finalComponentType === \"div\") {\n return (\n <div\n ref={ref as React.Ref<HTMLDivElement>}\n {...commonProps}\n >\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span\n ref={ref as React.Ref<HTMLSpanElement>}\n {...commonProps}\n >\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]}
|
package/dist/pressable.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
|
-
import { b as buttonVariants } from './button-variants-
|
|
3
|
+
import { b as buttonVariants } from './button-variants-CbpzhUdJ.cjs';
|
|
4
4
|
import 'class-variance-authority/types';
|
|
5
5
|
|
|
6
6
|
type FallbackComponentType = "span" | "div" | "button";
|
package/dist/pressable.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
|
-
import { b as buttonVariants } from './button-variants-
|
|
3
|
+
import { b as buttonVariants } from './button-variants-CbpzhUdJ.js';
|
|
4
4
|
import 'class-variance-authority/types';
|
|
5
5
|
|
|
6
6
|
type FallbackComponentType = "span" | "div" | "button";
|
package/dist/pressable.js
CHANGED
|
@@ -186,34 +186,148 @@ function useNavigation({
|
|
|
186
186
|
handleClick
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
|
-
var
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
189
|
+
var baseStyles = [
|
|
190
|
+
// Layout
|
|
191
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
|
|
192
|
+
// Typography - using CSS variables with sensible defaults
|
|
193
|
+
"font-[var(--button-font-family,inherit)]",
|
|
194
|
+
"font-[var(--button-font-weight,500)]",
|
|
195
|
+
"tracking-[var(--button-letter-spacing,0)]",
|
|
196
|
+
"leading-[var(--button-line-height,1.25)]",
|
|
197
|
+
"[text-transform:var(--button-text-transform,none)]",
|
|
198
|
+
"text-sm",
|
|
199
|
+
// Border radius
|
|
200
|
+
"rounded-[var(--button-radius,var(--radius,0.375rem))]",
|
|
201
|
+
// Smooth transition - slower and smoother default
|
|
202
|
+
"transition-[var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
|
|
203
|
+
// Box shadow (master level)
|
|
204
|
+
"shadow-[var(--button-shadow,none)]",
|
|
205
|
+
"hover:shadow-[var(--button-shadow-hover,var(--button-shadow,none))]",
|
|
206
|
+
// Disabled state
|
|
207
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
208
|
+
// SVG handling
|
|
209
|
+
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
|
|
210
|
+
// Focus styles
|
|
211
|
+
"outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
212
|
+
// Invalid state
|
|
213
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
|
|
214
|
+
].join(" ");
|
|
215
|
+
var buttonVariants = cva(baseStyles, {
|
|
216
|
+
variants: {
|
|
217
|
+
variant: {
|
|
218
|
+
// Default (Primary) variant - full customization
|
|
219
|
+
default: [
|
|
220
|
+
"bg-[var(--button-default-bg,hsl(var(--primary)))]",
|
|
221
|
+
"text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
|
|
222
|
+
"border-[length:var(--button-default-border-width,0px)]",
|
|
223
|
+
"border-[color:var(--button-default-border,transparent)]",
|
|
224
|
+
"shadow-[var(--button-default-shadow,var(--button-shadow,none))]",
|
|
225
|
+
"hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
|
|
226
|
+
"hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
|
|
227
|
+
"hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
|
|
228
|
+
"hover:shadow-[var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
|
|
229
|
+
].join(" "),
|
|
230
|
+
// Destructive variant - full customization
|
|
231
|
+
destructive: [
|
|
232
|
+
"bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
|
|
233
|
+
"text-[var(--button-destructive-fg,white)]",
|
|
234
|
+
"border-[length:var(--button-destructive-border-width,0px)]",
|
|
235
|
+
"border-[color:var(--button-destructive-border,transparent)]",
|
|
236
|
+
"shadow-[var(--button-destructive-shadow,var(--button-shadow,none))]",
|
|
237
|
+
"hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
|
|
238
|
+
"hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
|
|
239
|
+
"hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
|
|
240
|
+
"hover:shadow-[var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
|
|
241
|
+
"focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
|
|
242
|
+
"dark:bg-destructive/60"
|
|
243
|
+
].join(" "),
|
|
244
|
+
// Outline variant - full customization with proper border handling
|
|
245
|
+
outline: [
|
|
246
|
+
"bg-[var(--button-outline-bg,hsl(var(--background)))]",
|
|
247
|
+
"text-[var(--button-outline-fg,inherit)]",
|
|
248
|
+
"border-[length:var(--button-outline-border-width,1px)]",
|
|
249
|
+
"border-[color:var(--button-outline-border,hsl(var(--border)))]",
|
|
250
|
+
"shadow-[var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
|
|
251
|
+
"hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
|
|
252
|
+
"hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
|
|
253
|
+
"hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
|
|
254
|
+
"hover:shadow-[var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
|
|
255
|
+
"dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
|
|
256
|
+
].join(" "),
|
|
257
|
+
// Secondary variant - full customization
|
|
258
|
+
secondary: [
|
|
259
|
+
"bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
|
|
260
|
+
"text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
|
|
261
|
+
"border-[length:var(--button-secondary-border-width,0px)]",
|
|
262
|
+
"border-[color:var(--button-secondary-border,transparent)]",
|
|
263
|
+
"shadow-[var(--button-secondary-shadow,var(--button-shadow,none))]",
|
|
264
|
+
"hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
|
|
265
|
+
"hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
|
|
266
|
+
"hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
|
|
267
|
+
"hover:shadow-[var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
|
|
268
|
+
].join(" "),
|
|
269
|
+
// Ghost variant - full customization
|
|
270
|
+
ghost: [
|
|
271
|
+
"bg-[var(--button-ghost-bg,transparent)]",
|
|
272
|
+
"text-[var(--button-ghost-fg,inherit)]",
|
|
273
|
+
"border-[length:var(--button-ghost-border-width,0px)]",
|
|
274
|
+
"border-[color:var(--button-ghost-border,transparent)]",
|
|
275
|
+
"shadow-[var(--button-ghost-shadow,var(--button-shadow,none))]",
|
|
276
|
+
"hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
|
|
277
|
+
"hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
|
|
278
|
+
"hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
|
|
279
|
+
"hover:shadow-[var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
|
|
280
|
+
"dark:hover:bg-accent/50"
|
|
281
|
+
].join(" "),
|
|
282
|
+
// Link variant - full customization
|
|
283
|
+
link: [
|
|
284
|
+
"bg-[var(--button-link-bg,transparent)]",
|
|
285
|
+
"text-[var(--button-link-fg,hsl(var(--primary)))]",
|
|
286
|
+
"border-[length:var(--button-link-border-width,0px)]",
|
|
287
|
+
"border-[color:var(--button-link-border,transparent)]",
|
|
288
|
+
"shadow-[var(--button-link-shadow,none)]",
|
|
289
|
+
"hover:bg-[var(--button-link-hover-bg,transparent)]",
|
|
290
|
+
"hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
|
|
291
|
+
"hover:shadow-[var(--button-link-shadow-hover,none)]",
|
|
292
|
+
"underline-offset-4 hover:underline"
|
|
293
|
+
].join(" ")
|
|
210
294
|
},
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
295
|
+
size: {
|
|
296
|
+
default: [
|
|
297
|
+
"h-[var(--button-height-md,2.25rem)]",
|
|
298
|
+
"px-[var(--button-padding-x-md,1rem)]",
|
|
299
|
+
"py-[var(--button-padding-y-md,0.5rem)]",
|
|
300
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
|
|
301
|
+
].join(" "),
|
|
302
|
+
sm: [
|
|
303
|
+
"h-[var(--button-height-sm,2rem)]",
|
|
304
|
+
"px-[var(--button-padding-x-sm,0.75rem)]",
|
|
305
|
+
"py-[var(--button-padding-y-sm,0.25rem)]",
|
|
306
|
+
"gap-1.5",
|
|
307
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
|
|
308
|
+
].join(" "),
|
|
309
|
+
md: [
|
|
310
|
+
"h-[var(--button-height-md,2.25rem)]",
|
|
311
|
+
"px-[var(--button-padding-x-md,1rem)]",
|
|
312
|
+
"py-[var(--button-padding-y-md,0.5rem)]",
|
|
313
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
|
|
314
|
+
].join(" "),
|
|
315
|
+
lg: [
|
|
316
|
+
"h-[var(--button-height-lg,2.5rem)]",
|
|
317
|
+
"px-[var(--button-padding-x-lg,1.5rem)]",
|
|
318
|
+
"py-[var(--button-padding-y-lg,0.5rem)]",
|
|
319
|
+
"has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
|
|
320
|
+
].join(" "),
|
|
321
|
+
icon: "size-[var(--button-height-md,2.25rem)]",
|
|
322
|
+
"icon-sm": "size-[var(--button-height-sm,2rem)]",
|
|
323
|
+
"icon-lg": "size-[var(--button-height-lg,2.5rem)]"
|
|
214
324
|
}
|
|
325
|
+
},
|
|
326
|
+
defaultVariants: {
|
|
327
|
+
variant: "default",
|
|
328
|
+
size: "default"
|
|
215
329
|
}
|
|
216
|
-
);
|
|
330
|
+
});
|
|
217
331
|
var Pressable = React.forwardRef(
|
|
218
332
|
({
|
|
219
333
|
children,
|