refactor
This commit is contained in:
@@ -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,8 +1,4 @@
|
|||||||
export const clamp = (value, min, max) => {
|
export { clamp } from '../utils/math';
|
||||||
if (value < min) return min;
|
|
||||||
if (value > max) return max;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatTransform = (x, y, rotation = 0, scale = 1) =>
|
export const formatTransform = (x, y, rotation = 0, scale = 1) =>
|
||||||
`translate3d(${x}px, ${y}px, 0) rotate(${rotation}deg) scale(${scale})`;
|
`translate3d(${x}px, ${y}px, 0) rotate(${rotation}deg) scale(${scale})`;
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { ArrowLeftIcon, ArrowRightIcon } from '../ui/icons';
|
import { ArrowLeftIcon, ArrowRightIcon } from '../ui/icons';
|
||||||
|
import { clamp } from '../utils/math';
|
||||||
|
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
const clamp = (value, min, max) => {
|
|
||||||
if (value < min) return min;
|
|
||||||
if (value > max) return max;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ensureDocumentRoot = () => {
|
const ensureDocumentRoot = () => {
|
||||||
if (typeof document === 'undefined') {
|
if (typeof document === 'undefined') {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -2,50 +2,13 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { EditIcon, IconX, PlusIcon } from '../ui/icons';
|
import { EditIcon, IconX, PlusIcon } from '../ui/icons';
|
||||||
import QuickAddMenu from '../ui/QuickAddMenu';
|
import QuickAddMenu from '../ui/QuickAddMenu';
|
||||||
import { getTagColorStyle } from '../utils/colors';
|
import { getTagColorStyle } from '../utils/colors';
|
||||||
|
import {
|
||||||
|
formatDate,
|
||||||
|
toDateInputValue,
|
||||||
|
toIssuedTimestamp,
|
||||||
|
} from '../utils/date';
|
||||||
import { describeDocumentSummary } from './documentSummary';
|
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 = []) =>
|
export const sortCorrespondents = (entries = []) =>
|
||||||
entries
|
entries
|
||||||
.filter((entry) => entry && entry.name)
|
.filter((entry) => entry && entry.name)
|
||||||
@@ -257,7 +220,10 @@ const DocumentSummarySection = ({
|
|||||||
}
|
}
|
||||||
return describeDocumentSummary(document);
|
return describeDocumentSummary(document);
|
||||||
}, [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 editableTitle = Boolean(document && onUpdateTitle);
|
||||||
const editableIssued = Boolean(document && onUpdateIssued);
|
const editableIssued = Boolean(document && onUpdateIssued);
|
||||||
|
|||||||
@@ -1,23 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
|
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
|
||||||
import { getTagColorStyle } from '../utils/colors';
|
import { getTagColorStyle } from '../utils/colors';
|
||||||
|
import { formatDate } from '../utils/date';
|
||||||
import DocumentThumbnailImage from './DocumentThumbnailImage';
|
import DocumentThumbnailImage from './DocumentThumbnailImage';
|
||||||
import CorrespondentLinks from './CorrespondentLinks';
|
import CorrespondentLinks from './CorrespondentLinks';
|
||||||
import { resolveCorrespondents } from './correspondents';
|
import { resolveCorrespondents } from './correspondents';
|
||||||
import { writeTagTransferData } from './tagTransfer';
|
import { writeTagTransferData } from './tagTransfer';
|
||||||
import useInlineRename from './useInlineRename';
|
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 = ({
|
const DocumentsList = ({
|
||||||
entries,
|
entries,
|
||||||
focusedRowKey,
|
focusedRowKey,
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
const formatDateTime = (value) => {
|
import { formatDateTime } from '../utils/date';
|
||||||
if (!value) {
|
|
||||||
return '—';
|
|
||||||
}
|
|
||||||
const date = new Date(value);
|
|
||||||
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildDocumentMetadataItems = (document) => {
|
export const buildDocumentMetadataItems = (document) => {
|
||||||
if (!document) {
|
if (!document) {
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
import { formatFileSize } from '../utils/format';
|
import { formatFileSize } from '../utils/format';
|
||||||
|
import { formatDateTime as defaultFormatDateTime } from '../utils/date';
|
||||||
const defaultFormatDateTime = (value) => {
|
|
||||||
if (!value) {
|
|
||||||
return '—';
|
|
||||||
}
|
|
||||||
const date = new Date(value);
|
|
||||||
if (Number.isNaN(date.getTime())) {
|
|
||||||
return '—';
|
|
||||||
}
|
|
||||||
return date.toLocaleString();
|
|
||||||
};
|
|
||||||
|
|
||||||
const coercePageCount = (metadata) => {
|
const coercePageCount = (metadata) => {
|
||||||
const raw = metadata?.page_count;
|
const raw = metadata?.page_count;
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ export const isPrimaryPointerEvent = (event) => {
|
|||||||
return type === 'click' || type === 'pointerdown' || type === 'pointerup';
|
return type === 'click' || type === 'pointerdown' || type === 'pointerup';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useEntryPointerHandler = ({
|
export const useEntryPointer = ({
|
||||||
resolveDocumentRowKey,
|
resolveDocumentRowKey,
|
||||||
resolveFolderRowKey,
|
resolveFolderRowKey,
|
||||||
onSelectDocument,
|
onSelectDocument,
|
||||||
onSelectFolder,
|
onSelectFolder,
|
||||||
|
onInspectDocument,
|
||||||
}) =>
|
}) =>
|
||||||
useCallback(
|
useCallback(
|
||||||
(entry, event) => {
|
(entry, event) => {
|
||||||
@@ -39,16 +40,20 @@ export const useEntryPointerHandler = ({
|
|||||||
|
|
||||||
const modifierClick = isPointerModifierEvent(event);
|
const modifierClick = isPointerModifierEvent(event);
|
||||||
const primaryClick = isPrimaryPointerEvent(event);
|
const primaryClick = isPrimaryPointerEvent(event);
|
||||||
|
const metadata = { modifierClick, primaryClick, rowKey };
|
||||||
|
|
||||||
if (type === 'document') {
|
if (type === 'document') {
|
||||||
if (typeof onSelectDocument === 'function') {
|
if (typeof onSelectDocument === 'function') {
|
||||||
onSelectDocument(id, event, { modifierClick, primaryClick, rowKey });
|
onSelectDocument(id, event, metadata);
|
||||||
|
}
|
||||||
|
if (!modifierClick && primaryClick && typeof onInspectDocument === 'function') {
|
||||||
|
onInspectDocument(id, metadata);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof onSelectFolder === 'function') {
|
if (typeof onSelectFolder === 'function') {
|
||||||
onSelectFolder(id, event, { modifierClick, primaryClick, rowKey });
|
onSelectFolder(id, event, metadata);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -56,7 +61,8 @@ export const useEntryPointerHandler = ({
|
|||||||
resolveFolderRowKey,
|
resolveFolderRowKey,
|
||||||
onSelectDocument,
|
onSelectDocument,
|
||||||
onSelectFolder,
|
onSelectFolder,
|
||||||
|
onInspectDocument,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
export default useEntryPointerHandler;
|
export default useEntryPointer;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import usePasskeys from '../../settings/usePasskeys';
|
|||||||
import { useManagementModals } from '../../app/useManagementModals';
|
import { useManagementModals } from '../../app/useManagementModals';
|
||||||
import { api, useAppDispatch, useAppState } from '../../app/appState';
|
import { api, useAppDispatch, useAppState } from '../../app/appState';
|
||||||
import useWorkspaceSelection from '../../app/useWorkspaceSelection';
|
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 { isTagTransferEvent } from '../../documents/tagTransfer';
|
||||||
import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
|
import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
|
||||||
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
|
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import composeClassName from './classNames';
|
||||||
const composeClassName = (base, extra) => (extra ? `${base} ${extra}` : base);
|
|
||||||
|
|
||||||
const PanelHeader = ({
|
const PanelHeader = ({
|
||||||
className = '',
|
className = '',
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const composeClassName = (base, extra) => (extra ? `${base} ${extra}` : base);
|
||||||
|
|
||||||
|
export default composeClassName;
|
||||||
@@ -41,8 +41,7 @@ import {
|
|||||||
IconFileInfo,
|
IconFileInfo,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import FolderSvg from '../assets/folder.svg';
|
import FolderSvg from '../assets/folder.svg';
|
||||||
|
import composeClassName from './classNames';
|
||||||
const composeClassName = (base, extra) => (extra ? `${base} ${extra}` : base);
|
|
||||||
|
|
||||||
export const ChevronIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
export const ChevronIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||||
<TablerChevronRight
|
<TablerChevronRight
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { clamp } from '../utils/math';
|
||||||
|
|
||||||
const DEFAULT_VIEWPORT_MARGIN = 8;
|
const DEFAULT_VIEWPORT_MARGIN = 8;
|
||||||
|
|
||||||
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
||||||
|
|
||||||
const resolveViewportWidth = () => {
|
const resolveViewportWidth = () => {
|
||||||
if (typeof window !== 'undefined' && typeof window.innerWidth === 'number') {
|
if (typeof window !== 'undefined' && typeof window.innerWidth === 'number') {
|
||||||
return window.innerWidth;
|
return window.innerWidth;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user