@timmbr/ui 1.0.0-beta → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +20 -16
  2. package/dist/components/Avatar/Avatar.styles.d.ts +1 -1
  3. package/dist/components/Badge/Badge.styles.d.ts +1 -1
  4. package/dist/components/Button/Button.styles.d.ts +2 -2
  5. package/dist/components/Chip/Chip.styles.d.ts +1 -1
  6. package/dist/components/CrossZoneLink/CrossZoneLink.d.ts +10 -0
  7. package/dist/components/CrossZoneLink/CrossZoneLink.d.ts.map +1 -0
  8. package/dist/components/CrossZoneLink/CrossZoneLink.js +111 -0
  9. package/dist/components/CrossZoneLink/CrossZoneLink.styles.d.ts +8 -0
  10. package/dist/components/CrossZoneLink/CrossZoneLink.styles.d.ts.map +1 -0
  11. package/dist/components/CrossZoneLink/CrossZoneLink.styles.js +33 -0
  12. package/dist/components/CrossZoneLink/CrossZoneLink.types.d.ts +31 -0
  13. package/dist/components/CrossZoneLink/CrossZoneLink.types.d.ts.map +1 -0
  14. package/dist/components/CrossZoneLink/index.d.ts +4 -0
  15. package/dist/components/CrossZoneLink/index.d.ts.map +1 -0
  16. package/dist/components/Divider/Divider.styles.d.ts +1 -1
  17. package/dist/components/FormField/FormField.js +3 -3
  18. package/dist/components/Heading/Heading.styles.d.ts +2 -2
  19. package/dist/components/Icon/Icon.styles.d.ts +1 -1
  20. package/dist/components/Inline/Inline.styles.d.ts +2 -2
  21. package/dist/components/Input/Input.js +3 -3
  22. package/dist/components/Input/Input.styles.d.ts +1 -1
  23. package/dist/components/Input/Input.styles.js +1 -1
  24. package/dist/components/Input/Input.types.d.ts +1 -1
  25. package/dist/components/Input/Input.types.d.ts.map +1 -1
  26. package/dist/components/LinkButton/LinkButton.d.ts +13 -0
  27. package/dist/components/LinkButton/LinkButton.d.ts.map +1 -0
  28. package/dist/components/LinkButton/LinkButton.js +179 -0
  29. package/dist/components/LinkButton/LinkButton.styles.d.ts +8 -0
  30. package/dist/components/LinkButton/LinkButton.styles.d.ts.map +1 -0
  31. package/dist/components/LinkButton/LinkButton.styles.js +61 -0
  32. package/dist/components/LinkButton/LinkButton.types.d.ts +60 -0
  33. package/dist/components/LinkButton/LinkButton.types.d.ts.map +1 -0
  34. package/dist/components/LinkButton/index.d.ts +4 -0
  35. package/dist/components/LinkButton/index.d.ts.map +1 -0
  36. package/dist/components/Progress/Progress.styles.d.ts +1 -1
  37. package/dist/components/Radio/Radio.js +3 -3
  38. package/dist/components/Select/Select.js +3 -3
  39. package/dist/components/Select/Select.styles.d.ts +1 -1
  40. package/dist/components/SideBarNavigation/SideBarNavigation.context.d.ts +15 -0
  41. package/dist/components/SideBarNavigation/SideBarNavigation.context.d.ts.map +1 -0
  42. package/dist/components/SideBarNavigation/SideBarNavigation.context.js +10 -0
  43. package/dist/components/SideBarNavigation/SideBarNavigation.d.ts +4 -0
  44. package/dist/components/SideBarNavigation/SideBarNavigation.d.ts.map +1 -0
  45. package/dist/components/SideBarNavigation/SideBarNavigation.helpers.d.ts +19 -0
  46. package/dist/components/SideBarNavigation/SideBarNavigation.helpers.d.ts.map +1 -0
  47. package/dist/components/SideBarNavigation/SideBarNavigation.helpers.js +48 -0
  48. package/dist/components/SideBarNavigation/SideBarNavigation.js +132 -0
  49. package/dist/components/SideBarNavigation/SideBarNavigation.styles.d.ts +18 -0
  50. package/dist/components/SideBarNavigation/SideBarNavigation.styles.d.ts.map +1 -0
  51. package/dist/components/SideBarNavigation/SideBarNavigation.styles.js +85 -0
  52. package/dist/components/SideBarNavigation/SideBarNavigation.types.d.ts +69 -0
  53. package/dist/components/SideBarNavigation/SideBarNavigation.types.d.ts.map +1 -0
  54. package/dist/components/SideBarNavigation/index.d.ts +13 -0
  55. package/dist/components/SideBarNavigation/index.d.ts.map +1 -0
  56. package/dist/components/SideBarNavigation/subcomponents/SidebarBranding.d.ts +13 -0
  57. package/dist/components/SideBarNavigation/subcomponents/SidebarBranding.d.ts.map +1 -0
  58. package/dist/components/SideBarNavigation/subcomponents/SidebarBranding.js +58 -0
  59. package/dist/components/SideBarNavigation/subcomponents/SidebarFooter.d.ts +10 -0
  60. package/dist/components/SideBarNavigation/subcomponents/SidebarFooter.d.ts.map +1 -0
  61. package/dist/components/SideBarNavigation/subcomponents/SidebarFooter.js +27 -0
  62. package/dist/components/SideBarNavigation/subcomponents/SidebarNavItem.d.ts +14 -0
  63. package/dist/components/SideBarNavigation/subcomponents/SidebarNavItem.d.ts.map +1 -0
  64. package/dist/components/SideBarNavigation/subcomponents/SidebarNavItem.js +147 -0
  65. package/dist/components/SideBarNavigation/subcomponents/SidebarPopoverMenu.d.ts +13 -0
  66. package/dist/components/SideBarNavigation/subcomponents/SidebarPopoverMenu.d.ts.map +1 -0
  67. package/dist/components/SideBarNavigation/subcomponents/SidebarPopoverMenu.js +106 -0
  68. package/dist/components/SideBarNavigation/subcomponents/SidebarProfile.d.ts +13 -0
  69. package/dist/components/SideBarNavigation/subcomponents/SidebarProfile.d.ts.map +1 -0
  70. package/dist/components/SideBarNavigation/subcomponents/SidebarProfile.js +118 -0
  71. package/dist/components/SideBarNavigation/subcomponents/SidebarSubItem.d.ts +13 -0
  72. package/dist/components/SideBarNavigation/subcomponents/SidebarSubItem.d.ts.map +1 -0
  73. package/dist/components/SideBarNavigation/subcomponents/SidebarSubItem.js +34 -0
  74. package/dist/components/SideBarNavigation/subcomponents/SidebarToggle.d.ts +8 -0
  75. package/dist/components/SideBarNavigation/subcomponents/SidebarToggle.d.ts.map +1 -0
  76. package/dist/components/SideBarNavigation/subcomponents/SidebarToggle.js +40 -0
  77. package/dist/components/Stack/Stack.styles.d.ts +2 -2
  78. package/dist/components/Tabs/Tabs.styles.d.ts +2 -2
  79. package/dist/components/Text/Text.styles.d.ts +2 -2
  80. package/dist/components/Textarea/Textarea.js +3 -3
  81. package/dist/components/Textarea/Textarea.styles.d.ts +1 -1
  82. package/dist/components/Toast/Toast.d.ts.map +1 -1
  83. package/dist/components/Toast/Toast.js +12 -3
  84. package/dist/components/Toast/Toast.types.d.ts +34 -0
  85. package/dist/components/Toast/Toast.types.d.ts.map +1 -1
  86. package/dist/components/Toast/ToastContainer.d.ts +7 -0
  87. package/dist/components/Toast/ToastContainer.d.ts.map +1 -0
  88. package/dist/components/Toast/ToastContainer.js +104 -0
  89. package/dist/components/Toast/index.d.ts +2 -0
  90. package/dist/components/Toast/index.d.ts.map +1 -1
  91. package/dist/components/Toast/toastStore.d.ts +31 -0
  92. package/dist/components/Toast/toastStore.d.ts.map +1 -0
  93. package/dist/components/Toast/toastStore.js +104 -0
  94. package/dist/index.d.ts +2 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +16 -1
  97. package/dist/providers/TimmbrConfigProvider.d.ts +20 -2
  98. package/dist/providers/TimmbrConfigProvider.d.ts.map +1 -1
  99. package/dist/providers/TimmbrConfigProvider.js +38 -3
  100. package/package.json +14 -6
