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