diff --git a/src/components/messageStream/overlays/ImageLightbox.tsx b/src/components/messageStream/overlays/ImageLightbox.tsx index c3237af..62d9db6 100644 --- a/src/components/messageStream/overlays/ImageLightbox.tsx +++ b/src/components/messageStream/overlays/ImageLightbox.tsx @@ -121,7 +121,7 @@ function LightboxView({ return createPortal(
-
+
{index + 1} / {images.length}
) : null}
e.stopPropagation()} - onTouchStart={(e) => { - touchStartX.current = e.touches[0].clientX; - }} - onTouchEnd={(e) => { - if (touchStartX.current == null) return; - const dx = e.changedTouches[0].clientX - touchStartX.current; - if (Math.abs(dx) > 40) { - if (dx > 0) goPrev(); - else goNext(); - } - touchStartX.current = null; - }} + className={`flex min-h-0 w-full flex-1 items-center justify-center px-4 pt-16 ${ + caption ? "pb-3" : "pb-16" + }`} > - {current.filename} - {caption ? ( -
-
- {autolink(caption)} -
-
- ) : null} +
e.stopPropagation()} + onTouchStart={(e) => { + touchStartX.current = e.touches[0].clientX; + }} + onTouchEnd={(e) => { + if (touchStartX.current == null) return; + const dx = e.changedTouches[0].clientX - touchStartX.current; + if (Math.abs(dx) > 40) { + if (dx > 0) goPrev(); + else goNext(); + } + touchStartX.current = null; + }} + > + {current.filename} +
+ + {caption ? ( +
e.stopPropagation()} + > +
+ {autolink(caption)} +
+
+ ) : null}
, document.body, ); diff --git a/src/layouts/PublicLayout.tsx b/src/layouts/PublicLayout.tsx index ddf3d1d..6ba44eb 100644 --- a/src/layouts/PublicLayout.tsx +++ b/src/layouts/PublicLayout.tsx @@ -1,5 +1,12 @@ -import { Globe, Menu, Search as SearchIcon, X } from "lucide-react"; -import { useState } from "react"; +import { + Check, + ChevronDown, + Globe, + Menu, + Search as SearchIcon, + X, +} from "lucide-react"; +import { useEffect, useRef, useState } from "react"; import { Link, Outlet, useLocation, useNavigate } from "react-router-dom"; import { ArkLogoMark } from "../components/ArkLogoMark"; import { useI18n, type Lang } from "../i18n"; @@ -55,6 +62,105 @@ function navClassName(active: boolean) { ].join(" "); } +type LanguageDropdownProps = { + lang: Lang; + setLang: (lang: Lang) => void; + ariaLabel: string; + className?: string; +}; + +function LanguageDropdown({ + lang, + setLang, + ariaLabel, + className = "", +}: LanguageDropdownProps) { + const [open, setOpen] = useState(false); + const rootRef = useRef(null); + const selected = LANG_OPTIONS.find((option) => option.code === lang); + + useEffect(() => { + if (!open) return; + + const closeOnOutside = (event: MouseEvent | TouchEvent) => { + if (!rootRef.current?.contains(event.target as Node)) setOpen(false); + }; + const closeOnEscape = (event: KeyboardEvent) => { + if (event.key === "Escape") setOpen(false); + }; + + document.addEventListener("mousedown", closeOnOutside); + document.addEventListener("touchstart", closeOnOutside); + window.addEventListener("keydown", closeOnEscape); + return () => { + document.removeEventListener("mousedown", closeOnOutside); + document.removeEventListener("touchstart", closeOnOutside); + window.removeEventListener("keydown", closeOnEscape); + }; + }, [open]); + + return ( +
+ + + {open ? ( +
+ {LANG_OPTIONS.map((option) => { + const active = option.code === lang; + return ( + + ); + })} +
+ ) : null} +
+ ); +} + export function PublicLayout() { const { t, lang, setLang } = useI18n(); const { pathname, search, hash } = useLocation(); @@ -154,25 +260,12 @@ export function PublicLayout() { className="min-w-0 flex-1 rounded-md bg-transparent text-sm text-neutral-200 outline-none placeholder:text-[#777985] focus-visible:ring-2 focus-visible:ring-ark-gold/60 focus-visible:ring-offset-2 focus-visible:ring-offset-[#1a1b20]" />
-
- - -
+