f9fbc79612
- Remove RELOAD_TAB message chain, use chrome.tabs.reload() directly - Rename IndexedDB label to '站点存储' for accuracy - Introduce StorageSizeInfo type to distinguish bytes vs count - Rename totalSize to totalBytes for clarity - Merge runCleanScript into runScript to reduce duplication - Rename CleaningResult.success to overallSuccess to avoid confusion - Remove unused domain state and setDomain call Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
78 lines
2.5 KiB
TypeScript
78 lines
2.5 KiB
TypeScript
import React from 'react';
|
|
import type { StorageCleanerOptions } from '@/types/storage';
|
|
import type { StorageSizeInfo } from './useStorageCleaner';
|
|
import OptionItem from './OptionItem';
|
|
import { useI18n } from '@/utils/chromeI18n';
|
|
import { cn } from '@/lib/utils';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import { Label } from '@/components/ui/label';
|
|
|
|
interface StorageOptionsGridProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
options: StorageCleanerOptions;
|
|
sizes: Record<string, StorageSizeInfo>;
|
|
allSelected: boolean;
|
|
someSelected: boolean;
|
|
onOptionChange: (key: keyof StorageCleanerOptions) => void;
|
|
onSelectAll: (checked: boolean) => void;
|
|
}
|
|
|
|
export default function StorageOptionsGrid({
|
|
options,
|
|
sizes,
|
|
allSelected,
|
|
someSelected,
|
|
onOptionChange,
|
|
onSelectAll,
|
|
className,
|
|
...props
|
|
}: StorageOptionsGridProps) {
|
|
const { t } = useI18n('storageCleaner');
|
|
|
|
const optionKeys: (keyof StorageCleanerOptions)[] = [
|
|
'localStorage',
|
|
'sessionStorage',
|
|
'indexedDB',
|
|
'cookies',
|
|
'cacheStorage',
|
|
'serviceWorkers',
|
|
];
|
|
|
|
const handleToggleAll = () => {
|
|
onSelectAll(!allSelected);
|
|
};
|
|
|
|
return (
|
|
<div className={cn('w-full overflow-hidden', className)} {...props}>
|
|
<div className="px-3.5 pt-3.5 pb-2">
|
|
<div className="grid grid-cols-2 gap-2 items-stretch">
|
|
{optionKeys.map((key) => (
|
|
<OptionItem
|
|
key={key}
|
|
labelKey={`storageCleaner:options.${key}`}
|
|
checked={options[key]}
|
|
sizeInfo={sizes[key]}
|
|
onChange={() => onOptionChange(key)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
onClick={handleToggleAll}
|
|
className="border-t border-border flex justify-between items-center pl-3.5 pr-7 py-2.5 bg-muted/20 hover:bg-muted/40 cursor-pointer select-none transition-colors"
|
|
>
|
|
<Label className="text-xs font-bold text-muted-foreground/90 cursor-pointer tracking-wide uppercase">
|
|
{t('storageCleaner:selectAll')}
|
|
</Label>
|
|
|
|
<Checkbox
|
|
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
|
|
onClick={(e) => e.stopPropagation()}
|
|
onCheckedChange={(checked) => onSelectAll(checked === true)}
|
|
className="h-4 w-4 shrink-0 rounded border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-muted-foreground/40"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|