@freightos/freightwind 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -0
  3. package/dist/cjs/components/alert.js +33 -16
  4. package/dist/cjs/components/avatar.js +53 -58
  5. package/dist/cjs/components/badge.js +42 -18
  6. package/dist/cjs/components/button.js +35 -24
  7. package/dist/cjs/components/checkbox.js +21 -3
  8. package/dist/cjs/components/chip.js +67 -9
  9. package/dist/cjs/components/message.js +38 -0
  10. package/dist/cjs/components/pop-confirm.js +86 -0
  11. package/dist/cjs/components/radio-button-group.js +31 -35
  12. package/dist/cjs/components/radio-group.js +2 -1
  13. package/dist/cjs/components/slider.js +14 -6
  14. package/dist/cjs/components/switch.js +29 -10
  15. package/dist/cjs/components/tooltip.js +40 -11
  16. package/dist/cjs/index.js +38 -84
  17. package/dist/cjs/lib/icon-utils.js +5 -0
  18. package/dist/cjs/{components/aspect-ratio.js → lib/use-stable-id.js} +7 -4
  19. package/dist/cjs/lib/utils.js +18 -1
  20. package/dist/esm/components/alert.js +34 -17
  21. package/dist/esm/components/avatar.js +53 -22
  22. package/dist/esm/components/badge.js +45 -19
  23. package/dist/esm/components/button.js +36 -25
  24. package/dist/esm/components/checkbox.js +22 -4
  25. package/dist/esm/components/chip.js +34 -9
  26. package/dist/esm/components/message.js +34 -0
  27. package/dist/esm/components/pop-confirm.js +51 -0
  28. package/dist/esm/components/radio-button-group.js +31 -33
  29. package/dist/esm/components/radio-group.js +2 -1
  30. package/dist/esm/components/slider.js +14 -6
  31. package/dist/esm/components/switch.js +30 -11
  32. package/dist/esm/components/tooltip.js +40 -7
  33. package/dist/esm/index.js +18 -66
  34. package/dist/esm/lib/icon-utils.js +1 -0
  35. package/dist/esm/lib/use-stable-id.js +6 -0
  36. package/dist/esm/lib/utils.js +18 -1
  37. package/dist/types/components/alert.d.ts +5 -5
  38. package/dist/types/components/avatar.d.ts +14 -19
  39. package/dist/types/components/badge.d.ts +22 -8
  40. package/dist/types/components/button.d.ts +15 -9
  41. package/dist/types/components/checkbox.d.ts +8 -2
  42. package/dist/types/components/chip.d.ts +14 -8
  43. package/dist/types/components/message.d.ts +14 -0
  44. package/dist/types/components/pop-confirm.d.ts +28 -0
  45. package/dist/types/components/radio-button-group.d.ts +19 -14
  46. package/dist/types/components/slider.d.ts +10 -2
  47. package/dist/types/components/switch.d.ts +7 -6
  48. package/dist/types/components/tooltip.d.ts +9 -6
  49. package/dist/types/index.d.ts +25 -61
  50. package/dist/types/lib/icon-utils.d.ts +1 -0
  51. package/dist/types/lib/use-stable-id.d.ts +1 -0
  52. package/guidelines/Guidelines.md +54 -0
  53. package/guidelines/design-tokens/colors.md +81 -0
  54. package/guidelines/design-tokens/spacing.md +45 -0
  55. package/guidelines/design-tokens/typography.md +50 -0
  56. package/guidelines/overview-components.md +252 -0
  57. package/guidelines/overview-icons.md +52 -0
  58. package/package.json +63 -54
  59. package/tokens.css +409 -0
  60. package/dist/cjs/components/accordion.js +0 -57
  61. package/dist/cjs/components/breadcrumb.js +0 -65
  62. package/dist/cjs/components/calendar.js +0 -106
  63. package/dist/cjs/components/card.js +0 -59
  64. package/dist/cjs/components/chart.js +0 -176
  65. package/dist/cjs/components/collapsible.js +0 -43
  66. package/dist/cjs/components/command.js +0 -73
  67. package/dist/cjs/components/context-menu.js +0 -83
  68. package/dist/cjs/components/country-select.js +0 -155
  69. package/dist/cjs/components/date-picker.js +0 -59
  70. package/dist/cjs/components/date-range-picker.js +0 -59
  71. package/dist/cjs/components/date-time-picker.js +0 -106
  72. package/dist/cjs/components/dialog.js +0 -70
  73. package/dist/cjs/components/drawer.js +0 -68
  74. package/dist/cjs/components/dropdown-menu.js +0 -85
  75. package/dist/cjs/components/empty.js +0 -42
  76. package/dist/cjs/components/file-preview.js +0 -73
  77. package/dist/cjs/components/form.js +0 -106
  78. package/dist/cjs/components/inline-edit.js +0 -83
  79. package/dist/cjs/components/input-group.js +0 -70
  80. package/dist/cjs/components/input-otp.js +0 -58
  81. package/dist/cjs/components/input.js +0 -57
  82. package/dist/cjs/components/label.js +0 -45
  83. package/dist/cjs/components/menubar.js +0 -96
  84. package/dist/cjs/components/navigation-menu.js +0 -68
  85. package/dist/cjs/components/pagination.js +0 -65
  86. package/dist/cjs/components/phone-input.js +0 -218
  87. package/dist/cjs/components/popover.js +0 -49
  88. package/dist/cjs/components/progress.js +0 -43
  89. package/dist/cjs/components/resizable.js +0 -47
  90. package/dist/cjs/components/rich-text-editor.js +0 -152
  91. package/dist/cjs/components/route.js +0 -47
  92. package/dist/cjs/components/scroll-area.js +0 -48
  93. package/dist/cjs/components/select.js +0 -71
  94. package/dist/cjs/components/separator.js +0 -43
  95. package/dist/cjs/components/sheet.js +0 -245
  96. package/dist/cjs/components/skeleton.js +0 -8
  97. package/dist/cjs/components/sonner.js +0 -25
  98. package/dist/cjs/components/spinner.js +0 -25
  99. package/dist/cjs/components/stepper.js +0 -99
  100. package/dist/cjs/components/steps.js +0 -127
  101. package/dist/cjs/components/table.js +0 -66
  102. package/dist/cjs/components/tabs.js +0 -51
  103. package/dist/cjs/components/textarea.js +0 -44
  104. package/dist/cjs/components/time-picker.js +0 -110
  105. package/dist/cjs/components/toast.js +0 -75
  106. package/dist/cjs/components/toaster.js +0 -12
  107. package/dist/cjs/components/toggle-group.js +0 -58
  108. package/dist/cjs/components/toggle.js +0 -62
  109. package/dist/cjs/hooks/use-toast.js +0 -166
  110. package/dist/cjs/lib/countryUtils.js +0 -93
  111. package/dist/esm/components/accordion.js +0 -18
  112. package/dist/esm/components/aspect-ratio.js +0 -3
  113. package/dist/esm/components/breadcrumb.js +0 -23
  114. package/dist/esm/components/calendar.js +0 -70
  115. package/dist/esm/components/card.js +0 -18
  116. package/dist/esm/components/chart.js +0 -135
  117. package/dist/esm/components/collapsible.js +0 -5
  118. package/dist/esm/components/command.js +0 -29
  119. package/dist/esm/components/context-menu.js +0 -33
  120. package/dist/esm/components/country-select.js +0 -118
  121. package/dist/esm/components/date-picker.js +0 -23
  122. package/dist/esm/components/date-range-picker.js +0 -23
  123. package/dist/esm/components/date-time-picker.js +0 -70
  124. package/dist/esm/components/dialog.js +0 -24
  125. package/dist/esm/components/drawer.js +0 -23
  126. package/dist/esm/components/dropdown-menu.js +0 -35
  127. package/dist/esm/components/empty.js +0 -6
  128. package/dist/esm/components/file-preview.js +0 -69
  129. package/dist/esm/components/form.js +0 -63
  130. package/dist/esm/components/inline-edit.js +0 -47
  131. package/dist/esm/components/input-group.js +0 -63
  132. package/dist/esm/components/input-otp.js +0 -19
  133. package/dist/esm/components/input.js +0 -21
  134. package/dist/esm/components/label.js +0 -9
  135. package/dist/esm/components/menubar.js +0 -45
  136. package/dist/esm/components/navigation-menu.js +0 -24
  137. package/dist/esm/components/pagination.js +0 -23
  138. package/dist/esm/components/phone-input.js +0 -181
  139. package/dist/esm/components/popover.js +0 -10
  140. package/dist/esm/components/progress.js +0 -7
  141. package/dist/esm/components/resizable.js +0 -9
  142. package/dist/esm/components/rich-text-editor.js +0 -145
  143. package/dist/esm/components/route.js +0 -11
  144. package/dist/esm/components/scroll-area.js +0 -11
  145. package/dist/esm/components/select.js +0 -26
  146. package/dist/esm/components/separator.js +0 -7
  147. package/dist/esm/components/sheet.js +0 -197
  148. package/dist/esm/components/skeleton.js +0 -6
  149. package/dist/esm/components/sonner.js +0 -22
  150. package/dist/esm/components/spinner.js +0 -21
  151. package/dist/esm/components/stepper.js +0 -57
  152. package/dist/esm/components/steps.js +0 -80
  153. package/dist/esm/components/table.js +0 -22
  154. package/dist/esm/components/tabs.js +0 -12
  155. package/dist/esm/components/textarea.js +0 -8
  156. package/dist/esm/components/time-picker.js +0 -74
  157. package/dist/esm/components/toast.js +0 -33
  158. package/dist/esm/components/toaster.js +0 -9
  159. package/dist/esm/components/toggle-group.js +0 -21
  160. package/dist/esm/components/toggle.js +0 -25
  161. package/dist/esm/hooks/use-toast.js +0 -128
  162. package/dist/esm/lib/countryUtils.js +0 -87
  163. package/dist/styles.css +0 -152
  164. package/dist/types/components/accordion.d.ts +0 -11
  165. package/dist/types/components/aspect-ratio.d.ts +0 -3
  166. package/dist/types/components/breadcrumb.d.ts +0 -19
  167. package/dist/types/components/calendar.d.ts +0 -7
  168. package/dist/types/components/card.d.ts +0 -11
  169. package/dist/types/components/chart.d.ts +0 -66
  170. package/dist/types/components/collapsible.d.ts +0 -5
  171. package/dist/types/components/command.d.ts +0 -80
  172. package/dist/types/components/context-menu.d.ts +0 -27
  173. package/dist/types/components/country-select.d.ts +0 -17
  174. package/dist/types/components/date-picker.d.ts +0 -9
  175. package/dist/types/components/date-range-picker.d.ts +0 -10
  176. package/dist/types/components/date-time-picker.d.ts +0 -10
  177. package/dist/types/components/dialog.d.ts +0 -23
  178. package/dist/types/components/drawer.d.ts +0 -22
  179. package/dist/types/components/dropdown-menu.d.ts +0 -27
  180. package/dist/types/components/empty.d.ts +0 -6
  181. package/dist/types/components/file-preview.d.ts +0 -9
  182. package/dist/types/components/form.d.ts +0 -23
  183. package/dist/types/components/inline-edit.d.ts +0 -10
  184. package/dist/types/components/input-group.d.ts +0 -16
  185. package/dist/types/components/input-otp.d.ts +0 -34
  186. package/dist/types/components/input.d.ts +0 -9
  187. package/dist/types/components/label.d.ts +0 -5
  188. package/dist/types/components/menubar.d.ts +0 -28
  189. package/dist/types/components/navigation-menu.d.ts +0 -12
  190. package/dist/types/components/pagination.d.ts +0 -29
  191. package/dist/types/components/phone-input.d.ts +0 -20
  192. package/dist/types/components/popover.d.ts +0 -9
  193. package/dist/types/components/progress.d.ts +0 -4
  194. package/dist/types/components/resizable.d.ts +0 -23
  195. package/dist/types/components/rich-text-editor.d.ts +0 -8
  196. package/dist/types/components/route.d.ts +0 -10
  197. package/dist/types/components/scroll-area.d.ts +0 -5
  198. package/dist/types/components/select.d.ts +0 -13
  199. package/dist/types/components/separator.d.ts +0 -4
  200. package/dist/types/components/sheet.d.ts +0 -49
  201. package/dist/types/components/skeleton.d.ts +0 -2
  202. package/dist/types/components/sonner.d.ts +0 -4
  203. package/dist/types/components/spinner.d.ts +0 -8
  204. package/dist/types/components/stepper.d.ts +0 -17
  205. package/dist/types/components/steps.d.ts +0 -64
  206. package/dist/types/components/table.d.ts +0 -14
  207. package/dist/types/components/tabs.d.ts +0 -7
  208. package/dist/types/components/textarea.d.ts +0 -3
  209. package/dist/types/components/time-picker.d.ts +0 -10
  210. package/dist/types/components/toast.d.ts +0 -15
  211. package/dist/types/components/toaster.d.ts +0 -1
  212. package/dist/types/components/toggle-group.d.ts +0 -12
  213. package/dist/types/components/toggle.d.ts +0 -12
  214. package/dist/types/hooks/use-toast.d.ts +0 -44
  215. package/dist/types/lib/countryUtils.d.ts +0 -20
  216. package/tailwind-preset.js +0 -70
