untitledui 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/commands/add.js +339 -0
  2. package/dist/commands/init.js +436 -0
  3. package/dist/helper/download-tar-api.js +129 -0
  4. package/dist/helper/download-tar.js +81 -0
  5. package/dist/helper/find-css-file.js +19 -0
  6. package/dist/helper/formatText.js +37 -0
  7. package/dist/helper/get-components-api.js +47 -0
  8. package/dist/helper/get-components-list.js +62 -0
  9. package/dist/helper/get-components.js +19 -0
  10. package/dist/helper/get-config.js +163 -0
  11. package/dist/helper/get-package-info.js +99 -0
  12. package/dist/helper/get-pkg-manager.js +16 -0
  13. package/dist/helper/get-project.js +176 -0
  14. package/dist/helper/install-template.js +29 -0
  15. package/dist/helper/match-color-css.js +82 -0
  16. package/dist/helper/update-color-css.js +134 -0
  17. package/dist/index.js +25 -0
  18. package/dist/package.json +50 -0
  19. package/dist/res/components.json +520 -0
  20. package/dist/res/config.json +3 -0
  21. package/package.json +61 -0
  22. package/templates/default/.prettierrc +10 -0
  23. package/templates/default/README.md +36 -0
  24. package/templates/default/eslint.config.mjs +58 -0
  25. package/templates/default/next.config.ts +6 -0
  26. package/templates/default/package.json +57 -0
  27. package/templates/default/postcss.config.js +5 -0
  28. package/templates/default/public/favicon.ico +0 -0
  29. package/templates/default/public/marketing/smiling-girl.png +0 -0
  30. package/templates/default/public/marketing/spirals.webp +0 -0
  31. package/templates/default/src/app/home-screen.tsx +109 -0
  32. package/templates/default/src/app/layout.tsx +42 -0
  33. package/templates/default/src/app/not-found.tsx +40 -0
  34. package/templates/default/src/app/page.tsx +3 -0
  35. package/templates/default/src/components/foundations/dot-icon.tsx +27 -0
  36. package/templates/default/src/components/foundations/featured-icon/featured-icons.tsx +153 -0
  37. package/templates/default/src/components/foundations/logo/UntitledLogo.tsx +63 -0
  38. package/templates/default/src/components/foundations/logo/UntitledLogoMinimal.tsx +164 -0
  39. package/templates/default/src/components/foundations/payment-icons/amex-icon.tsx +19 -0
  40. package/templates/default/src/components/foundations/payment-icons/apple-pay-icon.tsx +27 -0
  41. package/templates/default/src/components/foundations/payment-icons/discover-icon.tsx +34 -0
  42. package/templates/default/src/components/foundations/payment-icons/index.tsx +10 -0
  43. package/templates/default/src/components/foundations/payment-icons/mastercard-icon.tsx +39 -0
  44. package/templates/default/src/components/foundations/payment-icons/paypal-icon.tsx +45 -0
  45. package/templates/default/src/components/foundations/payment-icons/stripe-icon.tsx +27 -0
  46. package/templates/default/src/components/foundations/payment-icons/union-pay-icon.tsx +37 -0
  47. package/templates/default/src/components/foundations/payment-icons/visa-icon.tsx +27 -0
  48. package/templates/default/src/components/marketing/header-navigation/base-components/nav-menu-item.tsx +41 -0
  49. package/templates/default/src/components/marketing/header-navigation/components/header.tsx +245 -0
  50. package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx +53 -0
  51. package/templates/default/src/components/shared/avatar/avatar-label-group.tsx +32 -0
  52. package/templates/default/src/components/shared/avatar/avatar-profile-photo.tsx +84 -0
  53. package/templates/default/src/components/shared/avatar/avatar.tsx +131 -0
  54. package/templates/default/src/components/shared/avatar/base-components/avatar-add-button.tsx +33 -0
  55. package/templates/default/src/components/shared/avatar/base-components/avatar-company-icon.tsx +26 -0
  56. package/templates/default/src/components/shared/avatar/base-components/avatar-online-indicator.tsx +31 -0
  57. package/templates/default/src/components/shared/avatar/base-components/index.ts +4 -0
  58. package/templates/default/src/components/shared/avatar/base-components/verified-tick.tsx +34 -0
  59. package/templates/default/src/components/shared/avatar/utils.ts +12 -0
  60. package/templates/default/src/components/shared/badges/badge-groups.tsx +176 -0
  61. package/templates/default/src/components/shared/badges/badge-types.ts +264 -0
  62. package/templates/default/src/components/shared/badges/badges.tsx +479 -0
  63. package/templates/default/src/components/shared/button-group/button-group.tsx +97 -0
  64. package/templates/default/src/components/shared/buttons/app-store-buttons-outline.tsx +454 -0
  65. package/templates/default/src/components/shared/buttons/app-store-buttons.tsx +806 -0
  66. package/templates/default/src/components/shared/buttons/button-utility.tsx +87 -0
  67. package/templates/default/src/components/shared/buttons/button.tsx +284 -0
  68. package/templates/default/src/components/shared/buttons/close-button.tsx +39 -0
  69. package/templates/default/src/components/shared/buttons/social-button.tsx +135 -0
  70. package/templates/default/src/components/shared/buttons/social-logos.tsx +115 -0
  71. package/templates/default/src/components/shared/checkbox/checkbox.tsx +120 -0
  72. package/templates/default/src/components/shared/dropdown/dropdown.tsx +138 -0
  73. package/templates/default/src/components/shared/input-dropdown/combobox.tsx +161 -0
  74. package/templates/default/src/components/shared/input-dropdown/dropdown-item.tsx +98 -0
  75. package/templates/default/src/components/shared/input-dropdown/input-dropdown.tsx +172 -0
  76. package/templates/default/src/components/shared/input-dropdown/multi-select.tsx +373 -0
  77. package/templates/default/src/components/shared/input-dropdown/popover.tsx +36 -0
  78. package/templates/default/src/components/shared/input-dropdown/select.tsx +63 -0
  79. package/templates/default/src/components/shared/inputs/file-upload-trigger.tsx +74 -0
  80. package/templates/default/src/components/shared/inputs/form/form.tsx +10 -0
  81. package/templates/default/src/components/shared/inputs/hint-text.tsx +34 -0
  82. package/templates/default/src/components/shared/inputs/input/index.tsx +189 -0
  83. package/templates/default/src/components/shared/inputs/input/input-payment.tsx +134 -0
  84. package/templates/default/src/components/shared/inputs/input/input-with-button.tsx +69 -0
  85. package/templates/default/src/components/shared/inputs/input/input-with-dropdown.tsx +178 -0
  86. package/templates/default/src/components/shared/inputs/input/input-with-prefix.tsx +74 -0
  87. package/templates/default/src/components/shared/inputs/label.tsx +46 -0
  88. package/templates/default/src/components/shared/inputs/textarea/textarea.tsx +82 -0
  89. package/templates/default/src/components/shared/progress-indicators/progress-circles.tsx +176 -0
  90. package/templates/default/src/components/shared/progress-indicators/progress-indicators.tsx +86 -0
  91. package/templates/default/src/components/shared/progress-indicators/simple-circle.tsx +29 -0
  92. package/templates/default/src/components/shared/radio-buttons/radio-buttons.tsx +125 -0
  93. package/templates/default/src/components/shared/radio-groups/radio-group-avatar.tsx +62 -0
  94. package/templates/default/src/components/shared/radio-groups/radio-group-checkbox.tsx +72 -0
  95. package/templates/default/src/components/shared/radio-groups/radio-group-icon-card.tsx +95 -0
  96. package/templates/default/src/components/shared/radio-groups/radio-group-icon-simple.tsx +70 -0
  97. package/templates/default/src/components/shared/radio-groups/radio-group-payment-icon.tsx +71 -0
  98. package/templates/default/src/components/shared/radio-groups/radio-group-radio-button.tsx +76 -0
  99. package/templates/default/src/components/shared/radio-groups/radio-groups.tsx +8 -0
  100. package/templates/default/src/components/shared/slider/slider.tsx +76 -0
  101. package/templates/default/src/components/shared/tags/base-components/tag-checkbox.tsx +47 -0
  102. package/templates/default/src/components/shared/tags/base-components/tag-close-x.tsx +34 -0
  103. package/templates/default/src/components/shared/tags/tags.tsx +162 -0
  104. package/templates/default/src/components/shared/toggle/toggle.tsx +140 -0
  105. package/templates/default/src/components/shared/tooltips/tooltips.tsx +140 -0
  106. package/templates/default/src/components/utils/index.ts +48 -0
  107. package/templates/default/src/components/utils/isDeepEqual.ts +31 -0
  108. package/templates/default/src/components/utils/isReactComponent.ts +22 -0
  109. package/templates/default/src/components/utils/mergeRefs.ts +19 -0
  110. package/templates/default/src/components/utils/useBreakpoint.ts +36 -0
  111. package/templates/default/src/components/utils/uuid.ts +9 -0
  112. package/templates/default/src/fonts/GeistMonoVF.woff +0 -0
  113. package/templates/default/src/fonts/GeistVF.woff +0 -0
  114. package/templates/default/src/hooks/use-resize-observer.tsx +55 -0
  115. package/templates/default/src/providers/theme.tsx +11 -0
  116. package/templates/default/src/styles/colors.css +805 -0
  117. package/templates/default/src/styles/globals.css +86 -0
  118. package/templates/default/src/styles/text-styles.css +177 -0
  119. package/templates/default/src/styles/theme.css +1310 -0
  120. package/templates/default/src/styles/typography.css +428 -0
  121. package/templates/default/tsconfig.json +27 -0
