From 4eff829a30f9e94ce55912cb3f33fa35f1f3992b Mon Sep 17 00:00:00 2001 From: CrescentLeaf Date: Tue, 7 Oct 2025 22:31:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=85=8D=E7=BD=AE=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/ui/preference/Preference.tsx | 13 +++++++ client/ui/preference/PreferenceHeader.tsx | 3 ++ client/ui/preference/PreferenceLayout.tsx | 8 +++++ client/ui/preference/PreferenceStore.ts | 23 ++++++++++++ client/ui/preference/SelectPreference.tsx | 38 ++++++++++++++++++++ client/ui/preference/SwitchPreference.tsx | 19 ++++++++++ client/ui/preference/TextFieldPreference.tsx | 30 ++++++++++++++++ 7 files changed, 134 insertions(+) create mode 100644 client/ui/preference/Preference.tsx create mode 100644 client/ui/preference/PreferenceHeader.tsx create mode 100644 client/ui/preference/PreferenceLayout.tsx create mode 100644 client/ui/preference/PreferenceStore.ts create mode 100644 client/ui/preference/SelectPreference.tsx create mode 100644 client/ui/preference/SwitchPreference.tsx create mode 100644 client/ui/preference/TextFieldPreference.tsx diff --git a/client/ui/preference/Preference.tsx b/client/ui/preference/Preference.tsx new file mode 100644 index 0000000..b3811d7 --- /dev/null +++ b/client/ui/preference/Preference.tsx @@ -0,0 +1,13 @@ +import { $ } from 'mdui/jq' +import { Switch } from 'mdui' +import React from 'react' +import useEventListener from '../useEventListener.ts' + +export default function Preference({ title, icon, disabled, description, ...props } = { + disabled: false, +}) { + return + {title} + {description && {description}} + +} \ No newline at end of file diff --git a/client/ui/preference/PreferenceHeader.tsx b/client/ui/preference/PreferenceHeader.tsx new file mode 100644 index 0000000..c6ca694 --- /dev/null +++ b/client/ui/preference/PreferenceHeader.tsx @@ -0,0 +1,3 @@ +export default function PreferenceHeader({ title }) { + return {title} +} \ No newline at end of file diff --git a/client/ui/preference/PreferenceLayout.tsx b/client/ui/preference/PreferenceLayout.tsx new file mode 100644 index 0000000..a34a5ad --- /dev/null +++ b/client/ui/preference/PreferenceLayout.tsx @@ -0,0 +1,8 @@ +export default function PreferenceLayout({ children, ...props }) { + return + {children} + +} \ No newline at end of file diff --git a/client/ui/preference/PreferenceStore.ts b/client/ui/preference/PreferenceStore.ts new file mode 100644 index 0000000..5f2a8b5 --- /dev/null +++ b/client/ui/preference/PreferenceStore.ts @@ -0,0 +1,23 @@ +import React from 'react' + +export default class PreferenceStore { + constructor() { + const _ = React.useState<{ [key: string]: unknown }>({}) + this.value = _[0] + this.setter = _[1] + } + // 创建一个用于子选项的更新函数 + updater(key: string) { + return (value: unknown) => { + const newValue = JSON.parse(JSON.stringify({ + ...this.value, + [key]: value, + })) + this.setter(newValue) + this.onUpdate?.(newValue) + } + } + setOnUpdate(onUpdate) { + this.onUpdate = onUpdate + } +} diff --git a/client/ui/preference/SelectPreference.tsx b/client/ui/preference/SelectPreference.tsx new file mode 100644 index 0000000..f01a2be --- /dev/null +++ b/client/ui/preference/SelectPreference.tsx @@ -0,0 +1,38 @@ +import { $ } from 'mdui/jq' +import React from 'react' +import { Dropdown } from 'mdui' +import useEventListener from '../useEventListener.ts' + +// value as { [id: string]: string } +export default function SelectPreference({ title, icon, updater, selections, defaultCheckedId, disabled } = { + disabled: false, +}) { + const [ checkedId, setCheckedId ] = React.useState(defaultCheckedId) + + const dropDownRef = React.useRef(null) + const [isDropDownOpen, setDropDownOpen] = React.useState(false) + + useEventListener(dropDownRef, 'closed', (e) => { + setDropDownOpen(false) + }) + + return setDropDownOpen(!isDropDownOpen)}> + + { title } + { + e.stopPropagation() + setDropDownOpen(false) + }}> + { + Object.keys(selections).map((id) => + { + setCheckedId(id) + updater(id) + }}>{selections[id]} + ) + } + + + { selections[checkedId] } + +} \ No newline at end of file diff --git a/client/ui/preference/SwitchPreference.tsx b/client/ui/preference/SwitchPreference.tsx new file mode 100644 index 0000000..69bb271 --- /dev/null +++ b/client/ui/preference/SwitchPreference.tsx @@ -0,0 +1,19 @@ +import { $ } from 'mdui/jq' +import { Switch } from 'mdui' +import React from 'react' +import useEventListener from '../useEventListener.ts' + +export default function SwitchPreference({ title, icon, updater, disabled, description } = { + disabled: false, +}) { + const switchRef = React.useRef(null) + + return { + switchRef.current!.checked = !switchRef.current!.checked + updater(switchRef.current!.checked) + }}> + {title} + {description && {description}} + e.preventDefault()}> + +} \ No newline at end of file diff --git a/client/ui/preference/TextFieldPreference.tsx b/client/ui/preference/TextFieldPreference.tsx new file mode 100644 index 0000000..ac96e5d --- /dev/null +++ b/client/ui/preference/TextFieldPreference.tsx @@ -0,0 +1,30 @@ +import { $ } from 'mdui/jq' +import React from 'react' +import { TextField, prompt } from 'mdui' +import useEventListener from '../useEventListener.ts' + +export default function TextFieldPreference({ title, icon, description, updater, defaultValue, disabled } = { + disabled: false, +}) { + const [ text, setText ] = React.useState(defaultValue) + + return { + prompt({ + headline: title, + confirmText: "确定", + cancelText: "取消", + onConfirm: (value) => { + setText(value) + updater(value) + }, + onCancel: () => {}, + textFieldOptions: { + label: description, + value: text, + }, + }) + }}> + {title} + {description && {description}} + +} \ No newline at end of file