@clicktap/ui 0.14.12 → 0.14.13

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 (191) hide show
  1. package/components/Accordion/Accordion.tsx +82 -0
  2. package/components/Accordion/index.ts +3 -0
  3. package/components/Avatar/Avatar.stories.tsx +99 -0
  4. package/components/Avatar/Avatar.tsx +120 -0
  5. package/components/Avatar/Avatar.types.ts +3 -0
  6. package/components/Avatar/AvatarGroup/AvatarGroup.tsx +32 -0
  7. package/components/Avatar/AvatarGroup/AvatarGroup.types.ts +8 -0
  8. package/components/Avatar/index.ts +4 -0
  9. package/components/Badge/Badge.stories.tsx +72 -0
  10. package/components/Badge/Badge.tsx +169 -0
  11. package/components/Badge/Badge.types.ts +3 -0
  12. package/components/Badge/index.ts +2 -0
  13. package/components/Breadcrumbs/BreadcrumbEllipsis.tsx +47 -0
  14. package/components/Breadcrumbs/BreadcrumbEllipsis.types.ts +5 -0
  15. package/components/Breadcrumbs/BreadcrumbItem.tsx +23 -0
  16. package/components/Breadcrumbs/BreadcrumbItem.types.ts +3 -0
  17. package/components/Breadcrumbs/BreadcrumbLink.tsx +30 -0
  18. package/components/Breadcrumbs/BreadcrumbLink.types.ts +3 -0
  19. package/components/Breadcrumbs/BreadcrumbSeparator.tsx +41 -0
  20. package/components/Breadcrumbs/BreadcrumbSeparator.types.ts +9 -0
  21. package/components/Breadcrumbs/Breadcrumbs.tsx +28 -0
  22. package/components/Breadcrumbs/Breadcrumbs.types.ts +6 -0
  23. package/components/Breadcrumbs/index.ts +10 -0
  24. package/components/Button/Button.tsx +72 -0
  25. package/components/Button/Button.types.ts +7 -0
  26. package/components/Button/index.ts +2 -0
  27. package/components/Card/Card.tsx +15 -0
  28. package/components/Card/Card.types.ts +3 -0
  29. package/components/Card/index.ts +2 -0
  30. package/components/Checkbox/Checkbox.tsx +122 -0
  31. package/components/Checkbox/Checkbox.types.ts +15 -0
  32. package/components/Checkbox/index.ts +2 -0
  33. package/components/Collapsible/Collapsible.tsx +34 -0
  34. package/components/Collapsible/Collapsible.types.ts +5 -0
  35. package/components/Collapsible/CollapsibleTrigger.tsx +57 -0
  36. package/components/Collapsible/CollapsibleTrigger.types.ts +14 -0
  37. package/components/Collapsible/index.ts +10 -0
  38. package/components/Container/Container.tsx +26 -0
  39. package/components/Container/Container.types.ts +3 -0
  40. package/components/Container/index.ts +2 -0
  41. package/components/ContextMenu/ContextMenu.tsx +74 -0
  42. package/components/ContextMenu/ContextMenu.types.ts +17 -0
  43. package/components/ContextMenu/index.ts +2 -0
  44. package/components/CreditCardExpirationInput/CreditCardExpirationInput.tsx +115 -0
  45. package/components/CreditCardExpirationInput/CreditCardExpirationInput.types.ts +10 -0
  46. package/components/CreditCardExpirationInput/index.ts +2 -0
  47. package/components/CreditCardInput/CreditCardInput.tsx +147 -0
  48. package/components/CreditCardInput/CreditCardInput.types.ts +12 -0
  49. package/components/CreditCardInput/index.ts +2 -0
  50. package/components/DateInput/DateInput.tsx +81 -0
  51. package/components/DateInput/DateInput.types.ts +15 -0
  52. package/components/DateInput/index.ts +2 -0
  53. package/components/DateTimeFormat/DateTimeFormat.tsx +16 -0
  54. package/components/DateTimeFormat/DateTimeFormat.types.ts +7 -0
  55. package/components/DateTimeFormat/index.ts +2 -0
  56. package/components/Dialog/Dialog.tsx +65 -0
  57. package/components/Dialog/Dialog.types.ts +9 -0
  58. package/components/Dialog/index.ts +2 -0
  59. package/components/DialogTrigger/DialogTrigger.tsx +45 -0
  60. package/components/DialogTrigger/DialogTrigger.types.ts +6 -0
  61. package/components/DialogTrigger/index.ts +5 -0
  62. package/components/Divider/Divider.stories.tsx +37 -0
  63. package/components/Divider/Divider.tsx +34 -0
  64. package/components/Divider/Divider.types.ts +5 -0
  65. package/components/Divider/index.ts +2 -0
  66. package/components/DobInput/DobInput.tsx +120 -0
  67. package/components/DobInput/index.ts +2 -0
  68. package/components/Drawer/Drawer.tsx +126 -0
  69. package/components/Drawer/Drawer.types.ts +11 -0
  70. package/components/Drawer/index.ts +2 -0
  71. package/components/Icon/Account.tsx +50 -0
  72. package/components/Icon/Cart.tsx +43 -0
  73. package/components/Icon/Checkmark.tsx +34 -0
  74. package/components/Icon/Cross.tsx +36 -0
  75. package/components/Icon/DownArrow.tsx +23 -0
  76. package/components/Icon/Hamburger.tsx +23 -0
  77. package/components/Icon/Icon.types.ts +8 -0
  78. package/components/Icon/LinkArrow.tsx +32 -0
  79. package/components/Icon/Minus.tsx +20 -0
  80. package/components/Icon/Plus.tsx +20 -0
  81. package/components/Icon/Search.tsx +36 -0
  82. package/components/Icon/Trash.tsx +27 -0
  83. package/components/Icon/Verified.tsx +20 -0
  84. package/components/Icon/index.ts +14 -0
  85. package/components/Image/Image.tsx +32 -0
  86. package/components/Image/index.ts +2 -0
  87. package/components/Input/Input.tsx +109 -0
  88. package/components/Input/Input.types.ts +17 -0
  89. package/components/Input/index.ts +2 -0
  90. package/components/Link/Link.stories.tsx +96 -0
  91. package/components/Link/Link.tsx +34 -0
  92. package/components/Link/Link.types.ts +3 -0
  93. package/components/Link/index.ts +2 -0
  94. package/components/Loader/CircularEasing.tsx +66 -0
  95. package/components/Loader/CircularEasing.types.ts +8 -0
  96. package/components/Loader/Pulse.tsx +45 -0
  97. package/components/Loader/Pulse.types.ts +5 -0
  98. package/components/Loader/index.ts +4 -0
  99. package/components/Menu/ContextMenu.tsx +83 -0
  100. package/components/Menu/Menu.tsx +143 -0
  101. package/components/Menu/Menu.types.ts +44 -0
  102. package/components/Menu/index.ts +4 -0
  103. package/components/Meter/Meter.stories.tsx +111 -0
  104. package/components/Meter/Meter.tsx +68 -0
  105. package/components/Meter/Meter.types.ts +10 -0
  106. package/components/Meter/index.ts +2 -0
  107. package/components/Modal/Modal.tsx +16 -0
  108. package/components/Modal/Modal.types.ts +6 -0
  109. package/components/Modal/index.ts +2 -0
  110. package/components/ModalOverlay/ModalOverlay.tsx +121 -0
  111. package/components/ModalOverlay/ModalOverlay.types.ts +18 -0
  112. package/components/ModalOverlay/index.ts +2 -0
  113. package/components/NumberFormat/NumberFormat.tsx +19 -0
  114. package/components/NumberFormat/NumberFormat.types.ts +8 -0
  115. package/components/NumberFormat/index.ts +2 -0
  116. package/components/NumberInput/NumberInput.tsx +164 -0
  117. package/components/NumberInput/NumberInput.types.ts +22 -0
  118. package/components/NumberInput/index.ts +2 -0
  119. package/components/NumberTicker/DigitResolver.tsx +119 -0
  120. package/components/NumberTicker/DigitResolver.types.ts +18 -0
  121. package/components/NumberTicker/NumberTicker.tsx +56 -0
  122. package/components/NumberTicker/NumberTicker.types.ts +96 -0
  123. package/components/NumberTicker/hooks/useColumnTransition.ts +36 -0
  124. package/components/NumberTicker/hooks/useNumberDelta.ts +19 -0
  125. package/components/NumberTicker/hooks/useNumberTicker.ts +36 -0
  126. package/components/NumberTicker/index.ts +10 -0
  127. package/components/Pagination/Pagination.tsx +44 -0
  128. package/components/Pagination/index.ts +2 -0
  129. package/components/PasswordCheck/PasswordCheck.tsx +59 -0
  130. package/components/PasswordCheck/PasswordCheck.types.ts +4 -0
  131. package/components/PasswordCheck/PasswordCheck.utils.ts +47 -0
  132. package/components/PasswordCheck/index.ts +2 -0
  133. package/components/PhoneInput/PhoneInput.tsx +191 -0
  134. package/components/PhoneInput/index.ts +2 -0
  135. package/components/PinInput/PinInput.tsx +314 -0
  136. package/components/PinInput/PinInput.types.ts +21 -0
  137. package/components/PinInput/index.ts +2 -0
  138. package/components/Progressbar/CircularProgressbar.tsx +71 -0
  139. package/components/Progressbar/CircularProgressbar.types.ts +10 -0
  140. package/components/Progressbar/LinearProgressbar.tsx +75 -0
  141. package/components/Progressbar/LinearProgressbar.types.ts +11 -0
  142. package/components/Progressbar/index.ts +4 -0
  143. package/components/Radio/Radio.tsx +88 -0
  144. package/components/Radio/Radio.types.ts +16 -0
  145. package/components/Radio/index.ts +2 -0
  146. package/components/RadioGroup/RadioGroup.tsx +49 -0
  147. package/components/RadioGroup/RadioGroup.types.ts +7 -0
  148. package/components/RadioGroup/index.ts +2 -0
  149. package/components/Select/Option.tsx +32 -0
  150. package/components/Select/Option.types.ts +3 -0
  151. package/components/Select/Select.tsx +253 -0
  152. package/components/Select/Select.types.ts +42 -0
  153. package/components/Select/index.ts +8 -0
  154. package/components/Skeleton/Skeleton.tsx +15 -0
  155. package/components/Skeleton/Skeleton.types.ts +3 -0
  156. package/components/Skeleton/index.ts +2 -0
  157. package/components/Slider/Slider.tsx +110 -0
  158. package/components/Slider/Slider.types.ts +11 -0
  159. package/components/Slider/index.ts +2 -0
  160. package/components/Switch/Switch.tsx +63 -0
  161. package/components/Switch/Switch.types.ts +8 -0
  162. package/components/Switch/index.ts +2 -0
  163. package/components/Table/Table.tsx +52 -0
  164. package/components/Table/Table.types.ts +22 -0
  165. package/components/Table/index.ts +2 -0
  166. package/components/Tabs/Tab.tsx +118 -0
  167. package/components/Tabs/Tab.types.ts +10 -0
  168. package/components/Tabs/TabList.tsx +51 -0
  169. package/components/Tabs/TabList.types.ts +12 -0
  170. package/components/Tabs/TabPanel.tsx +19 -0
  171. package/components/Tabs/TabPanel.types.ts +3 -0
  172. package/components/Tabs/Tabs.context.tsx +9 -0
  173. package/components/Tabs/Tabs.tsx +39 -0
  174. package/components/Tabs/Tabs.types.ts +3 -0
  175. package/components/Tabs/index.ts +9 -0
  176. package/components/TimeInput/TimeInput.stories.tsx +125 -0
  177. package/components/TimeInput/TimeInput.tsx +81 -0
  178. package/components/TimeInput/TimeInput.types.ts +15 -0
  179. package/components/TimeInput/index.ts +2 -0
  180. package/components/ToggleButton/ToggleButton.stories.tsx +89 -0
  181. package/components/ToggleButton/ToggleButton.tsx +69 -0
  182. package/components/ToggleButton/ToggleButton.types.ts +6 -0
  183. package/components/ToggleButton/index.ts +2 -0
  184. package/components/Tooltip/Tooltip.tsx +59 -0
  185. package/components/Tooltip/Tooltip.types.ts +3 -0
  186. package/components/Tooltip/index.ts +2 -0
  187. package/components/UploadImage/UploadImage.tsx +206 -0
  188. package/components/UploadImage/UploadImage.types.ts +15 -0
  189. package/components/UploadImage/index.ts +2 -0
  190. package/package.json +1 -1
  191. package/tailwind.config.js +3 -1
