torch-glare 1.0.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.
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/cli/bin/addComponent.js +278 -0
- package/cli/bin/addHooks.js +75 -0
- package/cli/bin/addLayout.js +71 -0
- package/cli/bin/addProvider.js +71 -0
- package/cli/bin/addUtils.js +74 -0
- package/cli/bin/cli.js +73 -0
- package/cli/bin/init/init.js +15 -0
- package/cli/bin/init/tailwindInit.js +174 -0
- package/cli/bin/update.js +147 -0
- package/lib/components/ActionButton.tsx +63 -0
- package/lib/components/ActionsGroup.tsx +34 -0
- package/lib/components/AlertDialog.tsx +211 -0
- package/lib/components/Badge.tsx +116 -0
- package/lib/components/BadgeField.tsx +192 -0
- package/lib/components/Button.tsx +277 -0
- package/lib/components/Card.tsx +63 -0
- package/lib/components/Checkbox.tsx +104 -0
- package/lib/components/CountBadge.tsx +54 -0
- package/lib/components/DatePicker.tsx +464 -0
- package/lib/components/Drawer.tsx +118 -0
- package/lib/components/DropdownMenu.tsx +399 -0
- package/lib/components/FieldHint.tsx +76 -0
- package/lib/components/ImageAttachment.tsx +171 -0
- package/lib/components/InnerLabelField.tsx +155 -0
- package/lib/components/Input.tsx +179 -0
- package/lib/components/InputField.tsx +147 -0
- package/lib/components/Label.tsx +107 -0
- package/lib/components/LabelField.tsx +75 -0
- package/lib/components/LabeledCheckBox.tsx +65 -0
- package/lib/components/LabeledRadio.tsx +45 -0
- package/lib/components/LinkButton.tsx +90 -0
- package/lib/components/LoginButton.tsx +56 -0
- package/lib/components/PasswordLevel.tsx +58 -0
- package/lib/components/Popover.tsx +274 -0
- package/lib/components/ProfileMenu.tsx +90 -0
- package/lib/components/Radio.tsx +69 -0
- package/lib/components/RadioCard.tsx +70 -0
- package/lib/components/RingLoading.tsx +190 -0
- package/lib/components/SearchField.tsx +49 -0
- package/lib/components/Select.tsx +417 -0
- package/lib/components/SlideDatePicker.tsx +120 -0
- package/lib/components/SpinLoading.tsx +190 -0
- package/lib/components/Switcher.tsx +56 -0
- package/lib/components/TabFormItem.tsx +158 -0
- package/lib/components/Table.tsx +395 -0
- package/lib/components/Textarea.tsx +108 -0
- package/lib/components/Tooltip.tsx +111 -0
- package/lib/components/TransparentLabel.tsx +72 -0
- package/lib/components/TreeDropDown.tsx +69 -0
- package/lib/hooks/MobileSlidePicker/components/Picker.tsx +218 -0
- package/lib/hooks/MobileSlidePicker/components/PickerColumn.tsx +238 -0
- package/lib/hooks/MobileSlidePicker/components/PickerItem.tsx +64 -0
- package/lib/hooks/MobileSlidePicker/index.ts +10 -0
- package/lib/hooks/useActiveTreeItem.tsx +61 -0
- package/lib/hooks/useClickOutside.tsx +20 -0
- package/lib/hooks/useResize.tsx +78 -0
- package/lib/layouts/CLayout.tsx +326 -0
- package/lib/layouts/FieldSection.tsx +64 -0
- package/lib/layouts/TreeSubLayout.tsx +187 -0
- package/lib/providers/ThemeProvider.tsx +99 -0
- package/lib/utils/cn.ts +6 -0
- package/lib/utils/convertImageFileToDataUrl.ts +17 -0
- package/lib/utils/resize.ts +35 -0
- package/lib/utils/types.ts +12 -0
- package/package.json +28 -0
- package/torch-glare.js +24 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { InputHTMLAttributes, forwardRef, useState } from "react";
|
|
3
|
+
import { Label } from "./Label";
|
|
4
|
+
import { cn } from "../utils/cn";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { Themes } from "../utils/types";
|
|
7
|
+
import { Checkbox } from "./Checkbox";
|
|
8
|
+
|
|
9
|
+
interface Props extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
10
|
+
label: string;
|
|
11
|
+
id: string;
|
|
12
|
+
secondaryLabel?: string;
|
|
13
|
+
requiredLabel?: string;
|
|
14
|
+
directions?: "vertical" | "horizontal";
|
|
15
|
+
size?: "S" | "M" | "L";
|
|
16
|
+
theme?: Themes
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const LabeledCheckBox = forwardRef<HTMLInputElement, Props>(
|
|
20
|
+
(
|
|
21
|
+
{
|
|
22
|
+
id,
|
|
23
|
+
label,
|
|
24
|
+
secondaryLabel,
|
|
25
|
+
requiredLabel,
|
|
26
|
+
type = "checkbox",
|
|
27
|
+
directions,
|
|
28
|
+
theme,
|
|
29
|
+
className,
|
|
30
|
+
size = "M",
|
|
31
|
+
...props
|
|
32
|
+
},
|
|
33
|
+
ref
|
|
34
|
+
) => {
|
|
35
|
+
return (
|
|
36
|
+
<label
|
|
37
|
+
htmlFor={id}
|
|
38
|
+
data-theme={theme}
|
|
39
|
+
className={cn("flex items-center gap-1 group", className)}
|
|
40
|
+
>
|
|
41
|
+
<Checkbox
|
|
42
|
+
{...props}
|
|
43
|
+
id={id}
|
|
44
|
+
disabled={props.disabled}
|
|
45
|
+
type={type}
|
|
46
|
+
size={size}
|
|
47
|
+
ref={ref}
|
|
48
|
+
>
|
|
49
|
+
<Label
|
|
50
|
+
className="leading-none"
|
|
51
|
+
htmlFor={id}
|
|
52
|
+
label={label}
|
|
53
|
+
secondaryLabel={secondaryLabel}
|
|
54
|
+
requiredLabel={requiredLabel}
|
|
55
|
+
size={size}
|
|
56
|
+
directions={directions}
|
|
57
|
+
/>
|
|
58
|
+
</Checkbox>
|
|
59
|
+
</label>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
LabeledCheckBox.displayName = "LabeledCheckBox";
|
|
65
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { InputHTMLAttributes, forwardRef } from "react";
|
|
3
|
+
import { Label } from "./Label";
|
|
4
|
+
import { Radio } from "./Radio";
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
7
|
+
label?: string;
|
|
8
|
+
secondaryLabel?: string;
|
|
9
|
+
requiredLabel?: string;
|
|
10
|
+
size?: "S" | "M" | "L";
|
|
11
|
+
directions?: "vertical" | "horizontal";
|
|
12
|
+
theme?: "dark" | "light" | "default";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const LabeledRadio = forwardRef<HTMLInputElement, Props>(
|
|
16
|
+
(
|
|
17
|
+
{
|
|
18
|
+
label,
|
|
19
|
+
theme,
|
|
20
|
+
secondaryLabel,
|
|
21
|
+
requiredLabel,
|
|
22
|
+
size = "M",
|
|
23
|
+
type = "radio",
|
|
24
|
+
directions,
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
},
|
|
28
|
+
ref
|
|
29
|
+
) => {
|
|
30
|
+
return (
|
|
31
|
+
<Radio {...props} data-theme={theme} checked={props.checked} size={size} ref={ref} >
|
|
32
|
+
<Label
|
|
33
|
+
htmlFor={props.id}
|
|
34
|
+
label={label}
|
|
35
|
+
secondaryLabel={secondaryLabel}
|
|
36
|
+
requiredLabel={requiredLabel}
|
|
37
|
+
size={size}
|
|
38
|
+
directions={directions}
|
|
39
|
+
/>
|
|
40
|
+
</Radio>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
LabeledRadio.displayName = "LabeledRadio";
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React, { AnchorHTMLAttributes, SVGProps } from "react";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import { Themes } from "../utils/types";
|
|
5
|
+
|
|
6
|
+
// Link button base styles
|
|
7
|
+
export const linkButtonStyles = cva(
|
|
8
|
+
[
|
|
9
|
+
"bg-background-presentation-action-link-primary w-fit",
|
|
10
|
+
"flex items-center justify-center",
|
|
11
|
+
"rounded-[6px]",
|
|
12
|
+
"p-[2px]",
|
|
13
|
+
"text-content-presentation-action-link",
|
|
14
|
+
"hover:bg-background-presentation-action-link-hover hover:px-[2px]",
|
|
15
|
+
"transition-all duration-250 ease-in-out",
|
|
16
|
+
"group", // Add group class here
|
|
17
|
+
],
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
size: {
|
|
21
|
+
S: "h-[24px] typography-body-small-semibold",
|
|
22
|
+
M: "h-[26px] typography-body-medium-semibold",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
size: "S",
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
interface Props extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
32
|
+
size?: "S" | "M"; // this props will change the button style size see on figma design file
|
|
33
|
+
theme?: Themes
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const LinkButton: React.FC<Props> = ({ theme, size = "S", className, ...props }) => {
|
|
37
|
+
return (
|
|
38
|
+
<a
|
|
39
|
+
{...props}
|
|
40
|
+
data-theme={theme}
|
|
41
|
+
className={cn(
|
|
42
|
+
linkButtonStyles({
|
|
43
|
+
size: size,
|
|
44
|
+
}),
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<p className="px-[3px]">{props.children}</p>
|
|
49
|
+
<div
|
|
50
|
+
className={cn(
|
|
51
|
+
"rounded-[4px]",
|
|
52
|
+
"bg-background-presentation-state-information-primary",
|
|
53
|
+
"transition-all duration-[100] ease-in-out",
|
|
54
|
+
"h-0 w-0 p-0",
|
|
55
|
+
"opacity-0 group-hover:opacity-100",
|
|
56
|
+
"p-[3px]",
|
|
57
|
+
{
|
|
58
|
+
"group-hover:w-[20px] group-hover:h-[20px]": size === "S",
|
|
59
|
+
"group-hover:w-[22px] group-hover:h-[22px]": size === "M",
|
|
60
|
+
}
|
|
61
|
+
)}
|
|
62
|
+
>
|
|
63
|
+
<Arrow
|
|
64
|
+
className={cn("rtl:rotate-[260deg]", {
|
|
65
|
+
"group-hover:w-[14px] group-hover:h-[14px]": size === "S",
|
|
66
|
+
"group-hover:w-[16px] group-hover:h-[16px]": size === "M",
|
|
67
|
+
})}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</a>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const Arrow = (props: SVGProps<SVGSVGElement>) => (
|
|
75
|
+
<svg
|
|
76
|
+
viewBox="0 0 10 10"
|
|
77
|
+
fill="none"
|
|
78
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
79
|
+
{...props}
|
|
80
|
+
>
|
|
81
|
+
<path
|
|
82
|
+
d="M1.81582 1.58451L3.2807 0.119629L9.12479 0.875203L9.88037 6.71929L8.41549 8.18417L7.78584 3.31409L3.35267 7.74727L2.25272 6.64733L6.6859 2.21415L1.81582 1.58451Z"
|
|
83
|
+
fill="#F9F9F9"
|
|
84
|
+
/>
|
|
85
|
+
<path
|
|
86
|
+
d="M0.325211 8.57478L1.48169 7.4183L2.58164 8.51824L1.42515 9.67472L0.325211 8.57478Z"
|
|
87
|
+
fill="#F9F9F9"
|
|
88
|
+
/>
|
|
89
|
+
</svg>
|
|
90
|
+
);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { cn } from "../utils/cn"; // Assuming you have a `cn` utility
|
|
3
|
+
import { ButtonHTMLAttributes } from "react";
|
|
4
|
+
import { LoadingIcon } from "./Button";
|
|
5
|
+
import { Themes } from "../utils/types";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"w-full h-[42px] rounded-[8px] typography-body-large-regular flex justify-center items-center transition-all duration-250 ease-in-out border",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: [
|
|
13
|
+
"bg-background-system-body-primary",
|
|
14
|
+
"text-content-system-global-primary",
|
|
15
|
+
"border-border-system-global-primary",
|
|
16
|
+
"hover:border-[#9748FF]",
|
|
17
|
+
],
|
|
18
|
+
noBg: [
|
|
19
|
+
"bg-transparent",
|
|
20
|
+
"border-border-system-global-primary",
|
|
21
|
+
"text-content-system-global-primary",
|
|
22
|
+
"hover:border-[#9748FF]",
|
|
23
|
+
"no-underline",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
variant: "default",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export interface ButtonProps
|
|
34
|
+
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
|
35
|
+
VariantProps<typeof buttonVariants> {
|
|
36
|
+
isLoading?: boolean;
|
|
37
|
+
theme?: Themes
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function LoginButton({
|
|
41
|
+
variant = "default",
|
|
42
|
+
className,
|
|
43
|
+
isLoading,
|
|
44
|
+
theme,
|
|
45
|
+
...props
|
|
46
|
+
}: ButtonProps) {
|
|
47
|
+
return (
|
|
48
|
+
<button data-theme={theme} {...props} className={cn(buttonVariants({ variant, className }))}>
|
|
49
|
+
{isLoading ? (
|
|
50
|
+
<LoadingIcon className="w-[20px] h-[20px]" />
|
|
51
|
+
) : (
|
|
52
|
+
props.children
|
|
53
|
+
)}
|
|
54
|
+
</button>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Themes } from "../utils/types";
|
|
2
|
+
import { cn } from "../utils/cn"; // Assuming you have a `cn` utility
|
|
3
|
+
import { HTMLAttributes, useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
interface PassCheckProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
value: string; // The password value to check
|
|
7
|
+
theme?: Themes
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function PasswordLevel({ theme, value, className, ...props }: PassCheckProps) {
|
|
11
|
+
const [level, setLevel] = useState<number>(0);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const symbolRegex = /[!@#$%^&*(),.?":{}|<>]/;
|
|
15
|
+
const uppercaseRegex = /[A-Z]/;
|
|
16
|
+
let passwordLev = 0;
|
|
17
|
+
if (value.length >= 6) passwordLev++;
|
|
18
|
+
if (symbolRegex.test(value)) passwordLev++;
|
|
19
|
+
if (uppercaseRegex.test(value)) passwordLev++;
|
|
20
|
+
setLevel(passwordLev);
|
|
21
|
+
}, [value]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
data-theme={theme}
|
|
26
|
+
{...props}
|
|
27
|
+
className={cn(
|
|
28
|
+
"bg-background-system-body-secondary rounded-[4px] border border-solid border-border-system-global-primary p-[4px] grid grid-cols-3 gap-[4px] w-full ",
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
31
|
+
>
|
|
32
|
+
<div
|
|
33
|
+
className={cn(
|
|
34
|
+
"h-[4px] rounded-[8px] transition-all duration-300 ease-in-out bg-border-system-global-secondary",
|
|
35
|
+
{
|
|
36
|
+
"bg-border-presentation-state-negative": level >= 1,
|
|
37
|
+
}
|
|
38
|
+
)}
|
|
39
|
+
/>
|
|
40
|
+
<div
|
|
41
|
+
className={cn(
|
|
42
|
+
"h-[4px] rounded-[8px] transition-all duration-300 ease-in-out bg-border-system-global-secondary",
|
|
43
|
+
{
|
|
44
|
+
"bg-border-presentation-state-warning": level >= 2,
|
|
45
|
+
}
|
|
46
|
+
)}
|
|
47
|
+
/>
|
|
48
|
+
<div
|
|
49
|
+
className={cn(
|
|
50
|
+
"h-[4px] rounded-[8px] transition-all duration-300 ease-in-out bg-border-system-global-secondary",
|
|
51
|
+
{
|
|
52
|
+
"bg-border-presentation-state-success": level >= 3,
|
|
53
|
+
}
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { cva, VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import React, { useEffect, useRef } from "react";
|
|
5
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
6
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
7
|
+
import { Themes } from "../utils/types";
|
|
8
|
+
|
|
9
|
+
interface LocalPopOverProps extends VariantProps<typeof dropdownMenuStyles> {
|
|
10
|
+
variant?: "SystemStyle" | "PresentationStyle";
|
|
11
|
+
className?: string;
|
|
12
|
+
overlayBlur?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const dropdownMenuStyles = cva(
|
|
16
|
+
[
|
|
17
|
+
"p-1 max-h-[200px] z-[1000]",
|
|
18
|
+
"rounded-[8px]",
|
|
19
|
+
"border",
|
|
20
|
+
"min-w-[240px]",
|
|
21
|
+
"outline-none",
|
|
22
|
+
"overflow-scroll",
|
|
23
|
+
"data-[state=open]:animate-in",
|
|
24
|
+
"data-[state=closed]:animate-out",
|
|
25
|
+
"data-[state=closed]:fade-out-0",
|
|
26
|
+
"data-[state=open]:fade-in-0",
|
|
27
|
+
"scrollbar-hide",
|
|
28
|
+
"overflow-x-hidden",
|
|
29
|
+
],
|
|
30
|
+
{
|
|
31
|
+
variants: {
|
|
32
|
+
variant: {
|
|
33
|
+
SystemStyle: [
|
|
34
|
+
"border-border-system-global-secondary",
|
|
35
|
+
"bg-background-system-body-primary",
|
|
36
|
+
"shadow-[0px_0px_18px_0px_rgba(0,0,0,0.75)]",
|
|
37
|
+
],
|
|
38
|
+
PresentationStyle: [
|
|
39
|
+
"border-border-presentation-global-primary",
|
|
40
|
+
"bg-background-presentation-form-base",
|
|
41
|
+
"shadow-[0px_0px_10px_0px_rgba(0,0,0,0.4),0px_4px_4px_0px_rgba(0,0,0,0.2)]",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
overlayBlur: {
|
|
45
|
+
true: ["h-fit"],
|
|
46
|
+
},
|
|
47
|
+
defaultVariants: {
|
|
48
|
+
variant: "PresentationStyle",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const Popover = PopoverPrimitive.Root;
|
|
55
|
+
|
|
56
|
+
const PopoverTrigger = React.forwardRef<
|
|
57
|
+
React.ElementRef<typeof PopoverPrimitive.Trigger>,
|
|
58
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger> & {
|
|
59
|
+
className?: string
|
|
60
|
+
}
|
|
61
|
+
>(({ className, ...props }, ref) => (
|
|
62
|
+
<PopoverPrimitive.Trigger
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={cn(
|
|
65
|
+
"z-[20] transition-all duration-300 data-[state=open]:z-[49]",
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
));
|
|
71
|
+
|
|
72
|
+
PopoverTrigger.displayName = PopoverPrimitive.Trigger.displayName;
|
|
73
|
+
|
|
74
|
+
const PopoverContent = React.forwardRef<
|
|
75
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
76
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> &
|
|
77
|
+
LocalPopOverProps & {
|
|
78
|
+
theme?: Themes
|
|
79
|
+
}
|
|
80
|
+
>(
|
|
81
|
+
(
|
|
82
|
+
{
|
|
83
|
+
className,
|
|
84
|
+
align = "center",
|
|
85
|
+
sideOffset = 4,
|
|
86
|
+
variant = "SystemStyle",
|
|
87
|
+
overlayBlur = false,
|
|
88
|
+
theme,
|
|
89
|
+
...props
|
|
90
|
+
},
|
|
91
|
+
ref
|
|
92
|
+
) => (
|
|
93
|
+
<PopoverPrimitive.Portal>
|
|
94
|
+
{overlayBlur ? (
|
|
95
|
+
<div className="relative z-[42]" data-theme={theme}>
|
|
96
|
+
<div className="fixed top-0 left-0 flex h-full w-full items-center flex-shrink-0 bg-[rgba(16,7,25,0.32)] backdrop-blur-[8px] transition-all duration-300"></div>
|
|
97
|
+
<PopoverPrimitive.Content
|
|
98
|
+
ref={ref}
|
|
99
|
+
align={align}
|
|
100
|
+
sideOffset={sideOffset}
|
|
101
|
+
className={cn(
|
|
102
|
+
dropdownMenuStyles({ variant, overlayBlur }),
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
) : (
|
|
109
|
+
<PopoverPrimitive.Content
|
|
110
|
+
data-theme={theme}
|
|
111
|
+
ref={ref}
|
|
112
|
+
align={align}
|
|
113
|
+
sideOffset={sideOffset}
|
|
114
|
+
className={cn(
|
|
115
|
+
dropdownMenuStyles({ variant, overlayBlur }),
|
|
116
|
+
className
|
|
117
|
+
)}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
121
|
+
</PopoverPrimitive.Portal>
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
125
|
+
|
|
126
|
+
// Define the Props interface with a generic type parameter
|
|
127
|
+
interface Props<T extends React.ElementType = "li">
|
|
128
|
+
extends React.HTMLAttributes<HTMLElement>,
|
|
129
|
+
VariantProps<typeof PopoverItemStyles> {
|
|
130
|
+
asChild?: boolean;
|
|
131
|
+
as?: T; // The `as` prop can be any valid HTML element or React component
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Define the PopoverItem component with a generic type parameter
|
|
135
|
+
const PopoverItem = <T extends React.ElementType = "li">({
|
|
136
|
+
variant = "SystemStyle",
|
|
137
|
+
size = "M",
|
|
138
|
+
asChild,
|
|
139
|
+
className,
|
|
140
|
+
children,
|
|
141
|
+
active,
|
|
142
|
+
as = "li" as T, // Default to "li" if `as` is not provided
|
|
143
|
+
...props
|
|
144
|
+
}: Props<T> & Omit<React.ComponentPropsWithoutRef<T>, keyof Props<T>>) => {
|
|
145
|
+
const Component = asChild ? Slot : as;
|
|
146
|
+
const ref = useRef<HTMLLIElement>(null);
|
|
147
|
+
|
|
148
|
+
// Scroll to the selected item when the dropdown is opened
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (active && ref.current) {
|
|
151
|
+
ref.current.scrollIntoView({ behavior: "auto", block: "center" });
|
|
152
|
+
}
|
|
153
|
+
}, [active]);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<Component
|
|
157
|
+
{...(props as React.ComponentPropsWithoutRef<T>)} // Spread the props dynamically
|
|
158
|
+
className={cn(
|
|
159
|
+
PopoverItemStyles({
|
|
160
|
+
variant,
|
|
161
|
+
size,
|
|
162
|
+
active,
|
|
163
|
+
}),
|
|
164
|
+
className
|
|
165
|
+
)}
|
|
166
|
+
/// <reference path="" />
|
|
167
|
+
ref={ref}
|
|
168
|
+
>
|
|
169
|
+
{children}
|
|
170
|
+
</Component>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverItem };
|
|
176
|
+
|
|
177
|
+
export const PopoverItemStyles = cva(
|
|
178
|
+
[
|
|
179
|
+
"text-content-presentation-action-light-primary",
|
|
180
|
+
"outline-none",
|
|
181
|
+
"border",
|
|
182
|
+
"border-transparent",
|
|
183
|
+
"flex",
|
|
184
|
+
"gap-[8px]",
|
|
185
|
+
"items-center",
|
|
186
|
+
"justify-start",
|
|
187
|
+
"text-overflow",
|
|
188
|
+
"overflow-hidden",
|
|
189
|
+
"px-[12px]",
|
|
190
|
+
"rounded-[4px]",
|
|
191
|
+
"transition-all",
|
|
192
|
+
"ease-in-out",
|
|
193
|
+
"duration-300",
|
|
194
|
+
],
|
|
195
|
+
{
|
|
196
|
+
variants: {
|
|
197
|
+
variant: {
|
|
198
|
+
Default: [
|
|
199
|
+
"text-content-presentation-action-light-primary",
|
|
200
|
+
"bg-background-presentation-action-dropdown-primary",
|
|
201
|
+
"hover:bg-background-presentation-action-hover",
|
|
202
|
+
"hover:text-content-presentation-action-hover",
|
|
203
|
+
"focus:bg-background-presentation-action-selected",
|
|
204
|
+
"focus:text-content-presentation-action-light-primary",
|
|
205
|
+
"active:border-border-presentation-action-disabled",
|
|
206
|
+
"active:bg-background-presentation-action-selected",
|
|
207
|
+
"active:text-content-presentation-action-light-primary",
|
|
208
|
+
"active:border-border-presentation-action-disabled",
|
|
209
|
+
"disabled:text-content-presentation-state-disabled",
|
|
210
|
+
"disabled:bg-white-00",
|
|
211
|
+
],
|
|
212
|
+
Warning: [
|
|
213
|
+
"bg-background-presentation-action-dropdown-primary",
|
|
214
|
+
"text-content-presentation-state-information",
|
|
215
|
+
"hover:bg-background-presentation-state-information-primary",
|
|
216
|
+
"hover:text-content-presentation-action-hover",
|
|
217
|
+
],
|
|
218
|
+
Negative: [
|
|
219
|
+
"bg-background-presentation-action-dropdown-primary",
|
|
220
|
+
"text-content-presentation-state-negative",
|
|
221
|
+
"hover:bg-background-presentation-state-negative-primary",
|
|
222
|
+
"hover:!text-content-presentation-action-hover",
|
|
223
|
+
"focus:text-content-presentation-state-negative",
|
|
224
|
+
"active:text-content-presentation-state-negative",
|
|
225
|
+
],
|
|
226
|
+
SystemStyle: [
|
|
227
|
+
"bg-background-system-body-primary",
|
|
228
|
+
"text-content-system-global-primary",
|
|
229
|
+
"hover:!bg-background-system-action-secondary-hover",
|
|
230
|
+
"hover:!text-content-system-action-primary-hover",
|
|
231
|
+
"hover:!border-border-system-action-primary-hover",
|
|
232
|
+
"focus:bg-background-System-Action-Primary-Selected",
|
|
233
|
+
"focus:border-transparent",
|
|
234
|
+
"active:border-transparent",
|
|
235
|
+
"active:bg-background-System-Action-Primary-Selected",
|
|
236
|
+
"disabled:bg-background-system-body-secondary",
|
|
237
|
+
"disabled:text-content-system-global-disabled",
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
size: {
|
|
241
|
+
S: ["typography-body-small-regular", "h-[24px]"],
|
|
242
|
+
M: ["typography-body-medium-regular", "h-[32px]"],
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
disabled: {
|
|
246
|
+
true: [
|
|
247
|
+
"text-content-presentation-state-disabled",
|
|
248
|
+
"bg-white-00",
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
active: {
|
|
253
|
+
true: [
|
|
254
|
+
"bg-background-presentation-action-selected",
|
|
255
|
+
"text-content-presentation-action-light-primary",
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
defaultVariants: {
|
|
260
|
+
variant: "Default",
|
|
261
|
+
size: "M",
|
|
262
|
+
active: false,
|
|
263
|
+
disabled: false,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
compoundVariants: [
|
|
267
|
+
{
|
|
268
|
+
active: true,
|
|
269
|
+
variant: "Warning",
|
|
270
|
+
className: ["text-content-presentation-state-negative"],
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
}
|
|
274
|
+
);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode, useEffect, useState } from "react";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./Popover";
|
|
5
|
+
|
|
6
|
+
// Define the base styles and variants using `cva`
|
|
7
|
+
export const profileItemStyles = cva(
|
|
8
|
+
"flex items-center justify-between group w-full p-[6px] outline-none rounded-[8px] fucus:bg-background-system-action-primary-selected active:bg-background-system-action-primary-selected transition-all ease-in-out duration-150",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
selected: {
|
|
12
|
+
true: "bg-background-system-action-primary-selected",
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
interface ProfileItemProps extends HTMLAttributes<HTMLButtonElement> {
|
|
19
|
+
label: ReactNode;
|
|
20
|
+
selected?: boolean
|
|
21
|
+
icon?: string;
|
|
22
|
+
theme?: "dark" | "light" | "default";
|
|
23
|
+
popoverChildren?: ReactNode
|
|
24
|
+
overlayBlur?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ProfileMenu: React.FC<ProfileItemProps> = ({
|
|
28
|
+
label,
|
|
29
|
+
selected,
|
|
30
|
+
icon,
|
|
31
|
+
theme,
|
|
32
|
+
className,
|
|
33
|
+
popoverChildren,
|
|
34
|
+
overlayBlur,
|
|
35
|
+
...props
|
|
36
|
+
}) => {
|
|
37
|
+
|
|
38
|
+
const [isOpen, setIsOpen] = useState(selected);
|
|
39
|
+
const [dropdownWidth, setDropdownWidth] = useState(0);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setIsOpen(selected);
|
|
43
|
+
}, [selected])
|
|
44
|
+
return (
|
|
45
|
+
<Popover onOpenChange={(open) => setIsOpen(open)} >
|
|
46
|
+
<PopoverTrigger asChild>
|
|
47
|
+
<button
|
|
48
|
+
{...props}
|
|
49
|
+
data-theme={theme}
|
|
50
|
+
className={cn(
|
|
51
|
+
profileItemStyles({ selected }),
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
onPointerDown={(e: any) => {
|
|
55
|
+
props.onPointerDown && props.onPointerDown(e);
|
|
56
|
+
setDropdownWidth(e.currentTarget.offsetWidth);
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<div className="flex items-center gap-2">
|
|
60
|
+
<div className="flex items-center">
|
|
61
|
+
<span className="h-[28px] w-[2px] bg-transparent group-hover:bg-[#9748FF] group-hover:mr-[6px] transition-all ease-in-out duration-150"></span>
|
|
62
|
+
<img className="w-[28px] h-[28px] rounded-full object-cover" src={icon} alt="" />
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<p
|
|
66
|
+
className={cn(
|
|
67
|
+
"typography-body-medium-medium text-content-system-global-primary",
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{label}
|
|
71
|
+
</p>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
<i className={cn("ri-arrow-down-s-line text-[18px] text-content-system-global-primary group-hover:text-[#9748FF] transition-all ease-in-out duration-150", { "rotate-180": isOpen && popoverChildren })}></i>
|
|
76
|
+
</button>
|
|
77
|
+
</PopoverTrigger>
|
|
78
|
+
{
|
|
79
|
+
popoverChildren &&
|
|
80
|
+
<PopoverContent
|
|
81
|
+
overlayBlur={overlayBlur}
|
|
82
|
+
style={{ width: `${dropdownWidth}px` }}
|
|
83
|
+
theme={theme}>
|
|
84
|
+
{popoverChildren}
|
|
85
|
+
</PopoverContent>
|
|
86
|
+
}
|
|
87
|
+
</Popover>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|