From 5eb38a974bf3f2ff017421cac2f0fc7e85ebcc4f Mon Sep 17 00:00:00 2001 From: hectorqin Date: Wed, 11 Nov 2026 06:01:07 +0000 Subject: [PATCH] feat(web): one themed drop-down everywhere instead of native selects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MediaSelect moves to ui/select.tsx as Select (old name kept as an alias); its colours fall back to the theme tokens outside 影音 - sources, reader panels and admin pages use it; business settings default to it - a test rejects a native `(手机上系统的选择器比自绘的好用);影音里已经自绘的 `MediaSelect`、`SearchableSelect` 不受影响。 +- 下拉框只有一种:`Select`(`web/src/ui/select.tsx`,样式 `styles/select.css`),用法与 `` 仍在 DOM 里但不可见,值、表单校验和按 `select[aria-label=…]` 取值的测试都还落在它上面;看得见的是一个 `role="combobox"` 的按钮和浮在顶层的选项列表(在 Sheet 里也能用,Esc 只收起列表)。界面里不再直接写 `{event.preventDefault();trigger.current?.focus();show();onClick?.(event);}} onFocus={event=>{trigger.current?.focus();onFocus?.(event);}} onInvalid={event=>{event.preventDefault();setValidation(event.currentTarget.validationMessage||'请选择一项');trigger.current?.focus();onInvalid?.(event);}} onChange={event=>{setValidation('');sync();onChange?.(event);}}>{children} - - {validation&&{validation}} - {open&&
event.preventDefault()}> - {choices.map((choice,index)=>
!choice.disabled&&setActive(index)} onClick={event=>{event.preventDefault();event.stopPropagation();choose(index);}}>{choice.group&&{choice.group}}{choice.label}{choice.value===selected&&}
)} -
} - ; -} +/** Lives in ui/ now (every screen uses it); kept here so media imports stay put. */ +export { MediaSelect } from '../../../ui/select.tsx'; diff --git a/web/src/features/sources/pages/SourceCatalogPage.tsx b/web/src/features/sources/pages/SourceCatalogPage.tsx index 99fabbe..7b91c2d 100644 --- a/web/src/features/sources/pages/SourceCatalogPage.tsx +++ b/web/src/features/sources/pages/SourceCatalogPage.tsx @@ -1,3 +1,4 @@ +import { Select } from '../../../ui/select.tsx'; import { notify } from '../../../ui/notifications.ts'; import { Button, Callout } from '../../../ui/toolkit.tsx'; import { SourcesHeader } from '../components/SourcesHeader.tsx'; @@ -58,14 +59,14 @@ export function SourceCatalogPage() { finally { setBusy(false); } }; if (sources.isPending) return
; - if (!source) return

选择来源

选择一个已启用的书源开始搜索或浏览

; + if (!source) return

选择来源

选择一个已启用的书源开始搜索或浏览

; const supportsSearch = source.descriptor?.capabilities.includes('search'); const supportsBrowse = source.descriptor?.capabilities.includes('browse'); return
{error && setError('')}>{error}} - {supportsSearch &&
{ event.preventDefault(); void search(false); }}>{filterQuery.data?.map(field => field.type === 'select' ? : null)}{page?.nextCursor && }
} + {supportsSearch &&
{ event.preventDefault(); void search(false); }}>{filterQuery.data?.map(field => field.type === 'select' ? : null)}{page?.nextCursor && }
}
{supportsBrowse && }
{page?.batch &&

已检查 {page.batch.completed} / {page.batch.total} 个来源

} {page?.navigation && } diff --git a/web/src/features/sources/pages/SourceEditorPage.tsx b/web/src/features/sources/pages/SourceEditorPage.tsx index 8ace764..a1a24a3 100644 --- a/web/src/features/sources/pages/SourceEditorPage.tsx +++ b/web/src/features/sources/pages/SourceEditorPage.tsx @@ -1,3 +1,4 @@ +import { Select } from '../../../ui/select.tsx'; import { useEffect, useState, type FormEvent } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import type { SourceType } from '../../../api/sources.ts'; @@ -50,17 +51,17 @@ export function SourceEditorPage() {
{sourceId && !source ? 来源不存在 :
{type?.configSchema?.properties ? Object.entries(type.configSchema.properties).map(([key, field]) => field.type === 'boolean' ? : field.enum - ? + ? : ) :