@@ -0,0 +1,3 @@
1
+ import type { BreadcrumbProps } from 'react-aria-components';
2
+
3
+ export type BreadcrumbItemProps = BreadcrumbProps;
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+
3
+ import { Link } from 'react-aria-components';
4
+ import { cn } from '../../utils/cn';
5
+ import type { BreadcrumbLinkProps } from './BreadcrumbLink.types';
6
+
7
+ export function BreadcrumbLink({
8
+ children,
9
+ className,
10
+ ...props
11
+ }: BreadcrumbLinkProps) {
12
+ return (
13
+ <Link
14
+ className={cn(
15
+ 'text-slate-600 no-underline cursor-pointer',
16
+ 'focus-visible:outline-2 focus-visible:outline focus-visible:outline-slate-200',
17
+ 'hover:text-slate-900 hover:underline',
18
+ 'current:cursor-default current:text-slate-900 current:font-semibold',
19
+ 'disabled:cursor-default disabled:text-slate-400 disabled:current:text-slate-900',
20
+ className
21
+ )}
22
+ // eslint-disable-next-line react/jsx-props-no-spreading
23
+ {...props}
24
+ >
25
+ {children}
26
+ </Link>
27
+ );
28
+ }
29
+
30
+ export default BreadcrumbLink;
@@ -0,0 +1,3 @@
1
+ import type { LinkProps } from 'react-aria-components';
2
+
3
+ export type BreadcrumbLinkProps = LinkProps;
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { Breadcrumb } from 'react-aria-components';
4
+ import { cn } from '../../utils/cn';
5
+ import type { BreadcrumbSeparatorProps } from './BreadcrumbSeparator.types';
6
+
7
+ export function BreadcrumbSeparator({
8
+ children,
9
+ className,
10
+ ...props
11
+ }: BreadcrumbSeparatorProps) {
12
+ return (
13
+ <Breadcrumb
14
+ className={cn('flex items-center text-slate-600', 'my-0 mx-2', className)}
15
+ // eslint-disable-next-line react/jsx-props-no-spreading
16
+ {...props}
17
+ >
18
+ {children ?? (
19
+ <svg
20
+ aria-hidden="true"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ width="16"
23
+ height="16"
24
+ viewBox="0 0 16 16"
25
+ fill="none"
26
+ className="w-4 h-4"
27
+ >
28
+ <path
29
+ d="M6 12L10 8L6 4"
30
+ stroke="currentColor"
31
+ strokeWidth="1.5"
32
+ strokeLinecap="round"
33
+ strokeLinejoin="round"
34
+ />
35
+ </svg>
36
+ )}
37
+ </Breadcrumb>
38
+ );
39
+ }
40
+
41
+ export default BreadcrumbSeparator;
@@ -0,0 +1,9 @@
1
+ import type { BreadcrumbProps } from 'react-aria-components';
2
+ import type { ReactNode } from 'react';
3
+
4
+ /** @todo what is this type actually trying to do? */
5
+ export interface BreadcrumbSeparatorProps
6
+ extends Omit<BreadcrumbProps, 'children'>,
7
+ React.RefAttributes<HTMLLIElement> {
8
+ children?: ReactNode;
9
+ }
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+
3
+ import { Breadcrumbs as AriaBreadcrumbs } from 'react-aria-components';
4
+ import { cn } from '../../utils/cn';
5
+ import type { BreadcrumbsProps } from './Breadcrumbs.types';
6
+
7
+ export function Breadcrumbs({
8
+ children,
9
+ className,
10
+ ...props
11
+ }: BreadcrumbsProps<object>) {
12
+ return (
13
+ <AriaBreadcrumbs
14
+ className={cn(
15
+ 'flex items-center list-none text-sm',
16
+ 'mx-0 mt-0 mr-4',
17
+ 'px-0 pt-1.5 pb-0',
18
+ className
19
+ )}
20
+ // eslint-disable-next-line react/jsx-props-no-spreading
21
+ {...props}
22
+ >
23
+ {children}
24
+ </AriaBreadcrumbs>
25
+ );
26
+ }
27
+
28
+ export default Breadcrumbs;
@@ -0,0 +1,6 @@
1
+ import type { BreadcrumbsProps as AriaBreadcrumbsProps } from 'react-aria-components';
2
+
3
+ export type BreadcrumbsProps<T extends object> = Omit<
4
+ AriaBreadcrumbsProps<T>,
5
+ 'items'
6
+ >;
@@ -0,0 +1,10 @@
1
+ export { BreadcrumbEllipsis } from './BreadcrumbEllipsis';
2
+ export { BreadcrumbItem } from './BreadcrumbItem';
3
+ export { BreadcrumbLink } from './BreadcrumbLink';
4
+ export { BreadcrumbSeparator } from './BreadcrumbSeparator';
5
+ export { Breadcrumbs } from './Breadcrumbs';
6
+ export type { BreadcrumbEllipsisProps } from './BreadcrumbEllipsis.types';
7
+ export type { BreadcrumbItemProps } from './BreadcrumbItem.types';
8
+ export type { BreadcrumbLinkProps } from './BreadcrumbLink.types';
9
+ export type { BreadcrumbSeparatorProps } from './BreadcrumbSeparator.types';
10
+ export type { BreadcrumbsProps } from './Breadcrumbs.types';
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+
3
+ import { forwardRef } from 'react';
4
+ import { Button as AriaButton } from 'react-aria-components';
5
+ import { cn } from '../../utils/cn';
6
+ import type { ButtonProps } from './Button.types';
7
+
8
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
9
+ function Button(
10
+ {
11
+ className,
12
+ isDisabled = false,
13
+ isLoading = false,
14
+ size = 'md',
15
+ variant = 'solid',
16
+ ...props
17
+ },
18
+ ref
19
+ ) {
20
+ const baseClasses = [
21
+ 'flex items-center justify-center',
22
+ 'rounded-md border-solid border',
23
+ 'font-semibold text-sm',
24
+ 'py-2 px-8',
25
+ 'transition-all duration-200 ease-in-out',
26
+ 'focus:outline-2 focus:outline focus:outline-slate-200 pressed:scale-95',
27
+ // size logic:
28
+ size === 'sm' && 'h-8',
29
+ size === 'md' && 'h-10',
30
+ size === 'lg' && 'h-12',
31
+ // variant logic:
32
+ variant === 'ghost' && [
33
+ 'bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent',
34
+ 'border-transparent hover:border-transparent focus:border-transparent disabled:border-transparent',
35
+ 'text-slate-900 disabled:text-slate-400',
36
+ isLoading && 'disabled:text-slate-900',
37
+ ],
38
+ variant === 'outline' && [
39
+ 'bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent',
40
+ 'border-slate-300 hover:border-slate-400 focus:border-slate-400 disabled:border-slate-200',
41
+ 'text-slate-900 disabled:text-slate-500',
42
+ isLoading && ['disabled:border-slate-300', 'disabled:text-slate-900'],
43
+ ],
44
+ variant === 'solid' && [
45
+ 'bg-slate-800 hover:bg-slate-900 focus:bg-slate-900 disabled:bg-slate-900',
46
+ 'border-slate-800 hover:border-slate-900 focus:border-slate-900 disabled:border-slate-200',
47
+ 'text-white disabled:text-slate-400',
48
+ isLoading && ['disabled:border-slate-900', 'disabled:text-white'],
49
+ ],
50
+ ];
51
+
52
+ return (
53
+ <AriaButton
54
+ ref={ref}
55
+ type="button"
56
+ isDisabled={isDisabled}
57
+ className={(renderProps) => {
58
+ const userClasses =
59
+ typeof className === 'function'
60
+ ? className(renderProps)
61
+ : className;
62
+
63
+ return cn(baseClasses, userClasses);
64
+ }}
65
+ // eslint-disable-next-line react/jsx-props-no-spreading
66
+ {...props}
67
+ />
68
+ );
69
+ }
70
+ );
71
+
72
+ export default Button;
@@ -0,0 +1,7 @@
1
+ import type { ButtonProps as AriaButtonProps } from 'react-aria-components';
2
+
3
+ export type ButtonProps = AriaButtonProps & {
4
+ isLoading?: boolean;
5
+ variant?: 'solid' | 'outline' | 'ghost';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ };
@@ -0,0 +1,2 @@
1
+ export { Button } from './Button';
2
+ export type { ButtonProps } from './Button.types';
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import { cn } from '../../utils/cn';
4
+ import type { CardProps } from './Card.types';
5
+
6
+ export function Card({ children, className, ...props }: CardProps) {
7
+ return (
8
+ // eslint-disable-next-line react/jsx-props-no-spreading
9
+ <div className={cn('flex flex-col', className)} {...props}>
10
+ {children}
11
+ </div>
12
+ );
13
+ }
14
+
15
+ export default Card;
@@ -0,0 +1,3 @@
1
+ import type { PropsWithChildren, HTMLAttributes } from 'react';
2
+
3
+ export type CardProps = PropsWithChildren<HTMLAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { Card } from './Card';
2
+ export type { CardProps } from './Card.types';
@@ -0,0 +1,122 @@
1
+ import { Checkbox as AriaCheckbox } from 'react-aria-components';
2
+ import type { CheckboxRenderProps } from 'react-aria-components';
3
+ import { cn } from '../../utils/cn';
4
+ import type { CheckboxProps, CheckboxSlots } from './Checkbox.types';
5
+
6
+ function ControlSlot({
7
+ control,
8
+ isHovered,
9
+ isFocused,
10
+ isDisabled,
11
+ isIndeterminate,
12
+ isInvalid,
13
+ isSelected,
14
+ isPressed,
15
+ classNames,
16
+ ...rest
17
+ }: CheckboxRenderProps &
18
+ Pick<CheckboxSlots, 'control'> &
19
+ Pick<CheckboxProps, 'classNames'>) {
20
+ if (!control) {
21
+ return (
22
+ <div
23
+ className={cn(
24
+ 'flex items-center justify-center',
25
+ 'w-6 h-6',
26
+ 'border border-solid border-slate-300 rounded',
27
+ 'transition-all duration-200 ease-in-out',
28
+ 'checkbox-control',
29
+ isHovered && ['border-slate-400'],
30
+ isFocused && ['border-slate-400 outline-2 outline outline-slate-200'],
31
+ (isDisabled || (isDisabled && isIndeterminate)) && [
32
+ 'border-slate-200 bg-slate-100',
33
+ ],
34
+ isInvalid && ['bg-slate-100 text-red-600 border-red-500'],
35
+ isInvalid && isHovered && ['border-red-600'],
36
+ isInvalid && isFocused && ['border-red-600 outline-red-200'],
37
+ isInvalid &&
38
+ isSelected &&
39
+ isIndeterminate && ['bg-red-500 border-red-500'],
40
+ isInvalid &&
41
+ isSelected &&
42
+ isIndeterminate &&
43
+ isPressed && ['bg-red-600 border-red-600'],
44
+ (isSelected || isIndeterminate) && ['border-slate-300 bg-slate-300'],
45
+ (isSelected || isIndeterminate) &&
46
+ isPressed && ['border-slate-400 bg-slate-400'],
47
+ classNames?.control
48
+ )}
49
+ >
50
+ {/* <Control className="checkbox-control"> */}
51
+ <svg
52
+ viewBox="0 0 18 18"
53
+ aria-hidden="true"
54
+ style={{
55
+ strokeDasharray: '22px',
56
+ strokeDashoffset: isSelected || isIndeterminate ? 44 : 66,
57
+ }}
58
+ className={cn(
59
+ 'w-4 h-4 fill-none',
60
+ 'stroke-white stroke-[3px]',
61
+ 'transition-all duration-200 ease-in-out',
62
+ isDisabled && isIndeterminate && ['stroke-none fill-slate-300'],
63
+ isIndeterminate && ['stroke-none fill-white']
64
+ )}
65
+ >
66
+ {isIndeterminate ? (
67
+ <rect x={1} y={7.5} width={15} height={3} />
68
+ ) : (
69
+ <polyline points="1 9 7 14 15 4" />
70
+ )}
71
+ </svg>
72
+ </div>
73
+ );
74
+ }
75
+ return typeof control === 'function'
76
+ ? control({
77
+ isHovered,
78
+ isFocused,
79
+ isDisabled,
80
+ isIndeterminate,
81
+ isInvalid,
82
+ isSelected,
83
+ isPressed,
84
+ ...rest,
85
+ })
86
+ : control;
87
+ }
88
+
89
+ export function Checkbox({
90
+ children,
91
+ slots,
92
+ className,
93
+ classNames,
94
+ ...props
95
+ }: CheckboxProps) {
96
+ return (
97
+ <AriaCheckbox
98
+ className={cn(
99
+ 'flex items-center gap-2',
100
+ 'text-xs text-slate-500 forced-color-adjust-none disabled:text-slate-400 group',
101
+ 'invalid:text-red-500',
102
+ className
103
+ )}
104
+ // eslint-disable-next-line react/jsx-props-no-spreading
105
+ {...props}
106
+ >
107
+ {(renderProps) => (
108
+ <>
109
+ <ControlSlot
110
+ control={slots?.control}
111
+ classNames={classNames}
112
+ // eslint-disable-next-line react/jsx-props-no-spreading
113
+ {...renderProps}
114
+ />
115
+ {typeof children === 'function' ? children(renderProps) : children}
116
+ </>
117
+ )}
118
+ </AriaCheckbox>
119
+ );
120
+ }
121
+
122
+ export default Checkbox;
@@ -0,0 +1,15 @@
1
+ import type {
2
+ CheckboxProps as UiCheckboxProps,
3
+ CheckboxRenderProps,
4
+ } from 'react-aria-components';
5
+ import type { ReactNode } from 'react';
6
+ import type { SlotsToClasses } from '../../types/SlotsToClasses';
7
+
8
+ export interface CheckboxSlots {
9
+ control?: ReactNode | ((values: CheckboxRenderProps) => ReactNode);
10
+ }
11
+
12
+ export interface CheckboxProps extends UiCheckboxProps {
13
+ slots?: CheckboxSlots;
14
+ classNames?: SlotsToClasses<'control'>;
15
+ }
@@ -0,0 +1,2 @@
1
+ export { Checkbox } from './Checkbox';
2
+ export type { CheckboxProps, CheckboxSlots } from './Checkbox.types';
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+ import { motion } from 'framer-motion';
5
+ import type { CollapsibleContentProps } from './Collapsible.types';
6
+ import { CollapsibleTriggerStateContext } from './CollapsibleTrigger';
7
+ import { cn } from '../../utils/cn';
8
+
9
+ export function Collapsible({ children, className }: CollapsibleContentProps) {
10
+ const { isOpen, id, defaultOpen } = useContext(
11
+ CollapsibleTriggerStateContext
12
+ );
13
+ return (
14
+ <motion.div
15
+ className={cn('w-full overflow-hidden', className)}
16
+ id={id}
17
+ variants={{
18
+ hidden: { height: '0px', opacity: 0 },
19
+ visible: { height: 'auto', opacity: 1 },
20
+ }}
21
+ initial={defaultOpen ? 'visible' : 'hidden'}
22
+ animate={isOpen ? 'visible' : 'hidden'}
23
+ transition={{
24
+ type: 'spring',
25
+ bounce: 0,
26
+ }}
27
+ // eslint-disable-next-line react/jsx-props-no-spreading
28
+ >
29
+ {children}
30
+ </motion.div>
31
+ );
32
+ }
33
+
34
+ export default Collapsible;
@@ -0,0 +1,5 @@
1
+ import type { PropsWithChildren, HTMLAttributes } from 'react';
2
+
3
+ export type CollapsibleContentProps = PropsWithChildren<
4
+ HTMLAttributes<HTMLDivElement>
5
+ >;
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import { Provider } from 'react-aria-components';
4
+ import { useControlledState } from '@react-stately/utils';
5
+ import { createContext, useCallback, useId, useRef } from 'react';
6
+ import { PressResponder } from '@react-aria/interactions';
7
+ import {
8
+ CollapsibleTriggerProps,
9
+ CollapsibleTriggerState,
10
+ } from './CollapsibleTrigger.types';
11
+ import { cn } from '../../utils/cn';
12
+
13
+ /** @todo look into use hook to provide access instead of exporting directly */
14
+ export const CollapsibleTriggerStateContext =
15
+ createContext<CollapsibleTriggerState>({} as CollapsibleTriggerState);
16
+
17
+ export function CollapsibleTrigger({
18
+ children,
19
+ className,
20
+ ...props
21
+ }: CollapsibleTriggerProps) {
22
+ const id = useId();
23
+ const [isOpen, onOpenChange] = useControlledState(
24
+ props.isOpen,
25
+ props.defaultOpen || false,
26
+ props.onOpenChange
27
+ );
28
+
29
+ const buttonRef = useRef<HTMLButtonElement>(null);
30
+
31
+ const toggle = useCallback(() => {
32
+ onOpenChange(!isOpen);
33
+ }, [onOpenChange, isOpen]);
34
+
35
+ return (
36
+ <Provider
37
+ values={[
38
+ [
39
+ CollapsibleTriggerStateContext,
40
+ { isOpen, id, defaultOpen: props.defaultOpen || false },
41
+ ],
42
+ ]}
43
+ >
44
+ <PressResponder
45
+ ref={buttonRef}
46
+ isPressed={isOpen}
47
+ onPress={toggle}
48
+ aria-expanded={isOpen}
49
+ aria-controls={id}
50
+ >
51
+ <div className={cn('w-full', className)}>{children}</div>
52
+ </PressResponder>
53
+ </Provider>
54
+ );
55
+ }
56
+
57
+ export default CollapsibleTrigger;
@@ -0,0 +1,14 @@
1
+ import type { PropsWithChildren } from 'react';
2
+
3
+ export type CollapsibleTriggerState = {
4
+ isOpen: boolean;
5
+ id: string;
6
+ defaultOpen: boolean;
7
+ };
8
+
9
+ export type CollapsibleTriggerProps = PropsWithChildren<{
10
+ isOpen?: boolean;
11
+ defaultOpen?: boolean;
12
+ onOpenChange?: (isOpen: boolean) => void;
13
+ className?: string | undefined;
14
+ }>;
@@ -0,0 +1,10 @@
1
+ export { Collapsible } from './Collapsible';
2
+ export {
3
+ CollapsibleTrigger,
4
+ CollapsibleTriggerStateContext,
5
+ } from './CollapsibleTrigger';
6
+ export type { CollapsibleContentProps } from './Collapsible.types';
7
+ export type {
8
+ CollapsibleTriggerProps,
9
+ CollapsibleTriggerState,
10
+ } from './CollapsibleTrigger.types';
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import { forwardRef } from 'react';
4
+ import type { ForwardedRef } from 'react';
5
+ import type { ContainerProps } from './Container.types';
6
+ import { cn } from '../../utils/cn';
7
+
8
+ export const Container = forwardRef(
9
+ (
10
+ { children, className, ...props }: ContainerProps,
11
+ ref: ForwardedRef<HTMLDivElement>
12
+ ) => {
13
+ return (
14
+ <div
15
+ ref={ref}
16
+ className={cn('container', 'mx-auto my-0', 'py-0 px-4', className)}
17
+ // eslint-disable-next-line react/jsx-props-no-spreading
18
+ {...props}
19
+ >
20
+ {children}
21
+ </div>
22
+ );
23
+ }
24
+ );
25
+
26
+ export default Container;
@@ -0,0 +1,3 @@
1
+ import { PropsWithChildren, HTMLAttributes } from 'react';
2
+
3
+ export type ContainerProps = PropsWithChildren<HTMLAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { Container } from './Container';
2
+ export type { ContainerProps } from './Container.types';
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import { Menu, Popover } from 'react-aria-components';
4
+ import { forwardRef } from 'react';
5
+ import type { Ref } from 'react';
6
+ import { motion } from 'framer-motion';
7
+ import { cn } from '../../utils/cn';
8
+ import type { PopoverProps } from './ContextMenu.types';
9
+
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ const ForwardedPopover = forwardRef<HTMLElement, any>(
12
+ ({ style, ...props }, ref: Ref<HTMLElement>) => {
13
+ // Separate the dynamic style logic
14
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
15
+ const ariaStyle = typeof style === 'function' ? style(props) : style;
16
+
17
+ return (
18
+ // Pass only static styles to framer-motion
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, react/jsx-props-no-spreading
20
+ <Popover {...props} ref={ref} style={ariaStyle} />
21
+ );
22
+ }
23
+ );
24
+
25
+ const MotionPopover = motion.create(ForwardedPopover);
26
+
27
+ export function ContextMenu({
28
+ children,
29
+ key,
30
+ onAction,
31
+ setAnimation,
32
+ animation = 'hidden',
33
+ className,
34
+ classNames,
35
+ ...props
36
+ }: PopoverProps) {
37
+ return (
38
+ <MotionPopover
39
+ className={cn(
40
+ 'px-0 py-1.5',
41
+ 'shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_-4px_rgba(0,0,0,0.1)]',
42
+ 'rounded-md',
43
+ 'w-56',
44
+ 'bg-white',
45
+ 'border border-solid border-slate-300',
46
+ className
47
+ )}
48
+ key={key}
49
+ isExiting={animation === 'hidden'}
50
+ onAnimationComplete={(completedAnimation: string) => {
51
+ setAnimation((a) =>
52
+ completedAnimation === 'hidden' && a === 'hidden' ? 'unmounted' : a
53
+ );
54
+ }}
55
+ variants={{
56
+ hidden: { opacity: 0, y: -10 },
57
+ visible: { opacity: 1, y: 0 },
58
+ }}
59
+ initial="hidden"
60
+ animate={animation}
61
+ // eslint-disable-next-line react/jsx-props-no-spreading
62
+ {...props}
63
+ >
64
+ <Menu
65
+ className={cn('outline-none', classNames?.menu)}
66
+ onAction={onAction}
67
+ >
68
+ {children}
69
+ </Menu>
70
+ </MotionPopover>
71
+ );
72
+ }
73
+
74
+ export default ContextMenu;
@@ -0,0 +1,17 @@
1
+ import type { PopoverProps as AriaPopoverProps } from 'react-aria-components';
2
+ import type { Dispatch, Key, SetStateAction, ReactNode } from 'react';
3
+ import type { MotionStyle } from 'framer-motion';
4
+ import type { SlotsToClasses } from '../../types/SlotsToClasses';
5
+
6
+ type ContextMenuAnimationState = 'unmounted' | 'hidden' | 'visible';
7
+
8
+ export interface PopoverProps
9
+ extends Omit<AriaPopoverProps, 'children' | 'style'> {
10
+ animation: ContextMenuAnimationState;
11
+ children: ReactNode;
12
+ onAction?: (key: Key) => void;
13
+ setAnimation: Dispatch<SetStateAction<ContextMenuAnimationState>>;
14
+ key?: Key | null;
15
+ classNames?: SlotsToClasses<'menu'>;
16
+ style?: MotionStyle;
17
+ }
@@ -0,0 +1,2 @@
1
+ export { ContextMenu } from './ContextMenu';
2
+ export type { PopoverProps } from './ContextMenu.types';