feat: refactor vault component helpers to use dedicated functions for options retrieval

This commit is contained in:
shuaiplus
2026-04-29 15:28:23 +08:00
parent 85147e1569
commit 9c5fbda374
8 changed files with 93 additions and 48 deletions
+3 -2
View File
@@ -1,6 +1,6 @@
import ConfirmDialog from '@/components/ConfirmDialog';
import type { CustomFieldType, Folder } from '@/lib/types';
import { FIELD_TYPE_OPTIONS, toBooleanFieldValue } from '@/components/vault/vault-page-helpers';
import { getFieldTypeOptions, toBooleanFieldValue } from '@/components/vault/vault-page-helpers';
import { t } from '@/lib/i18n';
interface VaultDialogsProps {
@@ -61,6 +61,7 @@ interface VaultDialogsProps {
}
export default function VaultDialogs(props: VaultDialogsProps) {
const fieldTypeOptions = getFieldTypeOptions();
return (
<>
<ConfirmDialog
@@ -75,7 +76,7 @@ export default function VaultDialogs(props: VaultDialogsProps) {
<label className="field">
<span>{t('txt_field_type')}</span>
<select className="input" value={props.fieldType} onInput={(e) => props.onFieldTypeChange(Number((e.currentTarget as HTMLSelectElement).value) as CustomFieldType)}>
{FIELD_TYPE_OPTIONS.map((option) => (
{fieldTypeOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
+6 -4
View File
@@ -21,13 +21,13 @@ import { CSS } from '@dnd-kit/utilities';
import type { Cipher, Folder, VaultDraft, VaultDraftField } from '@/lib/types';
import { t } from '@/lib/i18n';
import {
CREATE_TYPE_OPTIONS,
cipherTypeLabel,
createEmptyLoginUri,
formatAttachmentSize,
formatHistoryTime,
getCreateTypeOptions,
getWebsiteMatchOptions,
toBooleanFieldValue,
WEBSITE_MATCH_OPTIONS,
} from '@/components/vault/vault-page-helpers';
interface VaultEditorProps {
@@ -77,6 +77,7 @@ interface SortableWebsiteRowProps {
}
function SortableWebsiteRow(props: SortableWebsiteRowProps) {
const websiteMatchOptions = getWebsiteMatchOptions();
const { attributes, listeners, setActivatorNodeRef, setNodeRef, transform, transition, isDragging } = useSortable({
id: props.id,
});
@@ -117,7 +118,7 @@ function SortableWebsiteRow(props: SortableWebsiteRowProps) {
props.onUpdateMatch(props.index, raw === '' ? null : Number(raw));
}}
>
{WEBSITE_MATCH_OPTIONS.map((option) => (
{websiteMatchOptions.map((option) => (
<option key={`website-match-${String(option.value)}`} value={option.value == null ? '' : String(option.value)}>
{option.label}
</option>
@@ -134,6 +135,7 @@ function SortableWebsiteRow(props: SortableWebsiteRowProps) {
}
export default function VaultEditor(props: VaultEditorProps) {
const createTypeOptions = getCreateTypeOptions();
const uriIdSeedRef = useRef(0);
const [uriItemIds, setUriItemIds] = useState<string[]>([]);
const [activeUriId, setActiveUriId] = useState<string | null>(null);
@@ -232,7 +234,7 @@ export default function VaultEditor(props: VaultEditorProps) {
if (nextType === 5) props.onSeedSshDefaults();
}}
>
{CREATE_TYPE_OPTIONS.map((option) => (
{createTypeOptions.map((option) => (
<option key={option.type} value={option.type}>
{option.label}
</option>
@@ -6,9 +6,9 @@ import LoadingState from '@/components/LoadingState';
import type { Cipher } from '@/lib/types';
import { t } from '@/lib/i18n';
import {
CREATE_TYPE_OPTIONS,
CreateTypeIcon,
VAULT_SORT_OPTIONS,
getCreateTypeOptions,
getVaultSortOptions,
VaultListIcon,
type SidebarFilter,
type VaultSortMode,
@@ -106,6 +106,8 @@ const CipherListItem = memo(function CipherListItem(props: CipherListItemProps)
});
export default function VaultListPanel(props: VaultListPanelProps) {
const createTypeOptions = getCreateTypeOptions();
const vaultSortOptions = getVaultSortOptions();
const createMenu = (
<div className="create-menu-wrap mobile-fab-wrap" ref={props.createMenuRef}>
<button
@@ -119,7 +121,7 @@ export default function VaultListPanel(props: VaultListPanelProps) {
</button>
{props.createMenuOpen && (
<div className="create-menu">
{CREATE_TYPE_OPTIONS.map((option) => (
{createTypeOptions.map((option) => (
<button key={option.type} type="button" className="create-menu-item" onClick={() => props.onStartCreate(option.type)}>
<CreateTypeIcon type={option.type} />
<span>{option.label}</span>
@@ -171,7 +173,7 @@ export default function VaultListPanel(props: VaultListPanelProps) {
</button>
{props.sortMenuOpen && (
<div className="sort-menu">
{VAULT_SORT_OPTIONS.map((option) => (
{vaultSortOptions.map((option) => (
<button
key={option.value}
type="button"
+3 -2
View File
@@ -21,7 +21,7 @@ import {
} from 'lucide-preact';
import type { Folder } from '@/lib/types';
import { t } from '@/lib/i18n';
import { FOLDER_SORT_OPTIONS, type SidebarFilter, type VaultSortMode } from '@/components/vault/vault-page-helpers';
import { getFolderSortOptions, type SidebarFilter, type VaultSortMode } from '@/components/vault/vault-page-helpers';
interface VaultSidebarProps {
folders: Folder[];
@@ -43,6 +43,7 @@ interface VaultSidebarProps {
}
export default function VaultSidebar(props: VaultSidebarProps) {
const folderSortOptions = getFolderSortOptions();
const nameCollator = useMemo(
() => new Intl.Collator(undefined, { sensitivity: 'base', numeric: true }),
[]
@@ -143,7 +144,7 @@ export default function VaultSidebar(props: VaultSidebarProps) {
</button>
{props.folderSortMenuOpen && (
<div className="sort-menu">
{FOLDER_SORT_OPTIONS.map((option) => (
{folderSortOptions.map((option) => (
<button
key={option.value}
type="button"
@@ -28,45 +28,56 @@ interface TypeOption {
label: string;
}
export const CREATE_TYPE_OPTIONS: TypeOption[] = [
{ type: 1, label: t('txt_login') },
{ type: 3, label: t('txt_card') },
{ type: 4, label: t('txt_identity') },
{ type: 2, label: t('txt_note') },
{ type: 5, label: t('txt_ssh_key') },
];
export function getCreateTypeOptions(): TypeOption[] {
return [
{ type: 1, label: t('txt_login') },
{ type: 3, label: t('txt_card') },
{ type: 4, label: t('txt_identity') },
{ type: 2, label: t('txt_note') },
{ type: 5, label: t('txt_ssh_key') },
];
}
export const VAULT_SORT_STORAGE_KEY = 'nodewarden.vault.sort.v1';
export const FOLDER_SORT_STORAGE_KEY = 'nodewarden.folder-sort.v1';
export const MOBILE_LAYOUT_QUERY = '(max-width: 1180px)';
export const VAULT_LIST_ROW_HEIGHT = 74;
export const VAULT_LIST_OVERSCAN = 10;
export const VAULT_SORT_OPTIONS: Array<{ value: VaultSortMode; label: string }> = [
{ value: 'edited', label: t('txt_sort_last_edited') },
{ value: 'created', label: t('txt_sort_created') },
{ value: 'name', label: t('txt_sort_name') },
];
export const FOLDER_SORT_OPTIONS: Array<{ value: VaultSortMode; label: string }> = [
{ value: 'edited', label: t('txt_sort_last_edited') },
{ value: 'created', label: t('txt_sort_created') },
{ value: 'name', label: t('txt_sort_name') },
];
export function getVaultSortOptions(): Array<{ value: VaultSortMode; label: string }> {
return [
{ value: 'edited', label: t('txt_sort_last_edited') },
{ value: 'created', label: t('txt_sort_created') },
{ value: 'name', label: t('txt_sort_name') },
];
}
export const FIELD_TYPE_OPTIONS: Array<{ value: CustomFieldType; label: string }> = [
{ value: 0, label: t('txt_text') },
{ value: 1, label: t('txt_hidden') },
{ value: 2, label: t('txt_boolean') },
];
export function getFolderSortOptions(): Array<{ value: VaultSortMode; label: string }> {
return [
{ value: 'edited', label: t('txt_sort_last_edited') },
{ value: 'created', label: t('txt_sort_created') },
{ value: 'name', label: t('txt_sort_name') },
];
}
export const WEBSITE_MATCH_OPTIONS: Array<{ value: number | null; label: string }> = [
{ value: null, label: t('txt_uri_match_default_base_domain') },
{ value: 0, label: t('txt_uri_match_base_domain') },
{ value: 1, label: t('txt_uri_match_host') },
{ value: 3, label: t('txt_uri_match_exact') },
{ value: 5, label: t('txt_uri_match_never') },
{ value: 2, label: t('txt_uri_match_starts_with') },
{ value: 4, label: t('txt_uri_match_regular_expression') },
];
export function getFieldTypeOptions(): Array<{ value: CustomFieldType; label: string }> {
return [
{ value: 0, label: t('txt_text') },
{ value: 1, label: t('txt_hidden') },
{ value: 2, label: t('txt_boolean') },
];
}
export function getWebsiteMatchOptions(): Array<{ value: number | null; label: string }> {
return [
{ value: null, label: t('txt_uri_match_default_base_domain') },
{ value: 0, label: t('txt_uri_match_base_domain') },
{ value: 1, label: t('txt_uri_match_host') },
{ value: 3, label: t('txt_uri_match_exact') },
{ value: 5, label: t('txt_uri_match_never') },
{ value: 2, label: t('txt_uri_match_starts_with') },
{ value: 4, label: t('txt_uri_match_regular_expression') },
];
}
export const TOTP_PERIOD_SECONDS = 30;
export const TOTP_RING_RADIUS = 14;
@@ -156,7 +167,7 @@ export function createEmptyLoginUri(): VaultDraftLoginUri {
export function websiteMatchLabel(value: number | null | undefined): string {
const normalized = typeof value === 'number' && Number.isFinite(value) ? value : null;
return WEBSITE_MATCH_OPTIONS.find((option) => option.value === normalized)?.label || t('txt_uri_match_default_base_domain');
return getWebsiteMatchOptions().find((option) => option.value === normalized)?.label || t('txt_uri_match_default_base_domain');
}
function valueOrFallback(value: string | null | undefined): string {