()
| 51 | } |
| 52 | |
| 53 | export function SettingsMenu() { |
| 54 | const t = useT(); |
| 55 | const { locale, setLocale } = useLocale(); |
| 56 | const { show, setShow } = useSelfRefs(); |
| 57 | const { open, toggle, ref } = usePopover(); |
| 58 | |
| 59 | return ( |
| 60 | <div className="relative" ref={ref}> |
| 61 | <Button |
| 62 | variant="ghost" |
| 63 | size="sm" |
| 64 | onClick={toggle} |
| 65 | title={t("settings.title")} |
| 66 | aria-label={t("settings.title")} |
| 67 | aria-haspopup="dialog" |
| 68 | aria-expanded={open} |
| 69 | className="px-2" |
| 70 | > |
| 71 | <Settings className="h-4 w-4" /> |
| 72 | </Button> |
| 73 | |
| 74 | {open && ( |
| 75 | <div |
| 76 | role="dialog" |
| 77 | aria-label={t("settings.title")} |
| 78 | className="absolute right-0 top-full mt-1.5 w-64 rounded-lg border bg-popover text-popover-foreground shadow-lg p-3 z-50" |
| 79 | > |
| 80 | <SectionLabel>{t("settings.section.display")}</SectionLabel> |
| 81 | |
| 82 | {/* 界面语言 */} |
| 83 | <div className="flex items-center justify-between mb-2.5"> |
| 84 | <span className="text-sm">{t("settings.language")}</span> |
| 85 | <div className="flex gap-0.5 rounded-md bg-muted p-0.5"> |
| 86 | <Segment active={locale === "zh"} onClick={() => locale !== "zh" && setLocale("zh")}> |
| 87 | {t("locale.toggle_to_zh")} |
| 88 | </Segment> |
| 89 | <Segment active={locale === "en"} onClick={() => locale !== "en" && setLocale("en")}> |
| 90 | {t("locale.toggle_to_en")} |
| 91 | </Segment> |
| 92 | </div> |
| 93 | </div> |
| 94 | |
| 95 | {/* § 段落引用 */} |
| 96 | <div className="flex items-center justify-between"> |
| 97 | <span className="text-sm">{t("settings.self_refs")}</span> |
| 98 | <div className="flex gap-0.5 rounded-md bg-muted p-0.5"> |
| 99 | <Segment active={show} onClick={() => !show && setShow(true)}> |
| 100 | {t("common.on")} |
| 101 | </Segment> |
| 102 | <Segment active={!show} onClick={() => show && setShow(false)}> |
| 103 | {t("common.off")} |
| 104 | </Segment> |
| 105 | </div> |
| 106 | </div> |
| 107 | </div> |
| 108 | )} |
| 109 | </div> |
| 110 | ); |
nothing calls this directly
no test coverage detected