@@ -1,37 +1,68 @@
1
+ 'use client';
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
3
- import * as AvatarPrimitive from '@radix-ui/react-avatar';
3
+ import { IconUser } from '@freightos/icons';
4
4
  import { cva } from 'class-variance-authority';
5
- import * as React from 'react';
6
- const avatarVariants = cva('relative flex shrink-0 overflow-hidden rounded-full', {
5
+ import { iconMap } from '../lib/icon-utils';
6
+ const FLAGS_BASE_URL = 'https://freightwind.freightos.com/flags/1x1';
7
+ // --- Component ---
8
+ const avatarVariants = cva('relative flex shrink-0 items-center justify-center overflow-hidden rounded-full', {
7
9
  variants: {
10
+ variant: {
11
+ default: 'bg-fds-purple-2 text-fds-purple-1',
12
+ info: 'bg-fds-blue-20 text-fds-blue-40',
13
+ secondary: 'bg-fds-blue-10 text-fds-blue-40',
14
+ warning: 'bg-fds-yellow-20 text-fds-yellow-50',
15
+ neutral: 'bg-fds-gray-20 text-fds-gray-80',
16
+ positive: 'bg-fds-green-20 text-fds-green-40',
17
+ filled: 'bg-white dark:bg-fds-gray-90 text-fds-gray-80',
18
+ ghost: 'bg-transparent text-fds-gray-80',
19
+ },
8
20
  size: {
9
- xs: 'h-[16px] w-[16px]',
10
- sm: 'h-[24px] w-[24px]',
11
- md: 'h-[32px] w-[32px]',
12
- lg: 'h-[40px] w-[40px]',
13
- xl: 'h-[58px] w-[58px]',
21
+ xs: 'h-[16px] w-[16px] text-fds-xs',
22
+ sm: 'h-[24px] w-[24px] text-fds-sm',
23
+ md: 'h-[32px] w-[32px] text-fds-base',
24
+ lg: 'h-[40px] w-[40px] text-fds-h6',
25
+ xl: 'h-[58px] w-[58px] text-fds-h4',
14
26
  },
15
27
  bordered: {
16
- true: 'border-fds-sm border-fds-gray-20',
28
+ true: 'border border-fds-gray-20',
17
29
  false: '',
18
30
  },
19
31
  },
20
32
  defaultVariants: {
33
+ variant: 'default',
21
34
  size: 'md',
22
35
  bordered: false,
23
36
  },
24
37
  });
25
- const Avatar = React.forwardRef(({ className, size = 'md', bordered = false, type = 'default', country, ...props }, ref) => {
26
- if (type === 'flag' && country) {
27
- const flagUrl = `https://festatic.freightos.com/flags/1x1/${country.toLowerCase()}.svg`;
28
- return (_jsx("div", { className: cn(avatarVariants({ size, bordered }), className), children: _jsx("img", { src: flagUrl, alt: country, className: "h-full w-full object-cover" }) }));
38
+ const iconSizeMap = {
39
+ xs: 10,
40
+ sm: 14,
41
+ md: 18,
42
+ lg: 22,
43
+ xl: 28,
44
+ };
45
+ function Avatar({ variant = 'default', size = 'md', bordered = false, icon, title, flag, src, alt, }) {
46
+ const classes = avatarVariants({ variant, size, bordered });
47
+ const iconSize = iconSizeMap[size];
48
+ // Content priority: src > flag > title > icon > default placeholder
49
+ if (src) {
50
+ return (_jsx("div", { className: classes, children: _jsx("img", { src: src, alt: alt ?? '', className: "h-full w-full object-cover" }) }));
29
51
  }
30
- return (_jsx(AvatarPrimitive.Root, { ref: ref, className: cn(avatarVariants({ size, bordered }), className), ...props }));
31
- });
32
- Avatar.displayName = AvatarPrimitive.Root.displayName;
33
- const AvatarImage = React.forwardRef(({ className, ...props }, ref) => (_jsx(AvatarPrimitive.Image, { ref: ref, className: cn('aspect-square h-full w-full', className), ...props })));
34
- AvatarImage.displayName = AvatarPrimitive.Image.displayName;
35
- const AvatarFallback = React.forwardRef(({ className, ...props }, ref) => (_jsx(AvatarPrimitive.Fallback, { ref: ref, className: cn('flex h-full w-full items-center justify-center rounded-full bg-muted', className), ...props })));
36
- AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
37
- export { Avatar, AvatarFallback, AvatarImage };
52
+ if (flag) {
53
+ const flagUrl = `${FLAGS_BASE_URL}/${flag.trim().toLowerCase()}.svg`;
54
+ return (_jsx("div", { className: classes, children: _jsx("img", { src: flagUrl, alt: alt ?? flag.toUpperCase(), className: "h-full w-full object-cover" }) }));
55
+ }
56
+ if (title) {
57
+ return (_jsx("div", { className: classes, role: "img", "aria-label": alt ?? title, children: _jsx("span", { className: "font-fds-semibold leading-none", "aria-hidden": "true", children: title.charAt(0).toUpperCase() }) }));
58
+ }
59
+ if (icon) {
60
+ const IconComp = iconMap[icon];
61
+ if (IconComp) {
62
+ return (_jsx("div", { className: classes, role: "img", "aria-label": alt ?? icon, children: _jsx(IconComp, { size: iconSize }) }));
63
+ }
64
+ }
65
+ // Default: user icon placeholder
66
+ return (_jsx("div", { className: classes, role: "img", "aria-label": alt ?? 'User avatar', children: _jsx(IconUser, { size: iconSize }) }));
67
+ }
68
+ export { Avatar };
@@ -1,20 +1,46 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cva } from "class-variance-authority";
3
- import { cn } from "../lib/utils";
4
- const badgeVariants = cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
5
- variants: {
6
- variant: {
7
- default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
8
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
9
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
10
- outline: "text-foreground",
11
- },
12
- },
13
- defaultVariants: {
14
- variant: "default",
15
- },
16
- });
17
- function Badge({ className, variant, ...props }) {
18
- return (_jsx("div", { className: cn(badgeVariants({ variant }), className), ...props }));
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from '../lib/utils';
4
+ const positionClasses = {
5
+ 'top-end': '-top-1 -end-1',
6
+ 'bottom-end': '-bottom-1 -end-1',
7
+ };
8
+ const statusDotColors = {
9
+ success: 'bg-fds-green-30',
10
+ error: 'bg-fds-red-30',
11
+ default: 'bg-fds-gray-40',
12
+ processing: 'bg-fds-blue-30',
13
+ warning: 'bg-fds-yellow-30',
14
+ };
15
+ const badgeSizeClasses = {
16
+ default: 'min-w-[16px] h-[16px] text-fds-sm px-[3px]',
17
+ large: 'min-w-[20px] h-[20px] text-fds-sm px-[4px]',
18
+ };
19
+ const variantClasses = {
20
+ default: 'bg-fds-red-30 text-white',
21
+ info: 'bg-fds-blue-30 text-white',
22
+ neutral: 'bg-white border border-fds-gray-20 text-fds-gray-80',
23
+ };
24
+ function Badge({ value, size = 'default', variant = 'default', position = 'top-end', status, text, children }) {
25
+ const badgeClasses = cn('inline-flex items-center justify-center rounded-full font-fds-semibold leading-none', badgeSizeClasses[size], variantClasses[variant]);
26
+ const display = typeof value === 'number'
27
+ ? (value > 99 ? '99+' : String(value))
28
+ : value;
29
+ const showValue = typeof value === 'string' ? value.length > 0 : (value != null && value > 0);
30
+ // Badge wrapping a child (e.g. icon or button)
31
+ if (children != null && value != null) {
32
+ return (_jsxs("div", { className: "relative inline-flex", children: [children, showValue && (_jsx("span", { className: cn(badgeClasses, 'absolute', positionClasses[position]), children: display }))] }));
33
+ }
34
+ // Standalone badge
35
+ if (value != null) {
36
+ if (!showValue)
37
+ return null;
38
+ return (_jsx("span", { className: badgeClasses, children: display }));
39
+ }
40
+ // Status badge
41
+ if (status) {
42
+ return (_jsxs("span", { className: "inline-flex items-center gap-fds-sm", children: [_jsx("span", { className: cn('h-[6px] w-[6px] rounded-full', statusDotColors[status]) }), text && _jsx("span", { className: "text-fds-base text-fds-gray-80", children: text })] }));
43
+ }
44
+ return null;
19
45
  }
20
- export { Badge, badgeVariants };
46
+ export { Badge };
@@ -1,39 +1,50 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { cva } from 'class-variance-authority';
3
4
  import * as React from 'react';
5
+ import { iconMap } from '../lib/icon-utils';
4
6
  import { cn } from '../lib/utils';
5
- const buttonVariants = cva('inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--fds-border-radius-md)] font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', {
7
+ import { Tooltip } from './tooltip';
8
+ const buttonVariants = cva('fw-base inline-flex w-fit items-center justify-center gap-fds-sm whitespace-nowrap rounded-fds-md cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', {
6
9
  variants: {
7
- type: {
8
- default: 'bg-fds-blue !text-fds-white hover:bg-fds-blue-40',
9
- secondary: 'bg-fds-white dark:bg-background border border-fds-blue !text-fds-blue hover:bg-fds-blue-10 dark:hover:bg-fds-gray-80',
10
- tertiary: 'bg-fds-white dark:bg-fds-gray-90 !text-fds-blue hover:bg-fds-blue-10 dark:hover:bg-fds-gray-80',
11
- text: 'bg-transparent !text-fds-blue hover:!text-fds-blue-50',
12
- danger: 'bg-fds-red hover:bg-fds-red/90 !text-white',
10
+ variant: {
11
+ default: 'bg-fds-blue-30 text-fds-white hover:bg-fds-blue-40',
12
+ secondary: 'bg-fds-white border border-fds-blue-30 text-fds-blue-30 hover:bg-fds-blue-10',
13
+ tertiary: 'bg-transparent text-fds-blue-30 hover:bg-fds-blue-10',
14
+ link: 'bg-transparent text-fds-blue-30 hover:text-fds-blue-50',
15
+ danger: 'bg-fds-red-30 text-fds-white hover:bg-fds-red-40',
16
+ 'danger-ghost': 'bg-fds-white border border-fds-red-30 text-fds-red-30 hover:bg-fds-red-10',
17
+ toggle: 'bg-fds-white border border-fds-gray-30 text-fds-gray-80 hover:border-fds-blue-30 hover:text-fds-blue-30',
13
18
  },
14
19
  size: {
15
- small: 'h-[var(--fds-size-lg)] text-fds-sm',
16
- medium: 'h-[var(--fds-size-xl)] text-fds-base',
17
- large: 'h-[var(--fds-size-xxl)] text-fds-base',
18
- },
19
- padding: {
20
- default: 'px-fds-lg',
21
- small: 'px-fds-md',
22
- large: 'px-fds-xl',
20
+ sm: 'h-[var(--fds-size-lg)] px-fds-md text-fds-sm [&_svg]:size-3',
21
+ md: 'h-[var(--fds-size-xl)] px-fds-lg text-fds-base [&_svg]:size-4',
22
+ lg: 'h-[var(--fds-size-xxl)] px-fds-xl text-fds-base [&_svg]:size-4',
23
23
  },
24
24
  },
25
25
  defaultVariants: {
26
- type: 'default',
27
- size: 'medium',
28
- padding: 'default',
26
+ variant: 'default',
27
+ size: 'md',
29
28
  },
30
29
  });
31
- const Button = React.forwardRef(
32
- // eslint-disable-next-line unused-imports/no-unused-vars
33
- ({ className, type, size, icon = false, asChild = false, htmlType, ...props }, ref) => {
34
- // Determine padding variant based on size, or none if icon
35
- const paddingVariant = icon ? undefined : (size === 'small' ? 'small' : size === 'large' ? 'large' : 'default');
36
- return (_jsx("button", { type: htmlType, className: cn(buttonVariants({ type, size, padding: paddingVariant }), icon && 'w-[var(--fds-size-xl)] aspect-square', icon && size === 'small' && 'w-[var(--fds-size-lg)]', icon && size === 'large' && 'w-[var(--fds-size-xxl)]', className), ref: ref, ...props }));
30
+ function LoadingSpinner() {
31
+ return (_jsx("svg", { className: "animate-spin", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z", fill: "currentColor" }) }));
32
+ }
33
+ const Button = React.forwardRef(({ variant = 'default', size = 'md', icon, loading = false, active, disabled, tooltip, fullWidth, children, ...props }, ref) => {
34
+ const iconOnly = !!icon && !children && !loading;
35
+ if (process.env.NODE_ENV !== 'production' && iconOnly && !tooltip) {
36
+ console.warn('Button: tooltip is required for icon-only buttons.');
37
+ }
38
+ const IconComp = icon ? iconMap[icon] : null;
39
+ // Icon sizes: with text (sm=12, md=16, lg=16), icon-only (sm=16, md=16, lg=24)
40
+ const iconSize = iconOnly
41
+ ? size === 'lg' ? 24 : 16
42
+ : size === 'sm' ? 12 : 16;
43
+ const button = (_jsxs("button", { className: cn(buttonVariants({ variant, size }), variant === 'toggle' && active && 'border-fds-blue-30 text-fds-blue-30 bg-fds-blue-10', iconOnly && '!px-0', iconOnly && size === 'sm' && 'w-[var(--fds-size-lg)]', iconOnly && size === 'md' && 'w-[var(--fds-size-xl)]', iconOnly && size === 'lg' && 'w-[var(--fds-size-xxl)]', fullWidth && 'w-full'), ref: ref, disabled: disabled || loading, "aria-busy": loading || undefined, "aria-label": iconOnly ? tooltip : undefined, "aria-pressed": variant === 'toggle' ? active : undefined, ...props, children: [loading ? _jsx(LoadingSpinner, {}) : IconComp && _jsx(IconComp, { size: iconSize }), children] }));
44
+ if (tooltip) {
45
+ return _jsx(Tooltip, { message: tooltip, children: button });
46
+ }
47
+ return button;
37
48
  });
38
49
  Button.displayName = 'Button';
39
50
  export { Button, buttonVariants };
@@ -1,8 +1,26 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
3
4
  import * as React from 'react';
4
- import { IconCheck } from '@freightos/icons';
5
5
  import { cn } from '../lib/utils';
6
- const Checkbox = React.forwardRef(({ className, ...props }, ref) => (_jsx(CheckboxPrimitive.Root, { ref: ref, className: cn('peer h-4 w-4 shrink-0 rounded-fds-md border border-fds-gray-40 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground', className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { className: cn('flex items-center justify-center text-current'), children: _jsx(IconCheck, { className: "h-4 w-4" }) }) })));
7
- Checkbox.displayName = CheckboxPrimitive.Root.displayName;
6
+ import { useStableId } from '../lib/use-stable-id';
7
+ const Checkbox = React.forwardRef(({ checked, onCheckedChange, disabled, error, children, }, ref) => {
8
+ const checkboxId = useStableId();
9
+ const [internalChecked, setInternalChecked] = React.useState(false);
10
+ const resolvedChecked = checked !== undefined ? checked : internalChecked;
11
+ const handleCheckedChange = (val) => {
12
+ if (checked === undefined)
13
+ setInternalChecked(val);
14
+ onCheckedChange?.(val);
15
+ };
16
+ const box = (_jsx(CheckboxPrimitive.Root, { ref: ref, id: checkboxId, checked: resolvedChecked, onCheckedChange: handleCheckedChange, disabled: disabled, "aria-invalid": error || undefined, className: cn('fw-base peer size-4 shrink-0 rounded-fds-sm border cursor-pointer transition-colors', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', disabled
17
+ ? 'cursor-not-allowed bg-fds-gray-10 border-fds-gray-20'
18
+ : error
19
+ ? 'border-fds-red-30 bg-fds-red-10 data-[state=checked]:bg-fds-red-30'
20
+ : 'border-fds-gray-30 data-[state=checked]:bg-fds-blue-30 data-[state=checked]:border-fds-blue-30'), children: _jsx(CheckboxPrimitive.Indicator, { className: cn('flex items-center justify-center', disabled ? 'text-fds-gray-60' : error && resolvedChecked !== true ? 'text-fds-red-30' : 'text-fds-white'), children: resolvedChecked === 'indeterminate' ? (_jsx("span", { className: cn('block size-2 rounded-[1px]', disabled ? 'bg-fds-gray-60' : error ? 'bg-fds-red-30' : 'bg-fds-blue-30') })) : (_jsx("svg", { width: "11", height: "9", viewBox: "0 0 11 9", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M3.96757 5.24179L1.53586 2.81007L0 4.3378L3.96742 8.30523L10.7609 1.51176L9.21392 0L3.96757 5.24179Z", fill: "currentColor" }) })) }) }));
21
+ if (!children)
22
+ return box;
23
+ return (_jsxs("div", { className: "flex items-center gap-fds-sm", children: [box, _jsx("label", { htmlFor: checkboxId, className: cn('text-fds-base cursor-pointer select-none', disabled && 'cursor-not-allowed text-fds-gray-60', !disabled && error && 'text-fds-red-30', !disabled && !error && 'text-fds-gray-80 dark:text-fds-gray-10'), children: children })] }));
24
+ });
25
+ Checkbox.displayName = 'Checkbox';
8
26
  export { Checkbox };
@@ -1,22 +1,47 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { cva } from 'class-variance-authority';
4
+ import * as React from 'react';
3
5
  import { IconClose } from '@freightos/icons';
6
+ import { iconMap } from '../lib/icon-utils';
4
7
  import { cn } from '../lib/utils';
5
- const chipVariants = cva('inline-flex items-center rounded-full h-5 px-2.5 py-0.5 !text-fds-sm font-fds-regular transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', {
8
+ const chipVariants = cva('inline-flex items-center rounded-full h-5 px-fds-sm text-fds-sm font-fds-regular transition-colors', {
6
9
  variants: {
7
- type: {
8
- default: 'bg-fds-purple-10 text-fds-purple',
10
+ variant: {
11
+ default: 'bg-fds-purple-2 text-fds-purple-1',
9
12
  neutral: 'bg-fds-gray-20 text-fds-gray-80',
10
- secondary: 'bg-fds-blue-10 text-fds-blue-40',
11
- warning: 'bg-fds-yellow-20 text-fds-yellow-40',
12
- positive: 'bg-fds-green-20 text-fds-green-40',
13
+ info: 'bg-[rgba(225,245,249,1)] text-fds-blue-40',
14
+ highlight: 'bg-[rgba(172,232,240,1)] text-fds-blue-40',
15
+ warning: 'bg-fds-yellow-20 text-fds-yellow-50',
16
+ success: 'bg-fds-green-20 text-fds-green-40',
17
+ notice: 'bg-fds-red-10 text-fds-red-30',
18
+ error: 'bg-fds-red-20 text-fds-red-40',
13
19
  },
14
20
  },
15
21
  defaultVariants: {
16
- type: 'default',
22
+ variant: 'default',
17
23
  },
18
24
  });
19
- function Chip({ className, type, onClose, children, ...props }) {
20
- return (_jsxs("div", { className: cn(chipVariants({ type }), className), ...props, children: [children, onClose && (_jsx("span", { onClick: onClose, className: "ms-fds-sm inline-flex cursor-pointer items-center hover:opacity-70", children: _jsx(IconClose, { size: "10px" }) }))] }));
25
+ const badgeColors = {
26
+ default: 'bg-fds-purple-1 text-fds-white',
27
+ neutral: 'bg-fds-gray-80 text-fds-white',
28
+ info: 'bg-fds-blue-40 text-fds-white',
29
+ highlight: 'bg-fds-blue-40 text-fds-white',
30
+ warning: 'bg-fds-yellow-50 text-fds-white',
31
+ success: 'bg-fds-green-40 text-fds-white',
32
+ notice: 'bg-fds-red-30 text-fds-white',
33
+ error: 'bg-fds-red-40 text-fds-white',
34
+ };
35
+ function Chip({ variant = 'default', icon, closable, onClose, badge, children, }) {
36
+ const [visible, setVisible] = React.useState(true);
37
+ if (!visible)
38
+ return null;
39
+ const handleClose = () => {
40
+ setVisible(false);
41
+ onClose?.();
42
+ };
43
+ const IconComp = icon ? iconMap[icon] : null;
44
+ const iconOnly = !!icon && !children;
45
+ return (_jsxs("div", { className: cn(chipVariants({ variant }), iconOnly && 'justify-center', badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && '!pl-0.5'), children: [badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && (_jsx("span", { className: cn('mr-1 inline-flex items-center justify-center rounded-full min-w-[16px] h-4 px-1 text-[10px] font-fds-semibold leading-none', badgeColors[variant]), children: typeof badge === 'number' && badge > 99 ? '99+' : badge })), IconComp && _jsx(IconComp, { size: 12, className: children ? 'mr-fds-sm' : '' }), children, closable && (_jsx("button", { type: "button", onClick: handleClose, "aria-label": "Remove", className: "fw-base ml-fds-sm inline-flex cursor-pointer items-center hover:opacity-70", children: _jsx(IconClose, { size: 10 }) }))] }));
21
46
  }
22
47
  export { Chip, chipVariants };
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { IconCheckCircled, IconInfoCircled, IconNegativeCircled, IconRisk } from "@freightos/icons";
4
+ import { toast, Toaster as Sonner } from "sonner";
5
+ const messageConfig = {
6
+ success: { icon: IconCheckCircled, bg: "bg-[rgba(71,169,110,1)]" },
7
+ info: { icon: IconInfoCircled, bg: "bg-[rgba(32,117,189,1)]" },
8
+ warning: { icon: IconRisk, bg: "bg-[rgba(237,175,7,1)]" },
9
+ error: { icon: IconNegativeCircled, bg: "bg-[rgba(216,39,30,1)]" },
10
+ };
11
+ function MessageToast({ variant, title, onClick, toastId, }) {
12
+ const config = messageConfig[variant];
13
+ const Icon = config.icon;
14
+ const dir = document.documentElement.getAttribute("dir") || "ltr";
15
+ return (_jsxs("div", { dir: dir, className: `mx-auto flex w-fit items-center gap-fds-sm rounded-fds-md p-fds-lg shadow-fds-md text-white font-[family-name:var(--font-open-sans)] ${config.bg} ${onClick ? "cursor-pointer" : ""}`, onClick: onClick ? () => {
16
+ onClick();
17
+ toast.dismiss(toastId);
18
+ } : undefined, children: [_jsx(Icon, { size: 16, className: "shrink-0" }), _jsx("span", { className: "text-fds-h6 font-fds-regular leading-fds-body", children: title })] }));
19
+ }
20
+ function showMessage(variant, title, options) {
21
+ return toast.custom((id) => (_jsx(MessageToast, { variant: variant, title: title, onClick: options?.onClick, toastId: id })), {
22
+ ...(options?.duration !== undefined && { duration: options.duration }),
23
+ });
24
+ }
25
+ const message = {
26
+ success: (title, options) => showMessage("success", title, options),
27
+ info: (title, options) => showMessage("info", title, options),
28
+ warning: (title, options) => showMessage("warning", title, options),
29
+ error: (title, options) => showMessage("error", title, options),
30
+ };
31
+ const MessageProvider = ({ duration = 3000, ...props }) => {
32
+ return (_jsx(Sonner, { position: "top-center", duration: duration, gap: 8, toastOptions: { style: { left: 0, right: 0 } }, ...props }));
33
+ };
34
+ export { MessageProvider, message };
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
4
+ import * as React from 'react';
5
+ import { Button } from './button';
6
+ import { iconMap } from '../lib/icon-utils';
7
+ function resolvePlacement(placement) {
8
+ const [sideRaw, alignRaw] = placement.split('-');
9
+ const align = alignRaw ?? 'center';
10
+ const isRtl = typeof document !== 'undefined' && document.documentElement.dir === 'rtl';
11
+ const resolveLogical = (value) => (value === 'start') !== isRtl ? 'left' : 'right';
12
+ const side = sideRaw === 'top' || sideRaw === 'bottom'
13
+ ? sideRaw
14
+ : resolveLogical(sideRaw);
15
+ const radixAlign = align === 'center'
16
+ ? 'center'
17
+ : isRtl
18
+ ? align === 'start' ? 'end' : 'start'
19
+ : align;
20
+ return { side, align: radixAlign };
21
+ }
22
+ const ARROW_EDGE_INSET = 10;
23
+ function PopConfirm({ title, onConfirm, onCancel, placement = 'top', children, slotProps, }) {
24
+ const [open, setOpen] = React.useState(false);
25
+ const { side, align } = resolvePlacement(placement);
26
+ const triggerRef = React.useRef(null);
27
+ const [alignOffset, setAlignOffset] = React.useState(0);
28
+ React.useEffect(() => {
29
+ if (align === 'center' || !triggerRef.current) {
30
+ setAlignOffset(0);
31
+ return;
32
+ }
33
+ const isHorizontal = side === 'top' || side === 'bottom';
34
+ const triggerSize = isHorizontal
35
+ ? triggerRef.current.offsetWidth
36
+ : triggerRef.current.offsetHeight;
37
+ setAlignOffset(ARROW_EDGE_INSET - triggerSize / 2);
38
+ }, [align, side]);
39
+ function handleConfirm() {
40
+ onConfirm();
41
+ setOpen(false);
42
+ }
43
+ function handleCancel() {
44
+ onCancel?.();
45
+ setOpen(false);
46
+ }
47
+ const iconName = slotProps?.icon?.name ?? 'risk';
48
+ const IconComp = iconMap[iconName];
49
+ return (_jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, ref: triggerRef, children: children }), _jsx(PopoverPrimitive.Portal, { children: _jsxs(PopoverPrimitive.Content, { side: side, align: align, alignOffset: alignOffset, sideOffset: 8, className: "z-[1300] w-[246px] rounded-fds-lg bg-white p-fds-lg shadow-fds-md origin-[--radix-popover-content-transform-origin] animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", children: [_jsxs("div", { className: "flex gap-fds-xs", children: [IconComp && _jsx(IconComp, { size: 16, color: slotProps?.icon?.color ?? 'yellow-30', className: "mt-[2.5px] shrink-0" }), _jsx("p", { className: "text-fds-base text-fds-gray-100", children: title })] }), _jsxs("div", { className: "mt-[10px] flex justify-end gap-fds-sm", children: [_jsx(Button, { variant: slotProps?.cancelButton?.variant ?? 'secondary', size: "sm", onClick: handleCancel, children: slotProps?.cancelButton?.children ?? 'No' }), _jsx(Button, { variant: slotProps?.confirmButton?.variant ?? 'danger', size: "sm", onClick: handleConfirm, children: slotProps?.confirmButton?.children ?? 'Yes' })] }), _jsx(PopoverPrimitive.Arrow, { className: "fill-white", width: 16, height: 8 })] }) })] }));
50
+ }
51
+ export { PopConfirm };
@@ -1,47 +1,45 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { cn } from '../lib/utils';
4
+ import { useStableId } from '../lib/use-stable-id';
3
5
  import { cva } from 'class-variance-authority';
4
6
  import * as React from 'react';
5
7
  import { RadioGroup, RadioGroupItem } from './radio-group';
6
- const radioButtonGroupItemVariants = cva('relative flex cursor-pointer items-center justify-center border bg-card px-[15px] transition-all has-[[data-state=checked]]:z-10', {
8
+ const itemVariants = cva('relative flex cursor-pointer items-center justify-center border bg-card transition-all has-[[data-state=checked]]:z-10', {
7
9
  variants: {
8
10
  size: {
9
- small: 'h-[24px]',
10
- medium: 'h-[32px]',
11
- large: 'h-[40px]',
12
- },
13
- variant: {
14
- default: 'border-fds-gray-30 text-fds-gray-80 dark:border-fds-gray-60 dark:text-fds-gray-40 has-[[data-state=checked]]:border-fds-blue has-[[data-state=checked]]:bg-fds-blue-10 has-[[data-state=checked]]:text-fds-blue dark:has-[[data-state=checked]]:border-fds-blue-20 dark:has-[[data-state=checked]]:text-fds-blue',
15
- custom: '', // No default checked styles, allows full customization
11
+ sm: 'h-[24px] text-fds-sm px-fds-md',
12
+ md: 'h-[32px] text-fds-base px-fds-lg',
13
+ lg: 'h-[40px] text-fds-h6 px-fds-xl',
16
14
  },
17
15
  },
18
16
  defaultVariants: {
19
- size: 'medium',
20
- variant: 'default',
17
+ size: 'md',
21
18
  },
22
19
  });
23
- const RadioButtonGroup = React.forwardRef(({ className, children, size, borderRadius = 'lg', ...props }, ref) => {
24
- const childrenArray = React.Children.toArray(children);
25
- const childrenWithProps = childrenArray.map((child, index) => {
26
- if (React.isValidElement(child)) {
20
+ const defaultColors = 'border-fds-gray-30 text-fds-gray-80 dark:border-fds-gray-60 dark:text-fds-gray-40 has-[[data-state=checked]]:border-fds-blue-30 has-[[data-state=checked]]:bg-fds-blue-10 has-[[data-state=checked]]:text-fds-blue-30 dark:has-[[data-state=checked]]:border-fds-blue-20 dark:has-[[data-state=checked]]:text-fds-blue-30';
21
+ const errorColors = 'border-fds-red-30 bg-fds-red-10 text-fds-red-30 has-[[data-state=checked]]:border-fds-red-30 has-[[data-state=checked]]:bg-fds-red-10 has-[[data-state=checked]]:text-fds-red-30';
22
+ const disabledColors = 'bg-fds-gray-10 border-fds-gray-20 text-fds-gray-60 has-[[data-state=checked]]:bg-fds-gray-20 has-[[data-state=checked]]:border-fds-gray-30 has-[[data-state=checked]]:text-fds-gray-60';
23
+ function RadioButtonGroup({ options, value, defaultValue, onValueChange, size = 'md', orientation = 'horizontal', disabled = false, error = false, }) {
24
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
25
+ const isControlled = value !== undefined;
26
+ const currentValue = isControlled ? value : internalValue;
27
+ const baseId = useStableId();
28
+ const handleValueChange = (v) => {
29
+ if (!isControlled)
30
+ setInternalValue(v);
31
+ onValueChange?.(v);
32
+ };
33
+ const isVertical = orientation === 'vertical';
34
+ const stateColors = disabled ? disabledColors : error ? errorColors : defaultColors;
35
+ return (_jsx(RadioGroup, { value: currentValue, onValueChange: handleValueChange, disabled: disabled, className: cn('flex', isVertical ? 'flex-col gap-fds-sm' : 'flex-row gap-0', disabled && 'opacity-50 cursor-not-allowed'), children: options.map((option, index) => {
27
36
  const isFirst = index === 0;
28
- const isLast = index === childrenArray.length - 1;
29
- const roundedStart = borderRadius === 'full' ? 'rounded-s-full' : 'rounded-s-fds-md';
30
- const roundedEnd = borderRadius === 'full' ? 'rounded-e-full' : 'rounded-e-fds-md';
31
- const childProps = child.props;
32
- return React.cloneElement(child, {
33
- ...childProps,
34
- size: childProps.size ?? size ?? undefined,
35
- className: cn(childProps.className, isFirst && roundedStart, isLast && roundedEnd, !isFirst && '-ml-px') || undefined,
36
- });
37
- }
38
- return child;
39
- });
40
- return (_jsx(RadioGroup, { ref: ref, className: cn('flex gap-0', className), ...props, children: childrenWithProps }));
41
- });
37
+ const isLast = index === options.length - 1;
38
+ const optionId = `${baseId}-${option.value}`;
39
+ return (_jsxs("div", { className: cn(itemVariants({ size }), stateColors, option.render && 'h-auto py-fds-sm', isVertical
40
+ ? 'rounded-fds-md w-full'
41
+ : cn(isFirst && 'rounded-s-fds-md', isLast && 'rounded-e-fds-md', !isFirst && '-ml-px'), disabled && 'cursor-not-allowed'), children: [_jsx(RadioGroupItem, { id: optionId, value: option.value, className: "sr-only", disabled: disabled }), _jsx("label", { htmlFor: optionId, className: cn('whitespace-nowrap after:absolute after:inset-0', !option.render && 'font-medium', disabled ? 'cursor-not-allowed' : 'cursor-pointer'), style: { color: 'inherit', fontSize: 'inherit' }, children: option.render ? option.render() : option.label })] }, option.value));
42
+ }) }));
43
+ }
42
44
  RadioButtonGroup.displayName = 'RadioButtonGroup';
43
- const RadioButtonGroupItem = React.forwardRef(({ className, value, id, size, variant, children, ...props }, ref) => {
44
- return (_jsxs("div", { ref: ref, className: cn(radioButtonGroupItemVariants({ size, variant }), className), ...props, children: [_jsx(RadioGroupItem, { id: id, value: value, className: "sr-only" }), _jsx("label", { htmlFor: id, className: "cursor-pointer whitespace-nowrap font-medium after:absolute after:inset-0", style: { color: 'inherit', fontSize: 'inherit' }, children: children })] }));
45
- });
46
- RadioButtonGroupItem.displayName = 'RadioButtonGroupItem';
47
- export { RadioButtonGroup, RadioButtonGroupItem };
45
+ export { RadioButtonGroup };
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
3
4
  import * as React from 'react';
@@ -7,7 +8,7 @@ const RadioGroup = React.forwardRef(({ className, ...props }, ref) => {
7
8
  });
8
9
  RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
9
10
  const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => {
10
- return (_jsx(RadioGroupPrimitive.Item, { ref: ref, className: cn('aspect-square h-[16px] w-[16px] rounded-full border border-fds-gray-80 text-fds-blue ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-fds-blue', className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: _jsx("div", { className: "h-[8px] w-[8px] rounded-full bg-fds-blue" }) }) }));
11
+ return (_jsx(RadioGroupPrimitive.Item, { ref: ref, className: cn('fw-base h-[16px] w-[16px] cursor-pointer rounded-full border border-fds-gray-30 bg-white ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:border-fds-gray-20 disabled:bg-fds-gray-10 data-[state=checked]:border-fds-blue-30 data-[state=checked]:disabled:border-fds-gray-30', className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: _jsx("div", { className: "h-[8px] w-[8px] rounded-full bg-fds-blue-30 [[disabled]_&]:bg-fds-gray-30" }) }) }));
11
12
  });
12
13
  RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
13
14
  export { RadioGroup, RadioGroupItem };
@@ -1,11 +1,19 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
3
3
  import * as SliderPrimitive from '@radix-ui/react-slider';
4
4
  import * as React from 'react';
5
- const Slider = React.forwardRef(({ className, ...props }, ref) => {
6
- const value = props.value || props.defaultValue || [0];
7
- const thumbCount = Array.isArray(value) ? value.length : 1;
8
- return (_jsxs(SliderPrimitive.Root, { ref: ref, className: cn('relative flex w-full touch-none select-none items-center', className), ...props, children: [_jsx(SliderPrimitive.Track, { className: "relative h-1 w-full grow overflow-hidden rounded-full bg-fds-gray-20", children: _jsx(SliderPrimitive.Range, { className: "absolute h-full bg-fds-blue" }) }), Array.from({ length: thumbCount }).map((_, i) => (_jsx(SliderPrimitive.Thumb, { className: "block h-4 w-4 rounded-full border-2 border-fds-blue bg-fds-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-20 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" }, i)))] }));
5
+ import { cn } from '../lib/utils';
6
+ const Slider = React.forwardRef(({ value, defaultValue, onValueChange, min = 0, max = 100, step = 1, disabled }, ref) => {
7
+ const [internalValue, setInternalValue] = React.useState(defaultValue ?? [min]);
8
+ const resolvedValue = value !== undefined ? value : internalValue;
9
+ const handleValueChange = (val) => {
10
+ if (value === undefined)
11
+ setInternalValue(val);
12
+ onValueChange?.(val);
13
+ };
14
+ return (_jsxs(SliderPrimitive.Root, { ref: ref, value: resolvedValue, onValueChange: handleValueChange, min: min, max: max, step: step, disabled: disabled, className: "relative flex w-full touch-none select-none items-center", children: [_jsx(SliderPrimitive.Track, { className: "relative h-1 w-full grow overflow-hidden rounded-full bg-fds-gray-20", children: _jsx(SliderPrimitive.Range, { className: cn('absolute h-full', disabled ? 'bg-fds-blue-05' : 'bg-fds-blue-30') }) }), resolvedValue.map((_, i) => (_jsx(SliderPrimitive.Thumb, { className: cn('block h-4 w-4 rounded-full border-2 bg-fds-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2', disabled
15
+ ? 'border-fds-blue-05 cursor-not-allowed'
16
+ : 'border-fds-blue-30 focus-visible:ring-fds-blue-20 cursor-pointer') }, i)))] }));
9
17
  });
10
- Slider.displayName = SliderPrimitive.Root.displayName;
18
+ Slider.displayName = 'Slider';
11
19
  export { Slider };
@@ -1,30 +1,49 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import * as React from 'react';
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as SwitchPrimitives from '@radix-ui/react-switch';
4
+ import * as React from 'react';
4
5
  import { cva } from 'class-variance-authority';
5
6
  import { cn } from '../lib/utils';
6
- const switchVariants = cva('peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-fds-blue data-[state=unchecked]:bg-fds-gray-30', {
7
+ import { useStableId } from '../lib/use-stable-id';
8
+ const switchVariants = cva('fw-base peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50', {
7
9
  variants: {
8
10
  size: {
9
- default: 'h-5 w-9',
10
- small: 'h-4 w-7',
11
+ lg: 'h-5 w-9',
12
+ sm: 'h-4 w-8',
11
13
  },
12
14
  },
13
15
  defaultVariants: {
14
- size: 'default',
16
+ size: 'lg',
15
17
  },
16
18
  });
17
19
  const switchThumbVariants = cva('pointer-events-none block rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=unchecked]:translate-x-0', {
18
20
  variants: {
19
21
  size: {
20
- default: 'h-4 w-4 data-[state=checked]:translate-x-4',
21
- small: 'h-3 w-3 data-[state=checked]:translate-x-3',
22
+ lg: 'size-4 data-[state=checked]:translate-x-4',
23
+ sm: 'size-3 data-[state=checked]:translate-x-4',
22
24
  },
23
25
  },
24
26
  defaultVariants: {
25
- size: 'default',
27
+ size: 'lg',
26
28
  },
27
29
  });
28
- const Switch = React.forwardRef(({ className, size, ...props }, ref) => (_jsx(SwitchPrimitives.Root, { className: cn(switchVariants({ size }), className), ...props, ref: ref, children: _jsx(SwitchPrimitives.Thumb, { className: cn(switchThumbVariants({ size })) }) })));
29
- Switch.displayName = SwitchPrimitives.Root.displayName;
30
+ const Switch = React.forwardRef(({ checked, onCheckedChange, disabled, error, size = 'lg', children }, ref) => {
31
+ const switchId = useStableId();
32
+ const [internalChecked, setInternalChecked] = React.useState(false);
33
+ const resolvedChecked = checked !== undefined ? checked : internalChecked;
34
+ const handleCheckedChange = (val) => {
35
+ if (checked === undefined)
36
+ setInternalChecked(val);
37
+ onCheckedChange?.(val);
38
+ };
39
+ const track = (_jsx(SwitchPrimitives.Root, { ref: ref, id: switchId, checked: resolvedChecked, onCheckedChange: handleCheckedChange, disabled: disabled, "aria-invalid": error || undefined, className: cn(switchVariants({ size }), disabled
40
+ ? 'data-[state=unchecked]:bg-fds-gray-20 data-[state=checked]:bg-fds-blue-05'
41
+ : error
42
+ ? 'data-[state=unchecked]:bg-fds-gray-60 data-[state=checked]:bg-fds-red-30'
43
+ : 'data-[state=unchecked]:bg-fds-gray-60 data-[state=checked]:bg-fds-blue-30'), children: _jsx(SwitchPrimitives.Thumb, { className: cn(switchThumbVariants({ size })) }) }));
44
+ if (!children)
45
+ return track;
46
+ return (_jsxs("div", { className: "flex items-center gap-fds-sm", children: [track, _jsx("label", { htmlFor: switchId, className: cn('text-fds-base cursor-pointer select-none', disabled && 'cursor-not-allowed text-fds-gray-60', !disabled && error && resolvedChecked && 'text-fds-red-30', !disabled && !(error && resolvedChecked) && 'text-fds-gray-80 dark:text-fds-gray-10'), children: children })] }));
47
+ });
48
+ Switch.displayName = 'Switch';
30
49
  export { Switch };