package/README.md CHANGED
@@ -67,12 +67,12 @@ export default function RootLayout({ children }: { children: React.ReactNode })
67
67
 
68
68
  | Category | Components | Description |
69
69
  | :--- | :--- | :--- |
70
- | **Actions** | `Button` | Accessible button with adornment icons, loading spinner, CVA variants, and `asChild` composition. |
70
+ | **Actions** | `Button`, `LinkButton` | Accessible buttons and anchor links with `next/link` default SPA routing, adornment icons, loading spinners, and cross-zone delegation. |
71
71
  | **Typography** | `Text`, `Heading` | Typography primitives restricted to semantic elements (`<p>`, `<span>`, `<h1>`–`<h6>`) with design token scaling. |
72
72
  | **Surfaces** | `Card`, `Avatar`, `Badge`, `Chip`, `Divider` | Display primitives including user avatars, status pills, elevated cards, interactive chips, and separators. |
73
73
  | **Layout** | `Container`, `Stack`, `Inline`, `Grid`, `Center` | Responsive breakpoint containers, flex distribution, responsive CSS grid, and centered wrappers. |
74
74
  | **Form Controls** | `Input`, `Textarea`, `Select`, `Checkbox`, `Radio`, `Switch`, `RangeSlider`, `FormField`, `Label` | Comprehensive form primitives with labels, helper text, validation error states, and dual-thumb sliders. |
