tangled
alpha
login
or
join now
isuggest.selfce.st
/
strand
3
fork
atom
alternative tangled frontend (extremely wip)
3
fork
atom
overview
issues
pulls
pipelines
feat: dropdown modal
serenity
2 weeks ago
61c09feb
bbad2d0c
+79
-7
2 changed files
expand all
collapse all
unified
split
src
components
Dropdown
DropdownModal.tsx
Nav
NavBarAuthed.tsx
+48
src/components/Dropdown/DropdownModal.tsx
···
1
1
+
import { useModalEscapeEffect } from "@/lib/hooks/useModalEscapeEffect";
2
2
+
import { useModalMousedownEffect } from "@/lib/hooks/useModalMousedownEffect";
3
3
+
import { AnimatePresence, motion } from "motion/react";
4
4
+
import { ReactNode, useRef, useState } from "react";
5
5
+
6
6
+
export const DropdownModal = ({
7
7
+
buttonComponent,
8
8
+
children,
9
9
+
className,
10
10
+
}: {
11
11
+
buttonComponent: ReactNode;
12
12
+
children: ReactNode;
13
13
+
className?: string;
14
14
+
}) => {
15
15
+
const [showDropdown, setShowDropdown] = useState(false);
16
16
+
17
17
+
const dropdownRef = useRef<HTMLDivElement>(null);
18
18
+
19
19
+
useModalEscapeEffect({ setShowModal: setShowDropdown });
20
20
+
useModalMousedownEffect({
21
21
+
setShowModal: setShowDropdown,
22
22
+
modalRef: dropdownRef,
23
23
+
});
24
24
+
25
25
+
return (
26
26
+
<div ref={dropdownRef} className="relative inline-block">
27
27
+
<button
28
28
+
className="cursor-pointer"
29
29
+
onClick={() => setShowDropdown((prev) => !prev)}
30
30
+
>
31
31
+
{buttonComponent}
32
32
+
</button>
33
33
+
<AnimatePresence>
34
34
+
{showDropdown && (
35
35
+
<motion.div
36
36
+
initial={{ opacity: 0, x: 8 }}
37
37
+
animate={{ opacity: 1, x: 0 }}
38
38
+
exit={{ opacity: 0, x: 8 }}
39
39
+
transition={{ duration: 0.1, ease: "easeOut" }}
40
40
+
className={`absolute right-0 z-50 origin-top-right cursor-default ${className}`}
41
41
+
>
42
42
+
{children}
43
43
+
</motion.div>
44
44
+
)}
45
45
+
</AnimatePresence>
46
46
+
</div>
47
47
+
);
48
48
+
};
+31
-7
src/components/Nav/NavBarAuthed.tsx
···
1
1
import { UnderlineIconRouterLink } from "@/components/Animated/UnderlineIconRouterLink";
2
2
+
import { DropdownModal } from "@/components/Dropdown/DropdownModal";
2
3
import { StrandIcon } from "@/components/Icons/Branding/StrandIcon";
3
4
import { Loading } from "@/components/Icons/Loading";
4
4
-
import { LucideLogIn } from "@/components/Icons/LucideLogIn";
5
5
+
import { Avatar } from "@/components/Profile/Avatar";
6
6
+
import { useModalEscapeEffect } from "@/lib/hooks/useModalEscapeEffect";
7
7
+
import { useModalMousedownEffect } from "@/lib/hooks/useModalMousedownEffect";
5
8
import { useOAuth } from "@/lib/oauth";
6
9
import { useAvatarQuery } from "@/lib/queries/get-avatar";
7
7
-
import { err } from "@/lib/result";
10
10
+
import { AnimatePresence, motion } from "motion/react";
11
11
+
import { useEffect, useRef, useState } from "react";
8
12
9
13
export const NavBarAuthed = () => {
10
14
const oauth = useOAuth();
···
17
21
error: avatarQueryError,
18
22
} = useAvatarQuery({ did, repoUrl });
19
23
20
20
-
const avatarSrc = avatarQueryData === "#" ? undefined : avatarQueryData;
24
24
+
const avatarUri = avatarQueryData === "#" ? undefined : avatarQueryData;
21
25
22
26
return (
23
27
<div className="bg-surface0 flex w-full items-center justify-between p-3">
···
39
43
) : avatarQueryError ? (
40
44
<p>{avatarQueryError.message}</p>
41
45
) : (
42
42
-
<img
43
43
-
src={avatarSrc}
44
44
-
className="outline-accent h-10 rounded-full outline"
45
45
-
/>
46
46
+
<AvatarWithDropdown uri={avatarUri} />
46
47
)}
47
48
</button>
48
49
</div>
49
50
</div>
50
51
);
51
52
};
53
53
+
54
54
+
const AvatarWithDropdown = ({ uri }: { uri: string | undefined }) => {
55
55
+
const [showDropdown, setShowDropdown] = useState(false);
56
56
+
57
57
+
const dropdownRef = useRef<HTMLDivElement>(null);
58
58
+
59
59
+
useModalEscapeEffect({ setShowModal: setShowDropdown });
60
60
+
useModalMousedownEffect({
61
61
+
setShowModal: setShowDropdown,
62
62
+
modalRef: dropdownRef,
63
63
+
});
64
64
+
65
65
+
const AvatarButton = <Avatar uri={uri} />;
66
66
+
67
67
+
return (
68
68
+
<DropdownModal
69
69
+
buttonComponent={AvatarButton}
70
70
+
className="bg-surface0 mt-3.5 w-72 rounded-lg p-2"
71
71
+
>
72
72
+
<p>Hello</p>
73
73
+
</DropdownModal>
74
74
+
);
75
75
+
};