refactor
This commit is contained in:
@@ -78,9 +78,6 @@ const persistWidth = (panel: PanelKey, value: number): void => {
|
||||
};
|
||||
|
||||
const applyPanelWidthToRoot = (panel: PanelKey, width: number, active: boolean): void => {
|
||||
if (!Number.isFinite(width)) {
|
||||
return;
|
||||
}
|
||||
const varName = panel === 'sidebar' ? '--sidebar-width' : '--detail-panel-width';
|
||||
const resolvedValue = panel === 'detail' && !active ? '0px' : `${width}px`;
|
||||
document.documentElement.style.setProperty(varName, resolvedValue);
|
||||
@@ -170,9 +167,7 @@ export const PanelManagerProvider: React.FC<PanelManagerProviderProps> = ({ chil
|
||||
const setPanelWidth = useCallback(
|
||||
(panel: PanelKey, width: number, { commit = true, log = true }: SetPanelWidthOptions = {}) => {
|
||||
const clamped = clampPanelWidth(panel, width);
|
||||
if (!Number.isFinite(clamped)) {
|
||||
return panelWidthsRef.current[panel];
|
||||
}
|
||||
|
||||
if (panel === 'sidebar') {
|
||||
setSidebarWidthState((prev) => (prev === clamped ? prev : clamped));
|
||||
} else {
|
||||
@@ -348,10 +343,7 @@ export const usePanelResizeBindings = (
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const rect = panelRef.current.getBoundingClientRect();
|
||||
const startWidth = Number.isFinite(rect?.width) ? rect.width : getPanelWidth(panel);
|
||||
if (!Number.isFinite(startWidth)) {
|
||||
return;
|
||||
}
|
||||
const startWidth = rect?.width ?? getPanelWidth(panel);
|
||||
const pointerId = event.pointerId ?? 'mouse';
|
||||
const startX = event.clientX;
|
||||
startPanelResize(panel);
|
||||
@@ -398,9 +390,7 @@ export const usePanelResizeBindings = (
|
||||
],
|
||||
);
|
||||
|
||||
const panelStyle = enabled && Number.isFinite(liveWidth)
|
||||
? { width: `${liveWidth}px` }
|
||||
: undefined;
|
||||
const panelStyle = enabled ? { width: `${liveWidth}px` } : undefined;
|
||||
|
||||
const handleProps = enabled
|
||||
? {
|
||||
|
||||
@@ -111,11 +111,7 @@ function CorrespondentsPanel({
|
||||
);
|
||||
|
||||
const renderUsage = useCallback((correspondent: Correspondent) => {
|
||||
const count = correspondent.usage_count;
|
||||
if (count == null || !Number.isFinite(count)) {
|
||||
return '0';
|
||||
}
|
||||
return count.toString();
|
||||
return correspondent.usage_count;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import type { DocumentId } from '../../types/identifiers';
|
||||
import type { Document } from '../../types/documents';
|
||||
|
||||
import type { Asset } from '../../types/assets';
|
||||
import type { Asset, ThumbnailMetadata } from '../../types/assets';
|
||||
|
||||
interface PreviewMetadataEntry {
|
||||
docId: DocumentId;
|
||||
@@ -40,13 +39,13 @@ const usePreviewMetadata = (
|
||||
const resolveAsset = (type: string) => getDocumentAsset?.(doc, type) ?? null;
|
||||
|
||||
let asset = resolveAsset('thumbnail');
|
||||
let metadata = (asset?.metadata as { width?: number; height?: number } | null) || null;
|
||||
let metadata: Partial<ThumbnailMetadata> | null = (asset?.metadata as Partial<ThumbnailMetadata> | null) || null;
|
||||
|
||||
const hasDimensions = (meta: { width?: number | string; height?: number | string } | null) =>
|
||||
Number.isFinite(Number(meta?.width)) &&
|
||||
Number.isFinite(Number(meta?.height)) &&
|
||||
Number(meta.width) > 0 &&
|
||||
Number(meta.height) > 0;
|
||||
const hasDimensions = (meta: Partial<ThumbnailMetadata> | null): meta is ThumbnailMetadata =>
|
||||
typeof meta?.width === 'number' &&
|
||||
typeof meta?.height === 'number' &&
|
||||
meta.width > 0 &&
|
||||
meta.height > 0;
|
||||
|
||||
if (!hasDimensions(metadata) && ensureAssetUrl && docId && asset?.id) {
|
||||
// Skip if we already failed for this doc to avoid infinite loops
|
||||
@@ -55,7 +54,7 @@ const usePreviewMetadata = (
|
||||
const ensured = await ensureAssetUrl(doc.id, asset);
|
||||
if (ensured) {
|
||||
asset = ensured;
|
||||
metadata = (asset?.metadata as { width?: number; height?: number } | null) || null;
|
||||
metadata = (asset?.metadata as Partial<ThumbnailMetadata> | null) || null;
|
||||
}
|
||||
|
||||
// If still no dimensions, mark as failed so we don't try again
|
||||
|
||||
@@ -23,10 +23,8 @@ const createDragPreview = (node: EventTarget | null, clientX: number, clientY: n
|
||||
return null;
|
||||
}
|
||||
const rect = node.getBoundingClientRect();
|
||||
const safeClientX = Number.isFinite(clientX) ? clientX : rect.left + rect.width / 2;
|
||||
const safeClientY = Number.isFinite(clientY) ? clientY : rect.top + rect.height / 2;
|
||||
const offsetX = Math.min(Math.max(safeClientX - rect.left, 0), rect.width);
|
||||
const offsetY = Math.min(Math.max(safeClientY - rect.top, 0), rect.height);
|
||||
const offsetX = Math.min(Math.max(clientX - rect.left, 0), rect.width);
|
||||
const offsetY = Math.min(Math.max(clientY - rect.top, 0), rect.height);
|
||||
const clone = node.cloneNode(true) as HTMLElement;
|
||||
clone.style.position = 'absolute';
|
||||
clone.style.top = '-9999px';
|
||||
|
||||
@@ -67,9 +67,8 @@ const useLazyVisibility = (
|
||||
|
||||
|
||||
|
||||
const getPageCount = (doc?: Document | null) => {
|
||||
const count = doc?.current_version?.metadata?.page_count;
|
||||
return Number.isFinite(count) ? Number(count) : null;
|
||||
const getPageCount = (doc?: Document | null): number | null => {
|
||||
return doc?.current_version?.metadata?.page_count;
|
||||
};
|
||||
|
||||
type Asset = AssetManagerAsset;
|
||||
@@ -140,7 +139,7 @@ const DocumentThumbnailImage = ({
|
||||
}, [document, ensureAssetUrl, getDocumentAsset, isVisible, thumbnailAsset]);
|
||||
|
||||
const pageCount = getPageCount(document);
|
||||
const showMultiPageBadge = Number.isFinite(pageCount) && pageCount > 1;
|
||||
const showMultiPageBadge = pageCount !== null && pageCount > 1;
|
||||
const innerClasses = ['document-thumbnail-inner'];
|
||||
if (showMultiPageBadge) {
|
||||
innerClasses.push('document-thumbnail-inner--multipage');
|
||||
|
||||
@@ -67,8 +67,8 @@ const normalizeItems = (items?: SelectionAssignmentMenuItem[]): NormalizedSelect
|
||||
: item.state === 'partial'
|
||||
? 'partial'
|
||||
: 'none';
|
||||
const numericCount = Number.isFinite(item.count) ? Number(item.count) : null;
|
||||
const numericTotal = Number.isFinite(item.total) ? Number(item.total) : null;
|
||||
const numericCount = item.count ?? null;
|
||||
const numericTotal = item.total ?? null;
|
||||
return {
|
||||
id: item.id ?? trimmedLabel,
|
||||
label: trimmedLabel,
|
||||
|
||||
@@ -25,7 +25,7 @@ const coercePageCount = (metadata?: { page_count?: number | string | null } | nu
|
||||
return null;
|
||||
}
|
||||
const parsed = Number.parseInt(String(raw), 10);
|
||||
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
||||
return parsed >= 0 ? parsed : null;
|
||||
};
|
||||
|
||||
const sanitizeArray = <T>(entries?: Array<T | null> | null): T[] =>
|
||||
@@ -47,11 +47,11 @@ export const describeDocumentSummary = (document?: Document | null, options: Des
|
||||
return formatDateTime(value) || '—';
|
||||
};
|
||||
const doc = document ?? ({} as Document);
|
||||
const sizeBytes = Number(doc.current_version?.size_bytes);
|
||||
const sizeLabel = Number.isFinite(sizeBytes) && sizeBytes > 0 ? formatFileSize(sizeBytes) : '—';
|
||||
const sizeBytes = doc.current_version?.size_bytes ?? null;
|
||||
const sizeLabel = sizeBytes !== null && sizeBytes > 0 ? formatFileSize(sizeBytes) : '—';
|
||||
const metadata = doc.current_version?.metadata || null;
|
||||
const pageCount = coercePageCount(metadata);
|
||||
const pageCountLabel = Number.isFinite(pageCount) ? String(pageCount) : '—';
|
||||
const pageCountLabel = pageCount !== null ? String(pageCount) : '—';
|
||||
const folderLabel = doc.folder_id == null ? DEFAULT_FOLDER_NAME : `Folder ${doc.folder_id}`;
|
||||
const tags = sanitizeArray<DocumentTag>(doc.tags);
|
||||
const correspondents = sanitizeArray<DocumentCorrespondent>(doc.correspondents);
|
||||
|
||||
@@ -145,7 +145,7 @@ const useDocumentDragHandlers = ({
|
||||
|
||||
let thumbWidth = size;
|
||||
let thumbHeight = size;
|
||||
if (Number.isFinite(aspectRatio) && aspectRatio > 0) {
|
||||
if (aspectRatio > 0) {
|
||||
if (aspectRatio >= 1) {
|
||||
thumbWidth = size;
|
||||
thumbHeight = Math.max(size / aspectRatio, size * 0.5);
|
||||
|
||||
@@ -267,7 +267,7 @@ const useDocumentTagging = ({
|
||||
},
|
||||
);
|
||||
const payload = 'data' in response ? response.data : response;
|
||||
const queued = Number.isFinite(payload?.queued)
|
||||
const queued = payload?.queued != null
|
||||
? Number(payload.queued)
|
||||
: targetIds.length;
|
||||
setStatusMessage(
|
||||
|
||||
@@ -519,7 +519,7 @@ const PdfViewer = ({ src, title, className, viewportRef }: PdfViewerProps): JSX.
|
||||
return;
|
||||
}
|
||||
const pageNumber = Number(attr);
|
||||
if (!Number.isFinite(pageNumber) || pageNumber <= 0) {
|
||||
if (!(pageNumber > 0)) {
|
||||
return;
|
||||
}
|
||||
handlePageVisibilityChange(pageNumber, entry.isIntersecting);
|
||||
@@ -833,15 +833,6 @@ function PdfPageCanvas({
|
||||
const safePageHeight = Math.max(1, descriptor.height);
|
||||
const widthScale = safeViewportWidth / safePageWidth;
|
||||
const heightScale = safeViewportHeight / safePageHeight;
|
||||
if (!Number.isFinite(widthScale) && Number.isFinite(heightScale)) {
|
||||
return 'height';
|
||||
}
|
||||
if (!Number.isFinite(heightScale) && Number.isFinite(widthScale)) {
|
||||
return 'width';
|
||||
}
|
||||
if (!Number.isFinite(widthScale) && !Number.isFinite(heightScale)) {
|
||||
return 'width';
|
||||
}
|
||||
return widthScale <= heightScale ? 'width' : 'height';
|
||||
}, [descriptor.height, descriptor.width, viewMode, viewportHeight, viewportWidth]);
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ const CapabilitySetsSection: React.FC<CapabilitySetsSectionProps> = ({
|
||||
label: set.label || set.slug || set.id,
|
||||
capabilities: Array.isArray(set.capabilities) ? set.capabilities : [],
|
||||
isSystem: Boolean(set?.is_system),
|
||||
version: Number.isFinite(set?.cap_version) ? Number(set.cap_version) : null,
|
||||
version: set?.cap_version != null ? Number(set.cap_version) : null,
|
||||
})),
|
||||
[capabilitySets],
|
||||
);
|
||||
@@ -492,7 +492,7 @@ const CapabilitySetsSection: React.FC<CapabilitySetsSectionProps> = ({
|
||||
: '—'}
|
||||
</td>
|
||||
<td>{isSystem ? 'Yes' : 'No'}</td>
|
||||
<td>{Number.isFinite(set.cap_version) ? Number(set.cap_version) : '—'}</td>
|
||||
<td>{set.cap_version != null ? Number(set.cap_version) : '—'}</td>
|
||||
<td className="settings-table__actions">
|
||||
{isSystem ? (
|
||||
<span className="settings-status">System set</span>
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import type { Identifier } from './identifiers';
|
||||
import type { Download } from './common';
|
||||
|
||||
export interface ThumbnailMetadata {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface Asset {
|
||||
id?: Identifier;
|
||||
asset_type?: string;
|
||||
download?: Download | null;
|
||||
metadata?: Record<string, unknown> | null;
|
||||
metadata?: ThumbnailMetadata | Record<string, unknown> | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface Correspondent {
|
||||
export interface DocumentVersion {
|
||||
assets?: Record<string, Asset> | Asset[] | null;
|
||||
metadata?: Record<string, unknown> & { page_count?: number } | null;
|
||||
size_bytes?: number | string | null;
|
||||
size_bytes?: number | null;
|
||||
checksum?: string | null;
|
||||
download?: Download | null;
|
||||
[key: string]: unknown;
|
||||
|
||||
@@ -123,11 +123,11 @@ export const CorrespondentIcon = createIcon(IconUserFilled, { baseClass: 'icon i
|
||||
|
||||
// Custom icons that need special handling
|
||||
export const FolderIcon: TablerIconComponent = ({ className, size = 16, title, ...rest }) => {
|
||||
const dimensionProps = Number.isFinite(size) ? { width: Number(size), height: Number(size) } : {};
|
||||
return (
|
||||
<FolderSvg
|
||||
className={composeClassName('folder-icon', className)}
|
||||
{...dimensionProps}
|
||||
width={size}
|
||||
height={size}
|
||||
role={title ? 'img' : 'presentation'}
|
||||
aria-hidden={title ? undefined : true}
|
||||
focusable="false"
|
||||
|
||||
@@ -33,8 +33,8 @@ const formatStyle = (metrics: FloatingMenuMetrics | null): FloatingMenuStyle =>
|
||||
top: metrics.top,
|
||||
left: metrics.left,
|
||||
};
|
||||
if (Number.isFinite(metrics.minWidth)) {
|
||||
style['--floating-min-width'] = `${Math.max(metrics.minWidth ?? 0, 0)}px`;
|
||||
if (metrics.minWidth != null) {
|
||||
style['--floating-min-width'] = `${Math.max(metrics.minWidth, 0)}px`;
|
||||
}
|
||||
if (metrics.width) {
|
||||
style.width = metrics.width;
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
export const formatFileSize = (value: number | string): string => {
|
||||
const bytes = Number(value);
|
||||
if (!Number.isFinite(bytes) || bytes <= 0) {
|
||||
return '0 B';
|
||||
}
|
||||
|
||||
export const formatFileSize = (bytes: number): string => {
|
||||
const sign = bytes < 0 ? '-' : '';
|
||||
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
|
||||
let index = 0;
|
||||
let amount = bytes;
|
||||
let amount = Math.abs(bytes);
|
||||
|
||||
while (amount >= 1024 && index < units.length - 1) {
|
||||
amount /= 1024;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return `${amount.toFixed(2)} ${units[index]}`;
|
||||
const decimals = index === 0 ? 0 : 2;
|
||||
return `${sign}${amount.toFixed(decimals)} ${units[index]}`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user