mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
feat: add search clear functionality and improve search input styling
This commit is contained in:
@@ -828,6 +828,7 @@ function folderName(id: string | null | undefined): string {
|
|||||||
sortMenuRef={sortMenuRef}
|
sortMenuRef={sortMenuRef}
|
||||||
listPanelRef={listPanelRef}
|
listPanelRef={listPanelRef}
|
||||||
onSearchInput={setSearchInput}
|
onSearchInput={setSearchInput}
|
||||||
|
onClearSearch={() => setSearchInput('')}
|
||||||
onSearchCompositionStart={() => setSearchComposing(true)}
|
onSearchCompositionStart={() => setSearchComposing(true)}
|
||||||
onSearchCompositionEnd={(value) => {
|
onSearchCompositionEnd={(value) => {
|
||||||
setSearchComposing(false);
|
setSearchComposing(false);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ interface VaultListPanelProps {
|
|||||||
sortMenuRef: RefObject<HTMLDivElement>;
|
sortMenuRef: RefObject<HTMLDivElement>;
|
||||||
listPanelRef: RefObject<HTMLDivElement>;
|
listPanelRef: RefObject<HTMLDivElement>;
|
||||||
onSearchInput: (value: string) => void;
|
onSearchInput: (value: string) => void;
|
||||||
|
onClearSearch: () => void;
|
||||||
onSearchCompositionStart: () => void;
|
onSearchCompositionStart: () => void;
|
||||||
onSearchCompositionEnd: (value: string) => void;
|
onSearchCompositionEnd: (value: string) => void;
|
||||||
onToggleSortMenu: () => void;
|
onToggleSortMenu: () => void;
|
||||||
@@ -62,14 +63,32 @@ export default function VaultListPanel(props: VaultListPanelProps) {
|
|||||||
return (
|
return (
|
||||||
<section className="list-col">
|
<section className="list-col">
|
||||||
<div className="list-head">
|
<div className="list-head">
|
||||||
<input
|
<div className="search-input-wrap">
|
||||||
className="search-input"
|
<input
|
||||||
placeholder={t('txt_search_your_secure_vault')}
|
className="search-input"
|
||||||
value={props.searchInput}
|
placeholder={t('txt_search_your_secure_vault')}
|
||||||
onInput={(e) => props.onSearchInput((e.currentTarget as HTMLInputElement).value)}
|
value={props.searchInput}
|
||||||
onCompositionStart={props.onSearchCompositionStart}
|
onInput={(e) => props.onSearchInput((e.currentTarget as HTMLInputElement).value)}
|
||||||
onCompositionEnd={(e) => props.onSearchCompositionEnd((e.currentTarget as HTMLInputElement).value)}
|
onCompositionStart={props.onSearchCompositionStart}
|
||||||
/>
|
onCompositionEnd={(e) => props.onSearchCompositionEnd((e.currentTarget as HTMLInputElement).value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key !== 'Escape' || !props.searchInput) return;
|
||||||
|
e.preventDefault();
|
||||||
|
props.onClearSearch();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!!props.searchInput && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="search-clear-btn"
|
||||||
|
aria-label={t('txt_clear_search')}
|
||||||
|
title={t('txt_clear_search_esc')}
|
||||||
|
onClick={props.onClearSearch}
|
||||||
|
>
|
||||||
|
<X size={14} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="sort-menu-wrap" ref={props.sortMenuRef}>
|
<div className="sort-menu-wrap" ref={props.sortMenuRef}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -546,6 +546,8 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
txt_save_profile_failed: "Save profile failed",
|
txt_save_profile_failed: "Save profile failed",
|
||||||
txt_search_sends: "Search sends...",
|
txt_search_sends: "Search sends...",
|
||||||
txt_search_your_secure_vault: "Search your secure vault...",
|
txt_search_your_secure_vault: "Search your secure vault...",
|
||||||
|
txt_clear_search: "Clear search",
|
||||||
|
txt_clear_search_esc: "Clear search (Esc)",
|
||||||
txt_sort: "Sort",
|
txt_sort: "Sort",
|
||||||
txt_sort_last_edited: "Modified",
|
txt_sort_last_edited: "Modified",
|
||||||
txt_sort_created: "Created",
|
txt_sort_created: "Created",
|
||||||
@@ -872,6 +874,8 @@ const zhCNOverrides: Record<string, string> = {
|
|||||||
txt_loading_nodewarden: '正在加载 NodeWarden...',
|
txt_loading_nodewarden: '正在加载 NodeWarden...',
|
||||||
txt_search_sends: '搜索发送...',
|
txt_search_sends: '搜索发送...',
|
||||||
txt_search_your_secure_vault: '搜索你的密码库...',
|
txt_search_your_secure_vault: '搜索你的密码库...',
|
||||||
|
txt_clear_search: '清空搜索',
|
||||||
|
txt_clear_search_esc: '清空搜索(Esc)',
|
||||||
txt_refresh: '刷新',
|
txt_refresh: '刷新',
|
||||||
txt_sync: '同步',
|
txt_sync: '同步',
|
||||||
txt_sync_vault: '同步',
|
txt_sync_vault: '同步',
|
||||||
|
|||||||
+59
-9
@@ -1021,12 +1021,15 @@ input[type='file'].input::file-selector-button:hover {
|
|||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40px;
|
height: 48px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid rgba(74, 103, 150, 0.42);
|
||||||
border-radius: 12px;
|
border-radius: 14px;
|
||||||
padding: 0 12px;
|
padding: 10px 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||||
transition:
|
transition:
|
||||||
border-color var(--dur-fast) var(--ease-smooth),
|
border-color var(--dur-fast) var(--ease-smooth),
|
||||||
box-shadow var(--dur-fast) var(--ease-out-soft),
|
box-shadow var(--dur-fast) var(--ease-out-soft),
|
||||||
@@ -1034,13 +1037,52 @@ input[type='file'].input::file-selector-button:hover {
|
|||||||
transform var(--dur-fast) var(--ease-out-soft);
|
transform var(--dur-fast) var(--ease-out-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-input-wrap {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.search-input:focus {
|
.search-input:focus {
|
||||||
border-color: rgba(43, 102, 217, 0.28);
|
border-color: rgba(43, 102, 217, 0.6);
|
||||||
background: #fbfdff;
|
background-color: #fbfdff;
|
||||||
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.08), 0 8px 18px rgba(37, 99, 235, 0.06);
|
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-input-wrap .search-input {
|
||||||
|
padding-right: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 9px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(148, 163, 184, 0.18);
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
transition: background-color var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft), transform var(--dur-fast) var(--ease-out-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear-btn:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.18);
|
||||||
|
color: var(--brand);
|
||||||
|
transform: translateY(-50%) scale(1.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear-btn:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
.tree-btn {
|
.tree-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -1151,10 +1193,13 @@ input[type='file'].input::file-selector-button:hover {
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-head .search-input {
|
.list-head .search-input-wrap {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 36px;
|
}
|
||||||
|
|
||||||
|
.list-head .search-input {
|
||||||
|
height: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-head .btn {
|
.list-head .btn {
|
||||||
@@ -3733,6 +3778,11 @@ input[type='file'].input::file-selector-button:hover {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-head .search-input-wrap {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.list-head .search-input {
|
.list-head .search-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user