This commit is contained in:
2025-11-09 20:21:46 +01:00
parent bfab2f2cdc
commit 20cf597c5c
15 changed files with 101 additions and 142 deletions
@@ -1,48 +0,0 @@
import { useCallback } from 'react';
import { useEntryPointerHandler as useEntryPointerCore, isPointerModifierEvent, isPrimaryPointerEvent } from '../documents/useEntryPointer';
export const useEntryPointer = ({
resolveDocumentRowKey,
resolveFolderRowKey,
onSelectDocument,
onInspectDocument,
onSelectFolder,
}) => {
const coreHandler = useEntryPointerCore({
resolveDocumentRowKey,
resolveFolderRowKey,
onSelectDocument: (documentId, event, meta) => {
const { modifierClick, primaryClick, rowKey } = meta;
onSelectDocument(documentId, event, { modifierClick, primaryClick, rowKey });
if (!modifierClick && primaryClick && typeof onInspectDocument === 'function') {
onInspectDocument(documentId, meta);
}
},
onSelectFolder,
});
return useCallback((entry, event) => {
if (!entry) {
return;
}
if (entry.type !== 'document') {
coreHandler(entry, event);
return;
}
const modifierClick = isPointerModifierEvent(event);
const primaryClick = isPrimaryPointerEvent(event);
onSelectDocument(entry.id, event, {
modifierClick,
primaryClick,
rowKey: entry.key,
});
if (!modifierClick && primaryClick) {
onInspectDocument?.(entry.id, { modifierClick, primaryClick, rowKey: entry.key });
}
}, [coreHandler, onInspectDocument, onSelectDocument]);
};
export default useEntryPointer;
+1 -5
View File
@@ -1,8 +1,4 @@
export const clamp = (value, min, max) => {
if (value < min) return min;
if (value > max) return max;
return value;
};
export { clamp } from '../utils/math';
export const formatTransform = (x, y, rotation = 0, scale = 1) =>
`translate3d(${x}px, ${y}px, 0) rotate(${rotation}deg) scale(${scale})`;
+1 -6
View File
@@ -1,15 +1,10 @@
import React, { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { ArrowLeftIcon, ArrowRightIcon } from '../ui/icons';
import { clamp } from '../utils/math';
const noop = () => {};
const clamp = (value, min, max) => {
if (value < min) return min;
if (value > max) return max;
return value;
};
const ensureDocumentRoot = () => {
if (typeof document === 'undefined') {
return null;
@@ -2,50 +2,13 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EditIcon, IconX, PlusIcon } from '../ui/icons';
import QuickAddMenu from '../ui/QuickAddMenu';
import { getTagColorStyle } from '../utils/colors';
import {
formatDate,
toDateInputValue,
toIssuedTimestamp,
} from '../utils/date';
import { describeDocumentSummary } from './documentSummary';
const formatDate = (value) => {
if (!value) {
return null;
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return null;
}
return date.toLocaleDateString();
};
const toDateInputValue = (value) => {
if (!value) {
return '';
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return '';
}
const timezoneOffset = date.getTimezoneOffset();
const localDate = new Date(date.getTime() - timezoneOffset * 60000);
return localDate.toISOString().slice(0, 10);
};
const toIssuedTimestamp = (dateString, fallback) => {
if (!dateString) {
return null;
}
const base = fallback ? new Date(fallback) : new Date();
if (Number.isNaN(base.getTime())) {
return null;
}
const [year, month, day] = dateString.split('-').map((part) => Number.parseInt(part, 10));
if (!year || !month || !day) {
return null;
}
const candidate = new Date(base);
candidate.setUTCFullYear(year, month - 1, day);
return candidate.toISOString();
};
export const sortCorrespondents = (entries = []) =>
entries
.filter((entry) => entry && entry.name)
@@ -257,7 +220,10 @@ const DocumentSummarySection = ({
}
return describeDocumentSummary(document);
}, [document]);
const issuedDateLabel = useMemo(() => formatDate(document?.issued_at), [document?.issued_at]);
const issuedDateLabel = useMemo(
() => formatDate(document?.issued_at, { fallback: null }),
[document?.issued_at],
);
const editableTitle = Boolean(document && onUpdateTitle);
const editableIssued = Boolean(document && onUpdateIssued);
+1 -11
View File
@@ -1,23 +1,13 @@
import React from 'react';
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
import { getTagColorStyle } from '../utils/colors';
import { formatDate } from '../utils/date';
import DocumentThumbnailImage from './DocumentThumbnailImage';
import CorrespondentLinks from './CorrespondentLinks';
import { resolveCorrespondents } from './correspondents';
import { writeTagTransferData } from './tagTransfer';
import useInlineRename from './useInlineRename';
const formatDate = (value) => {
if (!value) {
return "—";
}
const timestamp = Date.parse(value);
if (Number.isNaN(timestamp)) {
return "—";
}
return new Date(timestamp).toLocaleDateString();
};
const DocumentsList = ({
entries,
focusedRowKey,
+1 -7
View File
@@ -1,10 +1,4 @@
const formatDateTime = (value) => {
if (!value) {
return '—';
}
const date = new Date(value);
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
};
import { formatDateTime } from '../utils/date';
export const buildDocumentMetadataItems = (document) => {
if (!document) {
+1 -11
View File
@@ -1,15 +1,5 @@
import { formatFileSize } from '../utils/format';
const defaultFormatDateTime = (value) => {
if (!value) {
return '—';
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return '—';
}
return date.toLocaleString();
};
import { formatDateTime as defaultFormatDateTime } from '../utils/date';
const coercePageCount = (metadata) => {
const raw = metadata?.page_count;
+10 -4
View File
@@ -14,11 +14,12 @@ export const isPrimaryPointerEvent = (event) => {
return type === 'click' || type === 'pointerdown' || type === 'pointerup';
};
export const useEntryPointerHandler = ({
export const useEntryPointer = ({
resolveDocumentRowKey,
resolveFolderRowKey,
onSelectDocument,
onSelectFolder,
onInspectDocument,
}) =>
useCallback(
(entry, event) => {
@@ -39,16 +40,20 @@ export const useEntryPointerHandler = ({
const modifierClick = isPointerModifierEvent(event);
const primaryClick = isPrimaryPointerEvent(event);
const metadata = { modifierClick, primaryClick, rowKey };
if (type === 'document') {
if (typeof onSelectDocument === 'function') {
onSelectDocument(id, event, { modifierClick, primaryClick, rowKey });
onSelectDocument(id, event, metadata);
}
if (!modifierClick && primaryClick && typeof onInspectDocument === 'function') {
onInspectDocument(id, metadata);
}
return;
}
if (typeof onSelectFolder === 'function') {
onSelectFolder(id, event, { modifierClick, primaryClick, rowKey });
onSelectFolder(id, event, metadata);
}
},
[
@@ -56,7 +61,8 @@ export const useEntryPointerHandler = ({
resolveFolderRowKey,
onSelectDocument,
onSelectFolder,
onInspectDocument,
],
);
export default useEntryPointerHandler;
export default useEntryPointer;
@@ -12,7 +12,7 @@ import usePasskeys from '../../settings/usePasskeys';
import { useManagementModals } from '../../app/useManagementModals';
import { api, useAppDispatch, useAppState } from '../../app/appState';
import useWorkspaceSelection from '../../app/useWorkspaceSelection';
import { useEntryPointerHandler as useEntryPointerCore } from '../../documents/useEntryPointer';
import { useEntryPointer as useEntryPointerCore } from '../../documents/useEntryPointer';
import { isTagTransferEvent } from '../../documents/tagTransfer';
import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
+1 -2
View File
@@ -1,6 +1,5 @@
import React from 'react';
const composeClassName = (base, extra) => (extra ? `${base} ${extra}` : base);
import composeClassName from './classNames';
const PanelHeader = ({
className = '',
+3
View File
@@ -0,0 +1,3 @@
export const composeClassName = (base, extra) => (extra ? `${base} ${extra}` : base);
export default composeClassName;
+1 -2
View File
@@ -41,8 +41,7 @@ import {
IconFileInfo,
} from '@tabler/icons-react';
import FolderSvg from '../assets/folder.svg';
const composeClassName = (base, extra) => (extra ? `${base} ${extra}` : base);
import composeClassName from './classNames';
export const ChevronIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<TablerChevronRight
+1 -2
View File
@@ -1,9 +1,8 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { clamp } from '../utils/math';
const DEFAULT_VIEWPORT_MARGIN = 8;
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const resolveViewportWidth = () => {
if (typeof window !== 'undefined' && typeof window.innerWidth === 'number') {
return window.innerWidth;
+57
View File
@@ -0,0 +1,57 @@
const ensureDate = (value) => {
if (!value) {
return null;
}
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
return Number.isNaN(date.getTime()) ? null : date;
};
export const formatDate = (value, { fallback = '—', locale, options } = {}) => {
const date = ensureDate(value);
if (!date) {
return fallback;
}
return date.toLocaleDateString(locale, options);
};
export const formatDateTime = (value, { fallback = '—', locale, options } = {}) => {
const date = ensureDate(value);
if (!date) {
return fallback;
}
return date.toLocaleString(locale, options);
};
export const toDateInputValue = (value) => {
const date = ensureDate(value);
if (!date) {
return '';
}
const timezoneOffset = date.getTimezoneOffset();
const localDate = new Date(date.getTime() - timezoneOffset * 60000);
return localDate.toISOString().slice(0, 10);
};
export const toIssuedTimestamp = (dateString, fallback) => {
if (!dateString) {
return null;
}
const base = ensureDate(fallback) || new Date();
const [year, month, day] = dateString.split('-').map((part) => Number.parseInt(part, 10));
if (!year || !month || !day) {
return null;
}
const candidate = new Date(base);
candidate.setUTCFullYear(year, month - 1, day);
return Number.isNaN(candidate.getTime()) ? null : candidate.toISOString();
};
export const parseDateValue = (value) => ensureDate(value);
export default {
formatDate,
formatDateTime,
toDateInputValue,
toIssuedTimestamp,
parseDateValue,
};
+13
View File
@@ -0,0 +1,13 @@
export const clamp = (value, min, max) => {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
};
export default {
clamp,
};