@@ -0,0 +1,162 @@
1
+ "use client";
2
+
3
+ import { type PropsWithChildren, type RefAttributes, createContext, useContext } from "react";
4
+ import {
5
+ Tag as AriaTag,
6
+ TagGroup as AriaTagGroup,
7
+ type TagGroupProps as AriaTagGroupProps,
8
+ TagList as AriaTagList,
9
+ type TagProps as AriaTagProps,
10
+ } from "react-aria-components";
11
+ import Dot from "@/components/foundations/dot-icon";
12
+ import { cx } from "@/components/utils";
13
+ import Avatar from "../avatar/avatar";
14
+ import TagCheckbox from "./base-components/tag-checkbox";
15
+ import { TagCloseX } from "./base-components/tag-close-x";
16
+
17
+ export interface TagItem {
18
+ id: string;
19
+ label: string;
20
+ count?: number;
21
+ avatarSrc?: string;
22
+ avatarContrastBorder?: boolean;
23
+ dot?: boolean;
24
+ dotClassName?: string;
25
+ isDisabled?: boolean;
26
+ onClose?: (id: string) => void;
27
+ }
28
+
29
+ const TagGroupContext = createContext<{
30
+ selectionMode: "none" | "single" | "multiple";
31
+ size: "sm" | "md" | "lg";
32
+ }>({
33
+ selectionMode: "none",
34
+ size: "sm",
35
+ });
36
+
37
+ interface TagGroupProps extends AriaTagGroupProps, RefAttributes<HTMLDivElement> {
38
+ label: string;
39
+ size?: "sm" | "md" | "lg";
40
+ }
41
+
42
+ export const TagGroup = ({ label, selectionMode = "none", size = "sm", children, ...otherProps }: TagGroupProps) => {
43
+ return (
44
+ <TagGroupContext.Provider value={{ selectionMode, size }}>
45
+ <AriaTagGroup aria-label={label} selectionMode={selectionMode} disallowEmptySelection={selectionMode === "single"} {...otherProps}>
46
+ {children}
47
+ </AriaTagGroup>
48
+ </TagGroupContext.Provider>
49
+ );
50
+ };
51
+
52
+ export const TagList = AriaTagList;
53
+
54
+ const styles = {
55
+ sm: {
56
+ root: {
57
+ base: "px-2 py-[3px] tt-xs-md",
58
+ withCheckbox: "pl-[5px]",
59
+ withAvatar: "pl-1",
60
+ withDot: "pl-1.5",
61
+ withCount: "pr-1",
62
+ withClose: "pr-1",
63
+ },
64
+ content: "gap-1",
65
+ count: "px-1 tt-xs-md",
66
+ },
67
+ md: {
68
+ root: {
69
+ base: "px-[9px] py-[2px] tt-sm-md",
70
+ withCheckbox: "pl-1",
71
+ withAvatar: "pl-[5px]",
72
+ withDot: "pl-[7px]",
73
+ withCount: "pr-[3px]",
74
+ withClose: "pr-1",
75
+ },
76
+ content: "gap-[5px]",
77
+ count: "px-[5px] tt-xs-md",
78
+ },
79
+ lg: {
80
+ root: {
81
+ base: "px-2.5 py-[4px] tt-sm-md",
82
+ withCheckbox: "pl-[5px]",
83
+ withAvatar: "pl-[7px]",
84
+ withDot: "pl-[9px]",
85
+ withCount: "pr-1",
86
+ withClose: "pr-1",
87
+ },
88
+ content: "gap-1.5",
89
+ count: "px-1.5 tt-sm-md",
90
+ },
91
+ };
92
+
93
+ interface TagProps extends AriaTagProps, RefAttributes<object>, Omit<TagItem, "label" | "id"> {}
94
+
95
+ export const Tag = ({
96
+ id,
97
+ avatarSrc,
98
+ avatarContrastBorder,
99
+ dot,
100
+ dotClassName,
101
+ isDisabled,
102
+ count,
103
+ className,
104
+ children,
105
+ onClose,
106
+ }: PropsWithChildren<TagProps>) => {
107
+ const context = useContext(TagGroupContext);
108
+
109
+ const leadingContent = avatarSrc ? (
110
+ <Avatar size="xxs" src={avatarSrc} alt="Avatar" contrastBorder={avatarContrastBorder} />
111
+ ) : dot ? (
112
+ <Dot className={cx("text-fg-success-secondary", dotClassName)} size="sm" />
113
+ ) : null;
114
+
115
+ return (
116
+ <AriaTag
117
+ id={id}
118
+ isDisabled={isDisabled}
119
+ className={(state) =>
120
+ cx(
121
+ "flex cursor-default items-center gap-[3px] rounded-md bg-primary text-secondary ring-1 ring-border-primary ring-inset focus:outline-hidden focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring",
122
+ styles[context.size].root.base,
123
+
124
+ // With avatar
125
+ avatarSrc && styles[context.size].root.withAvatar,
126
+ // With X button
127
+ (onClose || state.allowsRemoving) && styles[context.size].root.withClose,
128
+ // With dot
129
+ dot && styles[context.size].root.withDot,
130
+ // With count
131
+ typeof count === "number" && styles[context.size].root.withCount,
132
+ // With checkbox
133
+ context.selectionMode !== "none" && styles[context.size].root.withCheckbox,
134
+ // Disabled
135
+ isDisabled && "cursor-not-allowed",
136
+
137
+ typeof className === "function" ? className(state) : className,
138
+ )
139
+ }
140
+ >
141
+ {({ isSelected, isDisabled, allowsRemoving }) => (
142
+ <>
143
+ <div className={cx("flex items-center gap-1", styles[context.size].content)}>
144
+ {context.selectionMode !== "none" && <TagCheckbox size={context.size} isSelected={isSelected} isDisabled={isDisabled} />}
145
+
146
+ {leadingContent}
147
+
148
+ {children}
149
+
150
+ {typeof count === "number" && (
151
+ <span className={cx("flex items-center justify-center rounded-[3px] bg-tertiary text-center", styles[context.size].count)}>
152
+ {count}
153
+ </span>
154
+ )}
155
+ </div>
156
+
157
+ {(onClose || allowsRemoving) && <TagCloseX size={context.size} onPress={() => id && onClose?.(id.toString())} />}
158
+ </>
159
+ )}
160
+ </AriaTag>
161
+ );
162
+ };
@@ -0,0 +1,140 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import type { SwitchProps } from "react-aria-components";
5
+ import { Switch } from "react-aria-components";
6
+ import { cx } from "@/components/utils";
7
+
8
+ interface ToggleBaseProps {
9
+ size?: "sm" | "md";
10
+ slim?: boolean;
11
+ className?: string;
12
+ isHovered?: boolean;
13
+ isFocused?: boolean;
14
+ isSelected?: boolean;
15
+ isDisabled?: boolean;
16
+ }
17
+
18
+ export const ToggleBase = ({ className, isHovered, isDisabled, isFocused, isSelected, slim, size = "sm" }: ToggleBaseProps) => {
19
+ const styles = {
20
+ default: {
21
+ sm: {
22
+ root: "h-5 w-9 p-0.5",
23
+ switch: cx("size-4", isSelected && "translate-x-4"),
24
+ },
25
+ md: {
26
+ root: "h-6 w-11 p-0.5",
27
+ switch: cx("size-5", isSelected && "translate-x-5"),
28
+ },
29
+ },
30
+ slim: {
31
+ sm: {
32
+ root: "h-4 w-8",
33
+ switch: cx("size-4", isSelected && "translate-x-4"),
34
+ },
35
+ md: {
36
+ root: "h-5 w-10",
37
+ switch: cx("size-5", isSelected && "translate-x-5"),
38
+ },
39
+ },
40
+ };
41
+
42
+ const classes = slim ? styles.slim[size] : styles.default[size];
43
+
44
+ return (
45
+ <div
46
+ className={cx(
47
+ "cursor-pointer rounded-full bg-tertiary outline-focus-ring transition duration-150 ease-linear",
48
+ isSelected && "bg-brand-solid",
49
+ isSelected && isHovered && "bg-brand-solid_hover",
50
+ isDisabled && "cursor-not-allowed bg-disabled",
51
+ isFocused && "outline-2 outline-offset-2",
52
+
53
+ slim && "ring-1 ring-border-secondary ring-inset",
54
+ slim && isSelected && "ring-transparent",
55
+ classes.root,
56
+ className,
57
+ )}
58
+ >
59
+ <div
60
+ style={{
61
+ transition: "transform 0.15s ease-in-out, translate 0.15s ease-in-out, border-color 0.1s linear, background-color 0.1s linear",
62
+ }}
63
+ className={cx(
64
+ "rounded-full bg-fg-white shadow-sm",
65
+ isDisabled && "bg-toggle-button-fg_disabled",
66
+
67
+ slim && "shadow-xs",
68
+ slim && "border border-toggle-border",
69
+ slim && isSelected && "border-toggle-slim-border_pressed",
70
+ slim && isSelected && isHovered && "border-toggle-slim-border_pressed-hover",
71
+
72
+ classes.switch,
73
+ )}
74
+ />
75
+ </div>
76
+ );
77
+ };
78
+
79
+ interface ToggleProps extends SwitchProps {
80
+ size?: "sm" | "md";
81
+ label?: string;
82
+ hint?: ReactNode;
83
+ slim?: boolean;
84
+ }
85
+
86
+ export const Toggle = ({ label, hint, className, size = "sm", slim, ...ariaSwitchProps }: ToggleProps) => {
87
+ const sizes = {
88
+ sm: {
89
+ root: "gap-2",
90
+ textWrapper: "",
91
+ label: "tt-sm-md",
92
+ hint: "tt-sm",
93
+ },
94
+ md: {
95
+ root: "gap-3",
96
+ textWrapper: "gap-0.5",
97
+ label: "tt-md-md",
98
+ hint: "tt-md",
99
+ },
100
+ };
101
+
102
+ return (
103
+ <Switch
104
+ {...ariaSwitchProps}
105
+ className={(renderProps) =>
106
+ cx(
107
+ "flex w-max items-start",
108
+ renderProps.isDisabled && "cursor-not-allowed",
109
+ sizes[size].root,
110
+ typeof className === "function" ? className(renderProps) : className,
111
+ )
112
+ }
113
+ >
114
+ {({ isSelected, isDisabled, isFocused, isHovered }) => (
115
+ <>
116
+ <ToggleBase
117
+ slim={slim}
118
+ size={size}
119
+ isHovered={isHovered}
120
+ isDisabled={isDisabled}
121
+ isFocused={isFocused}
122
+ isSelected={isSelected}
123
+ className={slim ? "mt-0.5" : ""}
124
+ />
125
+
126
+ {(label || hint) && (
127
+ <div className={cx("flex flex-col", sizes[size].textWrapper)}>
128
+ {label && <p className={cx("text-secondary select-none", sizes[size].label)}>{label}</p>}
129
+ {hint && (
130
+ <span className={cx("text-tertiary", sizes[size].hint)} onClick={(event) => event.stopPropagation()}>
131
+ {hint}
132
+ </span>
133
+ )}
134
+ </div>
135
+ )}
136
+ </>
137
+ )}
138
+ </Switch>
139
+ );
140
+ };
@@ -0,0 +1,140 @@
1
+ "use client";
2
+
3
+ import type { DetailedReactHTMLElement, ReactNode, RefAttributes } from "react";
4
+ import { cloneElement, useRef } from "react";
5
+ import type { Placement } from "@react-types/overlays";
6
+ import type { FocusableElement } from "@react-types/shared";
7
+ import { mergeProps, useFocusable } from "react-aria";
8
+ import { Tooltip as AriaTooltip, TooltipTrigger as AriaTooltipTrigger, Button, OverlayArrow } from "react-aria-components";
9
+ import type { TooltipProps as AriaTooltipProps, ButtonProps, TooltipTriggerComponentProps } from "react-aria-components";
10
+ import { cx } from "@/components/utils";
11
+
12
+ const padding: Partial<Record<Placement, string>> = {
13
+ "top left": "px-2.5",
14
+ "top right": "px-2.5",
15
+ "bottom left": "px-2.5",
16
+ "bottom right": "px-2.5",
17
+ left: "-ml-px",
18
+ "left top": "-ml-px",
19
+ "left bottom": "-ml-px",
20
+ start: "-ml-px",
21
+ "start top": "-ml-px",
22
+ "start bottom": "-ml-px",
23
+ right: "-mr-px",
24
+ "right top": "-mr-px",
25
+ "right bottom": "-mr-px",
26
+ end: "-mr-px",
27
+ "end top": "-mr-px",
28
+ "end bottom": "-mr-px",
29
+ };
30
+
31
+ interface TooltipProps extends TooltipTriggerComponentProps, Omit<AriaTooltipProps, "children"> {
32
+ title: ReactNode;
33
+ description?: ReactNode;
34
+ /**
35
+ * Whether to show the arrow on the tooltip.
36
+ *
37
+ * @default false
38
+ */
39
+ arrow?: boolean;
40
+ /**
41
+ * Delay in milliseconds before the tooltip is shown.
42
+ *
43
+ * @default 300
44
+ */
45
+ delay?: number;
46
+ }
47
+
48
+ export const Tooltip = ({
49
+ title,
50
+ description,
51
+ children,
52
+ arrow = false,
53
+ delay = 300,
54
+ closeDelay,
55
+ trigger,
56
+ isDisabled,
57
+ isOpen,
58
+ defaultOpen,
59
+ onOpenChange,
60
+ ...tooltipProps
61
+ }: TooltipProps) => {
62
+ return (
63
+ <AriaTooltipTrigger {...{ trigger, delay, closeDelay, isDisabled, isOpen, defaultOpen, onOpenChange }}>
64
+ {children}
65
+
66
+ <AriaTooltip
67
+ offset={6}
68
+ {...tooltipProps}
69
+ className={({ isEntering, isExiting }) =>
70
+ cx(
71
+ isEntering &&
72
+ "ease-out animate-in fade-in zoom-in-95 placement-top:origin-bottom placement-top:slide-in-from-bottom-0.5 placement-bottom:origin-top placement-bottom:slide-in-from-top-0.5",
73
+ isExiting &&
74
+ "ease-in animate-out fade-out zoom-out-95 placement-top:origin-bottom placement-top:slide-out-to-bottom-0.5 placement-bottom:origin-top placement-bottom:slide-out-to-top-0.5",
75
+ )
76
+ }
77
+ >
78
+ {arrow && (
79
+ <OverlayArrow className={cx(tooltipProps.placement && padding[tooltipProps.placement as keyof typeof padding], "group")}>
80
+ <svg
81
+ width={8}
82
+ height={8}
83
+ viewBox="0 0 8 8"
84
+ className="fill-bg-primary-solid group-data-[placement=bottom]:rotate-180 group-data-[placement=left]:-rotate-90 group-data-[placement=right]:rotate-90 group-data-[placement=top]:rotate-0"
85
+ >
86
+ <path d="M0 0 L4 4 L8 0" />
87
+ </svg>
88
+ </OverlayArrow>
89
+ )}
90
+ <div className={cx("z-50 flex max-w-xs flex-col items-start gap-1 rounded-lg bg-primary-solid px-3 shadow-lg", description ? "py-3" : "py-2")}>
91
+ <span className="tt-xs-semi text-white">{title}</span>
92
+
93
+ {description && <span className="tt-xs-md text-tooltip-supporting-text">{description}</span>}
94
+ </div>
95
+ </AriaTooltip>
96
+ </AriaTooltipTrigger>
97
+ );
98
+ };
99
+
100
+ type TooltipTriggerProps =
101
+ | (ButtonProps &
102
+ RefAttributes<HTMLButtonElement> & {
103
+ /**
104
+ * If true, the tooltip trigger props will be passed down to the child element
105
+ * instead of wrapping the child element in a button.
106
+ */
107
+ asChild?: never;
108
+ })
109
+ | {
110
+ /**
111
+ * If true, the tooltip trigger props will be passed down to the child element
112
+ * instead of wrapping the child element in a button.
113
+ */
114
+ asChild: true;
115
+ isDisabled?: boolean;
116
+ children: Omit<DetailedReactHTMLElement<any, any>, "ref">;
117
+ };
118
+
119
+ export const TooltipTrigger = (props: TooltipTriggerProps) => {
120
+ if (props.asChild) {
121
+ const triggerRef = useRef<FocusableElement>(null);
122
+
123
+ const { focusableProps } = useFocusable(
124
+ {
125
+ isDisabled: props.isDisabled,
126
+ },
127
+ triggerRef,
128
+ );
129
+
130
+ return cloneElement(props.children, mergeProps(focusableProps, props.children.props, { ref: triggerRef }));
131
+ }
132
+
133
+ const { asChild: _, className, ...buttonProps } = props;
134
+
135
+ return (
136
+ <Button {...buttonProps} className={(values) => cx("h-max w-max outline-hidden", typeof className === "function" ? className(values) : className)}>
137
+ {props.children}
138
+ </Button>
139
+ );
140
+ };
@@ -0,0 +1,48 @@
1
+ import { extendTailwindMerge } from "tailwind-merge";
2
+
3
+ const twMerge = extendTailwindMerge<"spacing">({
4
+ extend: {
5
+ classGroups: {
6
+ "font-size": [
7
+ "tt-xs",
8
+ "tt-sm",
9
+ "tt-md",
10
+ "tt-lg",
11
+ "tt-xl",
12
+ "td-xs",
13
+ "td-sm",
14
+ "td-md",
15
+ "td-lg",
16
+ "td-xl",
17
+
18
+ "tt-xs-md",
19
+ "tt-sm-md",
20
+ "tt-md-md",
21
+ "tt-lg-md",
22
+ "tt-xl-md",
23
+ "td-xs-md",
24
+ "td-sm-md",
25
+ "td-md-md",
26
+ "td-lg-md",
27
+ "td-xl-md",
28
+
29
+ "tt-xs-semi",
30
+ "tt-sm-semi",
31
+ "tt-md-semi",
32
+ "tt-lg-semi",
33
+ "tt-xl-semi",
34
+ "td-xs-semi",
35
+ "td-sm-semi",
36
+ "td-md-semi",
37
+ "td-lg-semi",
38
+ "td-xl-semi",
39
+ ],
40
+ },
41
+ },
42
+ });
43
+
44
+ export const cx = twMerge;
45
+
46
+ export function sortCx<T extends Record<string, string | number | Record<string, string | number | Record<string, string | number>>>>(classes: T): T {
47
+ return classes;
48
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Compares two values to determine if they are deeply equal.
3
+ *
4
+ * @param obj1 - The first value to compare.
5
+ * @param obj2 - The second value to compare.
6
+ *
7
+ * @returns True if the values are deeply equal, otherwise false.
8
+ */
9
+ export const isDeepEqual = (obj1: any, obj2: any): boolean => {
10
+ // Check if both are the same reference
11
+ if (obj1 === obj2) return true;
12
+
13
+ // Check if both are objects and not null
14
+ if (typeof obj1 !== "object" || obj1 === null || typeof obj2 !== "object" || obj2 === null) {
15
+ return false;
16
+ }
17
+
18
+ // Check if they have the same number of keys
19
+ const keys1 = Object.keys(obj1);
20
+ const keys2 = Object.keys(obj2);
21
+ if (keys1.length !== keys2.length) return false;
22
+
23
+ // Recursively check each key
24
+ for (const key of keys1) {
25
+ if (!keys2.includes(key) || !isDeepEqual(obj1[key], obj2[key])) {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ return true;
31
+ };
@@ -0,0 +1,22 @@
1
+ import type React from "react";
2
+
3
+ type ReactComponent = React.FC<any> | React.ComponentClass<any, any>;
4
+
5
+ export const isFunctionComponent = (component: any): component is React.FC<any> => {
6
+ return typeof component === "function";
7
+ };
8
+
9
+ export const isClassComponent = (component: any): component is React.ComponentClass<any, any> => {
10
+ return typeof component === "function" && component.prototype && (!!component.prototype.isReactComponent || !!component.prototype.render);
11
+ };
12
+
13
+ export const isForwardRefComponent = (component: any): component is React.ForwardRefExoticComponent<any> => {
14
+ return typeof component === "object" && component !== null && component.$$typeof.toString() === "Symbol(react.forward_ref)";
15
+ };
16
+
17
+ /**
18
+ * Checks if a given value is a valid React component.
19
+ */
20
+ export const isReactComponent = (component: any): component is ReactComponent => {
21
+ return isFunctionComponent(component) || isForwardRefComponent(component) || isClassComponent(component);
22
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Merges multiple React refs into a single ref callback.
3
+ *
4
+ * @param refs - An array of refs which can be mutable ref objects, legacy refs, or undefined/null.
5
+ * @returns A ref callback function that assigns the given value to all provided refs.
6
+ *
7
+ * @typeParam T - The type of the ref value.
8
+ */
9
+ export function mergeRefs<T = any>(refs: Array<React.MutableRefObject<T> | React.LegacyRef<T> | undefined | null>): React.RefCallback<T> {
10
+ return (value) => {
11
+ refs.forEach((ref) => {
12
+ if (typeof ref === "function") {
13
+ ref(value);
14
+ } else if (ref != null) {
15
+ (ref as React.MutableRefObject<T | null>).current = value;
16
+ }
17
+ });
18
+ };
19
+ }
@@ -0,0 +1,36 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ const screens = {
4
+ sm: "640px",
5
+ md: "768px",
6
+ lg: "1024px",
7
+ xl: "1280px",
8
+ "2xl": "1536px",
9
+ };
10
+
11
+ /**
12
+ * Checks whether a particular Tailwind viewport size applies.
13
+ *
14
+ * @param size The size to check, which must either be included in Tailwind's
15
+ * list of default screen sizes, or added to the Tailwind config file.
16
+ *
17
+ * @returns A boolean indicating whether the viewport size applies.
18
+ */
19
+ const useBreakpoint = (size: "sm" | "md" | "lg" | "xl" | "2xl") => {
20
+ const [matches, setMatches] = useState(typeof window !== "undefined" ? window.matchMedia(`(min-width: ${screens[size]})`).matches : true);
21
+
22
+ useEffect(() => {
23
+ const breakpoint = window.matchMedia(`(min-width: ${screens[size]})`);
24
+
25
+ setMatches(breakpoint.matches);
26
+
27
+ const handleChange = (value: MediaQueryListEvent) => setMatches(value.matches);
28
+
29
+ breakpoint.addEventListener("change", handleChange);
30
+ return () => breakpoint.removeEventListener("change", handleChange);
31
+ }, []);
32
+
33
+ return matches;
34
+ };
35
+
36
+ export default useBreakpoint;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generates a short UUID string consisting of 4 hexadecimal characters.
3
+ */
4
+ export const getShortUUID = () => {
5
+ return "xxxx".replace(/[x]/g, (c) => {
6
+ const r = (Math.random() * 16) | 0;
7
+ return r.toString(16);
8
+ });
9
+ };
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Copyright 2022 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+ import { useEffect } from "react";
13
+ import type { RefObject } from "@react-types/shared";
14
+
15
+ function hasResizeObserver() {
16
+ return typeof window.ResizeObserver !== "undefined";
17
+ }
18
+
19
+ type useResizeObserverOptionsType<T> = {
20
+ ref: RefObject<T | undefined | null> | undefined;
21
+ box?: ResizeObserverBoxOptions;
22
+ onResize: () => void;
23
+ };
24
+
25
+ export function useResizeObserver<T extends Element>(options: useResizeObserverOptionsType<T>) {
26
+ const { ref, box, onResize } = options;
27
+
28
+ useEffect(() => {
29
+ let element = ref?.current;
30
+ if (!element) {
31
+ return;
32
+ }
33
+
34
+ if (!hasResizeObserver()) {
35
+ window.addEventListener("resize", onResize, false);
36
+ return () => {
37
+ window.removeEventListener("resize", onResize, false);
38
+ };
39
+ } else {
40
+ const resizeObserverInstance = new window.ResizeObserver((entries) => {
41
+ if (!entries.length) {
42
+ return;
43
+ }
44
+
45
+ onResize();
46
+ });
47
+ resizeObserverInstance.observe(element, { box });
48
+ return () => {
49
+ if (element) {
50
+ resizeObserverInstance.unobserve(element);
51
+ }
52
+ };
53
+ }
54
+ }, [onResize, ref, box]);
55
+ }
@@ -0,0 +1,11 @@
1
+ "use client";
2
+
3
+ import { ThemeProvider } from "next-themes";
4
+
5
+ export function Theme({ children }: { children: React.ReactNode }) {
6
+ return (
7
+ <ThemeProvider attribute="data-theme" enableColorScheme enableSystem>
8
+ {children}
9
+ </ThemeProvider>
10
+ );
11
+ }