75
- | **Navigation** | `Tabs`, `Pagination` | Radix-driven accessible tabs with spring-physics glider indicator and multi-page pagination. |
75
+ | **Navigation** | `Tabs`, `Pagination`, `SideBarNavigation` | Accessible tabs, multi-page pagination, and responsive collapsable sidebar navigation. |
76
76
  | **Feedback** | `Alert`, `Progress`, `Skeleton`, `Spinner`, `Toast` | Status banners, determinate/indeterminate progress, shimmer loaders, and card-deck multi-toasts with hover-spread. |
77
77
  | **Overlays** | `Dialog`, `Drawer`, `Dropdown`, `Popover`, `Tooltip` | Centered dialogs, off-canvas sliding drawers, contextual dropdowns, interactive popovers, and accessible tooltips. |
78
78
  | **Data Display** | `Table`, `DataList`, `Stat`, `EmptyState` | Structured data tables with pagination, key-value data lists, KPI metrics, and empty state placeholders. |
@@ -82,24 +82,28 @@ export default function RootLayout({ children }: { children: React.ReactNode })
82
82
  ## Component Usage
83
83
 
84
84
 
85
- ### Button
85
+ ### Button & LinkButton
86
86
 
87
87
  ```tsx
88
- import { Button } from '@timmbr/ui';
89
- import Link from 'next/link';
88
+ import { Button, LinkButton } from '@timmbr/ui';
90
89
 
91
- export function Example() {
90
+ export function NavigationExample() {
92
91
  return (
93
- <div className="flex gap-4">
94
- {/* Standard variants */}
95
- <Button variant="default">Primary</Button>
96
- <Button variant="outline">Outline</Button>
97
- <Button variant="ghost">Ghost</Button>
98
-
99
- {/* Composition with Next.js Link via asChild */}
100
- <Button asChild>
101
- <Link href="/dashboard">Go to Dashboard</Link>
102
- </Button>
92
+ <div className="flex gap-4 items-center">
93
+ {/* 1. Internal same-zone navigation: uses next/link by default, styled as a link */}
94
+ <LinkButton href="/dashboard">
95
+ Go to Dashboard
96
+ </LinkButton>
97
+
98
+ {/* 2. Button visual appearance if desired */}
99
+ <LinkButton href="/signup" variant="primary">
100
+ Sign Up
101
+ </LinkButton>
102
+
103
+ {/* 3. Cross-zone boundary: NEVER use next/link across zones. Always use crossZone */}
104
+ <LinkButton href="/docs" crossZone>
105
+ Documentation (Cross-Zone)
106
+ </LinkButton>
103
107
  </div>
104
108
  );
105
109
  }
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const avatarRootVariants: (props?: ({
3
3
  size?: "sm" | "lg" | "md" | "xl" | "2xl" | "xs" | null | undefined;
4
- variant?: "outline" | "grey" | "subtle" | "primary" | "secondary" | null | undefined;
4
+ variant?: "grey" | "outline" | "subtle" | "primary" | "secondary" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  export declare const avatarStatusVariants: (props?: ({
7
7
  status?: "online" | "offline" | "busy" | "away" | null | undefined;
@@ -1,6 +1,6 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const badgeVariants: (props?: ({
3
- variant?: "default" | "outline" | "subtle" | "primary" | "secondary" | "destructive" | "success" | "brand" | null | undefined;
3
+ variant?: "default" | "destructive" | "success" | "outline" | "subtle" | "primary" | "secondary" | "brand" | null | undefined;
4
4
  size?: "sm" | "lg" | "md" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  export type BadgeVariants = VariantProps<typeof badgeVariants>;
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const buttonVariants: (props?: ({
3
- variant?: "link" | "default" | "outline" | "ghost" | "primary" | "secondary" | "destructive" | null | undefined;
4
- size?: "default" | "sm" | "lg" | "icon" | "md" | null | undefined;
3
+ variant?: "link" | "default" | "destructive" | "outline" | "ghost" | "primary" | "secondary" | null | undefined;
4
+ size?: "default" | "icon" | "sm" | "lg" | "md" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  export type ButtonVariants = VariantProps<typeof buttonVariants>;
7
7
  //# sourceMappingURL=Button.styles.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const chipVariants: (props?: ({
3
3
  variant?: "filled" | "subtle" | "outlined" | null | undefined;
4
- color?: "default" | "primary" | "destructive" | "success" | "warning" | null | undefined;
4
+ color?: "default" | "destructive" | "success" | "warning" | "primary" | null | undefined;
5
5
  size?: "default" | "sm" | "lg" | null | undefined;
6
6
  clickable?: boolean | null | undefined;
7
7
  selected?: boolean | null | undefined;
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import type { CrossZoneLinkProps } from './CrossZoneLink.types';
3
+ /**
4
+ * CrossZoneLink forces hard browser-level navigation across Next.js multi-zone / microfrontend boundaries.
5
+ *
6
+ * ARCHITECTURAL RULE:
7
+ * NEVER use `next/link` for navigation across zone boundaries. Always use `CrossZoneLink` from `@timmbr/ui`.
8
+ */
9
+ export declare const CrossZoneLink: React.ForwardRefExoticComponent<CrossZoneLinkProps & React.RefAttributes<HTMLAnchorElement>>;
10
+ //# sourceMappingURL=CrossZoneLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CrossZoneLink.d.ts","sourceRoot":"","sources":["../../../src/components/CrossZoneLink/CrossZoneLink.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,8FA4GzB,CAAC"}
@@ -0,0 +1,111 @@
1
+ "use client";
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { Slot } from '@radix-ui/react-slot';
5
+ import { cn } from '@timmbr/utils';
6
+ import { crossZoneLinkVariants } from './CrossZoneLink.styles.js';
7
+ import { useTimmbrConfig, resolveZoneHref } from '../../providers/TimmbrConfigProvider.js';
8
+
9
+ const CrossZoneLink = React.forwardRef(
10
+ ({
11
+ className,
12
+ style,
13
+ variant,
14
+ size,
15
+ asChild = false,
16
+ href,
17
+ zone,
18
+ disabled = false,
19
+ leftIcon,
20
+ rightIcon,
21
+ target,
22
+ rel,
23
+ onClick,
24
+ children,
25
+ ...props
26
+ }, ref) => {
27
+ const config = useTimmbrConfig();
28
+ const resolvedHref = resolveZoneHref(href, zone, config.zones?.zones);
29
+ const isExternal = resolvedHref.startsWith("http://") || resolvedHref.startsWith("https://") || target === "_blank";
30
+ const resolvedRel = rel ?? (isExternal ? "noopener noreferrer" : void 0);
31
+ const handleClick = (e) => {
32
+ if (disabled) {
33
+ e.preventDefault();
34
+ e.stopPropagation();
35
+ return;
36
+ }
37
+ onClick?.(e);
38
+ };
39
+ if (asChild) {
40
+ return /* @__PURE__ */ jsx(
41
+ Slot,
42
+ {
43
+ ref,
44
+ "data-slot": "cross-zone-link",
45
+ "data-cross-zone": "true",
46
+ "aria-disabled": disabled ? "true" : void 0,
47
+ tabIndex: disabled ? -1 : props.tabIndex,
48
+ className: cn(
49
+ crossZoneLinkVariants({
50
+ variant,
51
+ size,
52
+ disabled,
53
+ className
54
+ })
55
+ ),
56
+ style,
57
+ onClick: handleClick,
58
+ ...props,
59
+ children
60
+ }
61
+ );
62
+ }
63
+ return /* @__PURE__ */ jsxs(
64
+ "a",
65
+ {
66
+ ref,
67
+ href: disabled ? void 0 : resolvedHref,
68
+ target,
69
+ rel: resolvedRel,
70
+ "data-slot": "cross-zone-link",
71
+ "data-cross-zone": "true",
72
+ "data-testid": "timmbr-cross-zone-link",
73
+ "aria-disabled": disabled ? "true" : void 0,
74
+ tabIndex: disabled ? -1 : props.tabIndex,
75
+ style,
76
+ onClick: handleClick,
77
+ className: cn(
78
+ crossZoneLinkVariants({
79
+ variant,
80
+ size,
81
+ disabled,
82
+ className
83
+ })
84
+ ),
85
+ ...props,
86
+ children: [
87
+ leftIcon && /* @__PURE__ */ jsx(
88
+ "span",
89
+ {
90
+ className: "inline-flex shrink-0 items-center justify-center pointer-events-none",
91
+ "data-slot": "cross-zone-link-left-icon",
92
+ children: leftIcon
93
+ }
94
+ ),
95
+ children && /* @__PURE__ */ jsx("span", { children }),
96
+ rightIcon && /* @__PURE__ */ jsx(
97
+ "span",
98
+ {
99
+ className: "inline-flex shrink-0 items-center justify-center pointer-events-none",
100
+ "data-slot": "cross-zone-link-right-icon",
101
+ children: rightIcon
102
+ }
103
+ )
104
+ ]
105
+ }
106
+ );
107
+ }
108
+ );
109
+ CrossZoneLink.displayName = "CrossZoneLink";
110
+
111
+ export { CrossZoneLink };
@@ -0,0 +1,8 @@
1
+ import { type VariantProps } from 'class-variance-authority';
2
+ export declare const crossZoneLinkVariants: (props?: ({
3
+ variant?: "default" | "inline" | "subtle" | "primary" | "secondary" | "muted" | null | undefined;
4
+ size?: "default" | "sm" | "lg" | null | undefined;
5
+ disabled?: boolean | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export type CrossZoneLinkVariants = VariantProps<typeof crossZoneLinkVariants>;
8
+ //# sourceMappingURL=CrossZoneLink.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CrossZoneLink.styles.d.ts","sourceRoot":"","sources":["../../../src/components/CrossZoneLink/CrossZoneLink.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,eAAO,MAAM,qBAAqB;;;;8EA4BjC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { cva } from 'class-variance-authority';
2
+
3
+ const crossZoneLinkVariants = cva(
4
+ "inline-flex items-center no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 rounded-[2px] transition-all duration-150 select-none",
5
+ {
6
+ variants: {
7
+ variant: {
8
+ default: "text-primary hover:underline underline-offset-4 font-medium",
9
+ primary: "text-primary font-semibold hover:brightness-110 active:brightness-95",
10
+ secondary: "text-secondary-foreground hover:text-foreground font-medium",
11
+ muted: "text-grey-600 dark:text-grey-400 hover:text-foreground font-normal",
12
+ subtle: "text-foreground/80 hover:text-foreground font-normal",
13
+ inline: "text-primary underline underline-offset-4 hover:opacity-80 inline font-medium"
14
+ },
15
+ size: {
16
+ sm: "text-xs gap-1",
17
+ default: "text-sm gap-1.5",
18
+ lg: "text-base gap-2"
19
+ },
20
+ disabled: {
21
+ true: "pointer-events-none opacity-50 cursor-not-allowed",
22
+ false: "cursor-pointer"
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ variant: "default",
27
+ size: "default",
28
+ disabled: false
29
+ }
30
+ }
31
+ );
32
+
33
+ export { crossZoneLinkVariants };
@@ -0,0 +1,31 @@
1
+ import type * as React from 'react';
2
+ import type { VariantProps } from 'class-variance-authority';
3
+ import type { crossZoneLinkVariants } from './CrossZoneLink.styles';
4
+ export interface CrossZoneLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement>, VariantProps<typeof crossZoneLinkVariants> {
5
+ /**
6
+ * The destination URL or relative path.
7
+ */
8
+ href: string;
9
+ /**
10
+ * Optional zone key corresponding to configured zones in TimmbrConfigProvider.
11
+ * If provided and configured, resolves against the zone's base URL.
12
+ */
13
+ zone?: string;
14
+ /**
15
+ * Merges props onto an immediate child element using Radix UI Slot.
16
+ */
17
+ asChild?: boolean;
18
+ /**
19
+ * Leading icon element rendered before link text.
20
+ */
21
+ leftIcon?: React.ReactNode;
22
+ /**
23
+ * Trailing icon element rendered after link text.
24
+ */
25
+ rightIcon?: React.ReactNode;
26
+ /**
27
+ * Disables user interaction and navigation.
28
+ */
29
+ disabled?: boolean;
30
+ }
31
+ //# sourceMappingURL=CrossZoneLink.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CrossZoneLink.types.d.ts","sourceRoot":"","sources":["../../../src/components/CrossZoneLink/CrossZoneLink.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,WAAW,kBACf,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EACnD,YAAY,CAAC,OAAO,qBAAqB,CAAC;IAC5C;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;OAEG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
@@ -0,0 +1,4 @@
1
+ export { CrossZoneLink } from './CrossZoneLink';
2
+ export { crossZoneLinkVariants, type CrossZoneLinkVariants } from './CrossZoneLink.styles';
3
+ export type { CrossZoneLinkProps } from './CrossZoneLink.types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CrossZoneLink/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC3F,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -3,7 +3,7 @@ export declare const dividerVariants: (props?: ({
3
3
  orientation?: "horizontal" | "vertical" | null | undefined;
4
4
  variant?: "dashed" | "dotted" | "solid" | null | undefined;
5
5
  thickness?: "default" | "thick" | "thin" | null | undefined;
6
- color?: "default" | "primary" | "brand" | "muted" | null | undefined;
6
+ color?: "default" | "primary" | "muted" | "brand" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  export type DividerVariants = VariantProps<typeof dividerVariants>;
9
9
  //# sourceMappingURL=Divider.styles.d.ts.map
@@ -39,12 +39,12 @@ const FormField = React.forwardRef(
39
39
  {
40
40
  id: `${id}-desc`,
41
41
  className: cn(
42
- "text-xs mt-0.5 flex items-center gap-1.5",
42
+ "text-xs mt-0.5 flex items-start gap-1.5",
43
43
  isError ? "text-destructive font-medium" : "text-muted"
44
44
  ),
45
45
  children: [
46
- isError ? /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Info, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }),
47
- /* @__PURE__ */ jsx("span", { children: errorMessage || helperText })
46
+ isError ? /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5 shrink-0 mt-0.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Info, { className: "w-3.5 h-3.5 shrink-0 mt-0.5", "aria-hidden": "true" }),
47
+ /* @__PURE__ */ jsx("span", { className: "leading-tight", children: errorMessage || helperText })
48
48
  ]
49
49
  }
50
50
  )
@@ -2,8 +2,8 @@ import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const headingVariants: (props?: ({
3
3
  level?: 1 | 2 | 3 | 4 | 5 | 6 | null | undefined;
4
4
  font?: "title" | "display" | null | undefined;
5
- foreground?: "default" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "destructive" | "muted" | null | undefined;
6
- weight?: "bold" | "light" | "normal" | "medium" | "semibold" | null | undefined;
5
+ foreground?: "default" | "destructive" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "muted" | null | undefined;
6
+ weight?: "bold" | "normal" | "medium" | "light" | "semibold" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  export type HeadingVariants = VariantProps<typeof headingVariants>;
9
9
  //# sourceMappingURL=Heading.styles.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const iconVariants: (props?: ({
3
3
  size?: "sm" | "lg" | "md" | "xl" | "xs" | null | undefined;
4
- foreground?: "default" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "destructive" | "muted" | null | undefined;
4
+ foreground?: "default" | "destructive" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "muted" | null | undefined;
5
5
  clickable?: boolean | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export type IconVariants = VariantProps<typeof iconVariants>;
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const inlineVariants: (props?: ({
3
- align?: "center" | "end" | "start" | "baseline" | null | undefined;
4
- justify?: "center" | "end" | "start" | "between" | "around" | "evenly" | null | undefined;
3
+ align?: "center" | "start" | "end" | "baseline" | null | undefined;
4
+ justify?: "center" | "start" | "end" | "between" | "around" | "evenly" | null | undefined;
5
5
  wrap?: boolean | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export type InlineVariants = VariantProps<typeof inlineVariants>;
@@ -175,12 +175,12 @@ const Input = React.forwardRef(
175
175
  {
176
176
  id: `${id}-desc`,
177
177
  className: cn(
178
- "text-xs mt-0.5 flex items-center gap-1.5 text-left",
178
+ "text-xs mt-0.5 flex items-start gap-1.5 text-left",
179
179
  isError ? "text-destructive font-medium" : "text-muted-foreground"
180
180
  ),
181
181
  children: [
182
- isError ? /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5 shrink-0 text-destructive", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Info, { className: "w-3.5 h-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
183
- /* @__PURE__ */ jsx("span", { children: errorMessage || helperText })
182
+ isError ? /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5 shrink-0 mt-0.5 text-destructive", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Info, { className: "w-3.5 h-3.5 shrink-0 mt-0.5 text-muted-foreground", "aria-hidden": "true" }),
183
+ /* @__PURE__ */ jsx("span", { className: "leading-tight", children: errorMessage || helperText })
184
184
  ]
185
185
  }
186
186
  )
@@ -1,6 +1,6 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  export declare const inputWrapperVariants: (props?: ({
3
- variant?: "outline" | "filled" | "subtle" | null | undefined;
3
+ variant?: "filled" | "outline" | "subtle" | null | undefined;
4
4
  size?: "default" | "sm" | "lg" | "md" | null | undefined;
5
5
  isError?: boolean | null | undefined;
6
6
  isDisabled?: boolean | null | undefined;
@@ -33,7 +33,7 @@ const inputWrapperVariants = cva(
33
33
  }
34
34
  );
35
35
  const inputFieldVariants = cva(
36
- "w-full h-full bg-transparent font-sans border-0 p-0 text-foreground placeholder:text-muted focus:outline-none focus:ring-0 focus-visible:outline-none disabled:cursor-not-allowed"
36
+ "w-full h-full bg-transparent font-sans border-0 p-0 text-foreground placeholder:text-muted focus:outline-none focus:ring-0 focus-visible:outline-none disabled:cursor-not-allowed [box-shadow:none] [&:-webkit-autofill]:[-webkit-background-clip:text] [&:-webkit-autofill]:[background-clip:text] [&:-webkit-autofill]:[-webkit-text-fill-color:currentColor] [&:-webkit-autofill]:[caret-color:currentColor] [&:-webkit-autofill]:[transition:background-color_5000000s_ease-in-out_0s] [&:-webkit-autofill]:[box-shadow:none] [&:autofill]:[-webkit-background-clip:text] [&:autofill]:[background-clip:text] [&:autofill]:[-webkit-text-fill-color:currentColor] [&:autofill]:[caret-color:currentColor] [&:autofill]:[transition:background-color_5000000s_ease-in-out_0s] [&:autofill]:[box-shadow:none]"
37
37
  );
38
38
 
39
39
  export { inputFieldVariants, inputWrapperVariants };
@@ -41,7 +41,7 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
41
41
  /**
42
42
  * HTML input type (text, password, email, number, search, tel, url, date, etc.).
43
43
  */
44
- type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'date' | 'time' | (string & {});
44
+ type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'date' | 'time' | (string & Record<never, never>);
45
45
  /**
46
46
  * Whether to display an interactive show/hide password toggle button when type="password".
47
47
  * Defaults to true.
@@ -1 +1 @@
1
- {"version":3,"file":"Input.types.d.ts","sourceRoot":"","sources":["../../../src/components/Input/Input.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,EAC/D,aAAa;IACf;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;OAEG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChC;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,IAAI,CAAC,EACD,MAAM,GACN,UAAU,GACV,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,KAAK,GACL,KAAK,GACL,MAAM,GACN,MAAM,GACN,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB"}
1
+ {"version":3,"file":"Input.types.d.ts","sourceRoot":"","sources":["../../../src/components/Input/Input.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,EAC/D,aAAa;IACf;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;OAEG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChC;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,IAAI,CAAC,EACD,MAAM,GACN,UAAU,GACV,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,KAAK,GACL,KAAK,GACL,MAAM,GACN,MAAM,GACN,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACpC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB"}
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ import type { LinkButtonProps } from './LinkButton.types';
3
+ /**
4
+ * LinkButton is a unified navigation component for internal, cross-zone, and external links.
5
+ *
6
+ * NAVIGATION RULES:
7
+ * 1. Internal same-zone navigation: Uses Next.js `next/link` by default for client-side SPA routing and prefetching.
8
+ * 2. Cross-zone boundary navigation: Uses native HTML `<a>` navigation when `crossZone={true}` or when `zone` points
9
+ * to an external zone. NEVER use `next/link` across zone boundaries.
10
+ * 3. Default variant: Styled as a link by default, with button visual styling available via variant props.
11
+ */
12
+ export declare const LinkButton: React.ForwardRefExoticComponent<LinkButtonProps & React.RefAttributes<HTMLAnchorElement>>;
13
+ //# sourceMappingURL=LinkButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkButton.d.ts","sourceRoot":"","sources":["../../../src/components/LinkButton/LinkButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,2FAyMtB,CAAC"}
@@ -0,0 +1,179 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import NextLink from 'next/link';
5
+ import { Slot } from '@radix-ui/react-slot';
6
+ import { cn } from '@timmbr/utils';
7
+ import { SpinnerIcon } from '@timmbr/icons';
8
+ import { resolveMotion } from '../../types/motion.js';
9
+ import { linkButtonVariants } from './LinkButton.styles.js';
10
+ import { useTimmbrConfig, resolveZoneHref } from '../../providers/TimmbrConfigProvider.js';
11
+ import { useGlobalAnimation } from '../../providers/AnimationProvider.js';
12
+
13
+ const LinkButton = React.forwardRef(
14
+ ({
15
+ className,
16
+ style,
17
+ variant,
18
+ size,
19
+ asChild = false,
20
+ motion,
21
+ leftIcon,
22
+ rightIcon,
23
+ loading = false,
24
+ loadingText,
25
+ disabled = false,
26
+ href,
27
+ zone,
28
+ crossZone = false,
29
+ prefetch,
30
+ replace,
31
+ scroll,
32
+ target,
33
+ rel,
34
+ onClick,
35
+ children,
36
+ ...props
37
+ }, ref) => {
38
+ const config = useTimmbrConfig();
39
+ const globalMotion = useGlobalAnimation();
40
+ const resolvedVariant = variant ?? config.components?.button?.defaultVariant ?? "default";
41
+ const resolvedSize = size ?? config.components?.button?.defaultSize ?? "default";
42
+ const { shouldAnimate, motionClass } = resolveMotion(motion, globalMotion);
43
+ const isDisabled = disabled || loading;
44
+ const resolvedHref = href ? resolveZoneHref(href, zone, config.zones?.zones) : void 0;
45
+ const handleClick = (e) => {
46
+ if (isDisabled) {
47
+ e.preventDefault();
48
+ e.stopPropagation();
49
+ return;
50
+ }
51
+ onClick?.(e);
52
+ };
53
+ const buttonClassName = cn(
54
+ linkButtonVariants({
55
+ variant: resolvedVariant,
56
+ size: resolvedSize,
57
+ disabled: isDisabled,
58
+ className
59
+ }),
60
+ !shouldAnimate && "transition-none active:scale-100",
61
+ motionClass
62
+ );
63
+ if (asChild) {
64
+ return /* @__PURE__ */ jsx(
65
+ Slot,
66
+ {
67
+ ref,
68
+ className: buttonClassName,
69
+ style,
70
+ "data-slot": "link-button",
71
+ "aria-disabled": isDisabled ? "true" : void 0,
72
+ tabIndex: isDisabled ? -1 : props.tabIndex,
73
+ onClick: handleClick,
74
+ ...props,
75
+ children
76
+ }
77
+ );
78
+ }
79
+ const content = loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
80
+ /* @__PURE__ */ jsx(SpinnerIcon, { size: resolvedSize === "sm" ? 14 : 18 }),
81
+ loadingText ? /* @__PURE__ */ jsx("span", { children: loadingText }) : children
82
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
83
+ leftIcon && /* @__PURE__ */ jsx(
84
+ "span",
85
+ {
86
+ className: "inline-flex shrink-0 items-center justify-center pointer-events-none",
87
+ "data-slot": "link-button-left-icon",
88
+ children: leftIcon
89
+ }
90
+ ),
91
+ children && /* @__PURE__ */ jsx("span", { children }),
92
+ rightIcon && /* @__PURE__ */ jsx(
93
+ "span",
94
+ {
95
+ className: "inline-flex shrink-0 items-center justify-center pointer-events-none",
96
+ "data-slot": "link-button-right-icon",
97
+ children: rightIcon
98
+ }
99
+ )
100
+ ] });
101
+ if (isDisabled) {
102
+ return /* @__PURE__ */ jsx(
103
+ "a",
104
+ {
105
+ ref,
106
+ "data-slot": "link-button",
107
+ "data-testid": "timmbr-link-button",
108
+ "aria-disabled": "true",
109
+ tabIndex: -1,
110
+ style,
111
+ onClick: handleClick,
112
+ className: buttonClassName,
113
+ ...props,
114
+ children: content
115
+ }
116
+ );
117
+ }
118
+ const isExternal = Boolean(resolvedHref) && (resolvedHref.startsWith("http://") || resolvedHref.startsWith("https://") || resolvedHref.startsWith("mailto:") || resolvedHref.startsWith("tel:") || target === "_blank");
119
+ const isDifferentZone = Boolean(zone) && Boolean(config.zones?.currentZone) && zone !== config.zones?.currentZone;
120
+ const requiresCrossZone = crossZone || isDifferentZone || Boolean(zone && !config.zones?.currentZone);
121
+ const resolvedRel = rel ?? (isExternal ? "noopener noreferrer" : void 0);
122
+ if (requiresCrossZone || isExternal) {
123
+ return /* @__PURE__ */ jsx(
124
+ "a",
125
+ {
126
+ ref,
127
+ href: resolvedHref ?? "#",
128
+ target,
129
+ rel: resolvedRel,
130
+ "data-slot": "link-button",
131
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
132
+ "data-testid": "timmbr-link-button",
133
+ style,
134
+ onClick: handleClick,
135
+ className: buttonClassName,
136
+ ...props,
137
+ children: content
138
+ }
139
+ );
140
+ }
141
+ if (resolvedHref) {
142
+ return /* @__PURE__ */ jsx(
143
+ NextLink,
144
+ {
145
+ ref,
146
+ href: resolvedHref,
147
+ prefetch,
148
+ replace,
149
+ scroll,
150
+ target,
151
+ rel: resolvedRel,
152
+ "data-slot": "link-button",
153
+ "data-testid": "timmbr-link-button",
154
+ style,
155
+ onClick: handleClick,
156
+ className: buttonClassName,
157
+ ...props,
158
+ children: content
159
+ }
160
+ );
161
+ }
162
+ return /* @__PURE__ */ jsx(
163
+ "a",
164
+ {
165
+ ref,
166
+ "data-slot": "link-button",
167
+ "data-testid": "timmbr-link-button",
168
+ style,
169
+ onClick: handleClick,
170
+ className: buttonClassName,
171
+ ...props,
172
+ children: content
173
+ }
174
+ );
175
+ }
176
+ );
177
+ LinkButton.displayName = "LinkButton";
178
+
179
+ export { LinkButton };