@page-speed/pressable
Advanced tools
@@ -412,2 +412,3 @@ 'use strict'; | ||
| ref, | ||
| ...props, | ||
| href: normalizedHref, | ||
@@ -417,3 +418,2 @@ target, | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -428,5 +428,5 @@ } | ||
| ref, | ||
| ...props, | ||
| type: props.type || "button", | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -433,0 +433,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/utils/cn.ts","../../src/hooks/useNavigation.ts","../../src/core/button-variants.ts","../../src/core/Pressable.tsx"],"names":["twMerge","clsx","isBrowser","useRouterNavigation","useUrl","React","cva","React2","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\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 ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} | ||
| {"version":3,"sources":["../../src/utils/cn.ts","../../src/hooks/useNavigation.ts","../../src/core/button-variants.ts","../../src/core/Pressable.tsx"],"names":["twMerge","clsx","isBrowser","useRouterNavigation","useUrl","React","cva","React2","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\n };\n\n // Render link\n if (finalComponentType === \"a\" && shouldRenderLink) {\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n {...(props as LinkProps)}\n href={normalizedHref}\n target={target}\n rel={rel}\n {...commonProps}\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 {...(props as ButtonProps)}\n type={(props as ButtonProps).type || \"button\"}\n {...commonProps}\n >\n {children}\n </button>\n );\n }\n\n // Render fallback (span or div)\n if (finalComponentType === \"div\") {\n return (\n <div ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} |
@@ -390,2 +390,3 @@ import * as React from 'react'; | ||
| ref, | ||
| ...props, | ||
| href: normalizedHref, | ||
@@ -395,3 +396,2 @@ target, | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -406,5 +406,5 @@ } | ||
| ref, | ||
| ...props, | ||
| type: props.type || "button", | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -411,0 +411,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/utils/cn.ts","../../src/hooks/useNavigation.ts","../../src/core/button-variants.ts","../../src/core/Pressable.tsx"],"names":["useRouterNavigation","React2"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\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 ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} | ||
| {"version":3,"sources":["../../src/utils/cn.ts","../../src/hooks/useNavigation.ts","../../src/core/button-variants.ts","../../src/core/Pressable.tsx"],"names":["useRouterNavigation","React2"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\n };\n\n // Render link\n if (finalComponentType === \"a\" && shouldRenderLink) {\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n {...(props as LinkProps)}\n href={normalizedHref}\n target={target}\n rel={rel}\n {...commonProps}\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 {...(props as ButtonProps)}\n type={(props as ButtonProps).type || \"button\"}\n {...commonProps}\n >\n {children}\n </button>\n );\n }\n\n // Render fallback (span or div)\n if (finalComponentType === \"div\") {\n return (\n <div ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} |
+2
-2
@@ -412,2 +412,3 @@ 'use strict'; | ||
| ref, | ||
| ...props, | ||
| href: normalizedHref, | ||
@@ -417,3 +418,2 @@ target, | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -428,5 +428,5 @@ } | ||
| ref, | ||
| ...props, | ||
| type: props.type || "button", | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -433,0 +433,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/utils/cn.ts","../src/hooks/useNavigation.ts","../src/core/button-variants.ts","../src/core/Pressable.tsx"],"names":["twMerge","clsx","isBrowser","useRouterNavigation","useUrl","React","cva","React2","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\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 ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} | ||
| {"version":3,"sources":["../src/utils/cn.ts","../src/hooks/useNavigation.ts","../src/core/button-variants.ts","../src/core/Pressable.tsx"],"names":["twMerge","clsx","isBrowser","useRouterNavigation","useUrl","React","cva","React2","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\n };\n\n // Render link\n if (finalComponentType === \"a\" && shouldRenderLink) {\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n {...(props as LinkProps)}\n href={normalizedHref}\n target={target}\n rel={rel}\n {...commonProps}\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 {...(props as ButtonProps)}\n type={(props as ButtonProps).type || \"button\"}\n {...commonProps}\n >\n {children}\n </button>\n );\n }\n\n // Render fallback (span or div)\n if (finalComponentType === \"div\") {\n return (\n <div ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} |
+2
-2
@@ -390,2 +390,3 @@ import * as React from 'react'; | ||
| ref, | ||
| ...props, | ||
| href: normalizedHref, | ||
@@ -395,3 +396,2 @@ target, | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -406,5 +406,5 @@ } | ||
| ref, | ||
| ...props, | ||
| type: props.type || "button", | ||
| ...commonProps, | ||
| ...props, | ||
| children | ||
@@ -411,0 +411,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/utils/cn.ts","../src/hooks/useNavigation.ts","../src/core/button-variants.ts","../src/core/Pressable.tsx"],"names":["useRouterNavigation","React2"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\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 ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} | ||
| {"version":3,"sources":["../src/utils/cn.ts","../src/hooks/useNavigation.ts","../src/core/button-variants.ts","../src/core/Pressable.tsx"],"names":["useRouterNavigation","React2"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx for conditional classes and tailwind-merge for conflict resolution\n *\n * @param inputs - Class names, arrays, or objects to merge\n * @returns Merged class string with Tailwind conflicts resolved\n *\n * @example\n * ```tsx\n * cn(\"px-2 py-1\", isActive && \"bg-blue-500\", { \"font-bold\": isImportant })\n * // => \"px-2 py-1 bg-blue-500 font-bold\" (if both conditions are true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useNavigation as useRouterNavigation, useUrl, isBrowser } from \"@page-speed/router\";\nimport type { UseNavigationArgs, UseNavigationReturn, LinkType } from \"../types\";\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 // Check for extension markers (x, ext, extension)\n const extensionMatch = trimmed.match(/^(.+?)\\s*(x|ext\\.?|extension)\\s*(\\d+)$/i);\n let phoneNumber = trimmed;\n let extension = \"\";\n\n if (extensionMatch) {\n phoneNumber = extensionMatch[1];\n extension = extensionMatch[3];\n }\n\n // Clean the phone number (remove everything except digits and leading +)\n const hasPlus = phoneNumber.trim().startsWith(\"+\");\n const cleaned = phoneNumber.replace(/[^\\d]/g, \"\");\n\n // Add country code if needed:\n // - For exactly 10 digits (US/Canada format), prepend +1\n // - For 11+ digits without +, just add +\n let normalized = cleaned;\n if (!hasPlus) {\n if (cleaned.length === 10) {\n normalized = `+1${cleaned}`;\n } else if (cleaned.length >= 11) {\n normalized = `+${cleaned}`;\n }\n }\n\n // Add extension if present\n const withExtension = extension\n ? `${normalized};ext=${extension}`\n : normalized;\n\n return `tel:${withExtension}`;\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 =\n /^[\\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, currentOrigin: string, currentHref: string): boolean {\n if (!isBrowser()) {\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, currentHref);\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, currentOrigin: string, currentHref: string): string {\n if (!isBrowser()) {\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, currentHref);\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 // Get router navigation functions and current URL (SSR-safe)\n const { navigateTo } = useRouterNavigation();\n const currentUrl = useUrl();\n\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, currentUrl.origin, currentUrl.href)) {\n return \"internal\";\n }\n\n // Check if it's a valid URL\n try {\n new URL(\n trimmed,\n currentUrl.href || \"http://localhost\"\n );\n return \"external\";\n } catch {\n // Not a valid URL, treat as internal path\n return \"internal\";\n }\n }, [href, onClick, currentUrl.origin, currentUrl.href]);\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, currentUrl.origin, currentUrl.href);\n case \"external\":\n return trimmed;\n default:\n return trimmed;\n }\n }, [href, linkType, currentUrl.origin, currentUrl.href]);\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 =\n isInternal &&\n typeof normalizedHref === \"string\" &&\n 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 event.preventDefault();\n\n // Use the router's navigateTo for internal navigation\n navigateTo(normalizedHref);\n }\n },\n [onClick, shouldUseRouter, normalizedHref, navigateTo]\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-font-style` - Font style (default: normal)\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-family:var(--button-font-family,inherit)]\",\n \"[font-weight:var(--button-font-weight,500)]\",\n \"[font-style:var(--button-font-style,normal)]\",\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 - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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 \"[box-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:[box-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","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useNavigation } from \"../hooks/useNavigation\";\nimport { buttonVariants } from \"./button-variants\";\nimport type { PressableProps, LinkProps, ButtonProps } from \"../types\";\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 handleClick,\n } = navigation;\n\n // Determine what component to render\n const shouldRenderLink = normalizedHref && linkType !== \"none\";\n const shouldRenderButton = !shouldRenderLink && onClick;\n\n // Force <a> tag for internal links for SEO (even if componentType=\"button\")\n const effectiveComponentType =\n componentType ||\n (shouldRenderLink\n ? \"a\"\n : shouldRenderButton\n ? \"button\"\n : fallbackComponentType);\n\n // Override for SEO: internal links must be <a> tags\n const finalComponentType =\n isInternal && shouldRenderLink ? \"a\" : 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 const dataProps = Object.fromEntries(\n Object.entries(props).filter(([key]) => key.startsWith(\"data-\"))\n );\n const buttonDataAttributes = shouldApplyButtonStyles\n ? {\n \"data-slot\": \"button\",\n \"data-variant\": variant ?? \"default\",\n \"data-size\": size ?? \"default\",\n }\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 ...dataProps,\n ...buttonDataAttributes,\n };\n\n // Render link\n if (finalComponentType === \"a\" && shouldRenderLink) {\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n {...(props as LinkProps)}\n href={normalizedHref}\n target={target}\n rel={rel}\n {...commonProps}\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 {...(props as ButtonProps)}\n type={(props as ButtonProps).type || \"button\"}\n {...commonProps}\n >\n {children}\n </button>\n );\n }\n\n // Render fallback (span or div)\n if (finalComponentType === \"div\") {\n return (\n <div ref={ref as React.Ref<HTMLDivElement>} {...commonProps}>\n {children}\n </div>\n );\n }\n\n // Default to span\n return (\n <span ref={ref as React.Ref<HTMLSpanElement>} {...commonProps}>\n {children}\n </span>\n );\n }\n);\n\nPressable.displayName = \"Pressable\";\n"]} |
+1
-1
| { | ||
| "name": "@page-speed/pressable", | ||
| "version": "0.0.8", | ||
| "version": "0.0.9", | ||
| "description": "Performance-optimized universal link/button component with automatic URL detection and normalization for DashTrack ecosystem", | ||
@@ -5,0 +5,0 @@ "license": "BSD-3-Clause", |