refactor: Delete isPlainObject type guard and replace its usages with direct typeof checks.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
"dev": "webpack serve --mode development --open",
|
||||
"build": "webpack --mode production",
|
||||
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
|
||||
"check": "tsc --noEmit && npm run lint",
|
||||
"test:engine": "node --test tests/workspaceEngine.test.js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
toIssuedTimestamp,
|
||||
} from '../utils/date';
|
||||
import { describeDocumentSummary, type DocumentSummaryRow } from './documentSummary';
|
||||
import { isPlainObject } from '../utils/typeGuards';
|
||||
|
||||
import { useFolderManager } from '../folders/FolderManagerContext';
|
||||
|
||||
type Identifier = string | number;
|
||||
@@ -128,11 +128,11 @@ const resolveOptionName = (source?: QuickAddOption | string | null): string => {
|
||||
if (!source) {
|
||||
return '';
|
||||
}
|
||||
if (isPlainObject(source)) {
|
||||
const raw = source.name ?? source.label ?? '';
|
||||
return `${raw}`.trim();
|
||||
if (typeof source === 'string') {
|
||||
return source.trim();
|
||||
}
|
||||
return `${source}`.trim();
|
||||
const raw = source.name ?? source.label ?? '';
|
||||
return `${raw}`.trim();
|
||||
};
|
||||
|
||||
const normalizeQuickAddOption = (option?: QuickAddOption | string | null): QuickAddEntry | null => {
|
||||
@@ -140,17 +140,17 @@ const normalizeQuickAddOption = (option?: QuickAddOption | string | null): Quick
|
||||
return null;
|
||||
}
|
||||
const label = (() => {
|
||||
if (isPlainObject(option)) {
|
||||
const sourceLabel = option.label ?? option.name ?? '';
|
||||
return `${sourceLabel}`.trim();
|
||||
if (typeof option === 'string') {
|
||||
return option.trim();
|
||||
}
|
||||
return `${option}`.trim();
|
||||
const sourceLabel = option.label ?? option.name ?? '';
|
||||
return `${sourceLabel}`.trim();
|
||||
})();
|
||||
if (!label) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id: isPlainObject(option) && option.id ? option.id : label,
|
||||
id: typeof option !== 'string' && option.id ? option.id : label,
|
||||
label,
|
||||
original: option,
|
||||
};
|
||||
@@ -245,7 +245,7 @@ export const TagSection: React.FC<TagSectionProps> = ({
|
||||
return;
|
||||
}
|
||||
if (item.state === 'all' && onRemove) {
|
||||
const payload = isPlainObject(item.payload)
|
||||
const payload = item.payload && typeof item.payload === 'object' && 'id' in item.payload
|
||||
? (item.payload as TagEntry)
|
||||
: tags.find((tag) => (tag.id ?? tag.label) === item.id) ?? { id: item.id, label: item.label };
|
||||
onRemove(payload);
|
||||
@@ -379,7 +379,7 @@ export const CorrespondentSection: React.FC<CorrespondentSectionProps> = ({
|
||||
return;
|
||||
}
|
||||
if (item.state === 'all' && onRemove) {
|
||||
const payload = isPlainObject(item.payload)
|
||||
const payload = item.payload && typeof item.payload === 'object' && 'id' in item.payload
|
||||
? (item.payload as CorrespondentEntry)
|
||||
: entries.find((entry) => (entry.id ?? entry.name) === item.id) ?? { id: item.id, name: item.label };
|
||||
onRemove(payload);
|
||||
@@ -393,7 +393,7 @@ export const CorrespondentSection: React.FC<CorrespondentSectionProps> = ({
|
||||
if (!resolvedName) {
|
||||
return;
|
||||
}
|
||||
const payload = isPlainObject(source)
|
||||
const payload = typeof source !== 'string'
|
||||
? { ...source, name: resolvedName }
|
||||
: { id: null, name: resolvedName };
|
||||
onAdd({ name: resolvedName, option: payload, input: null });
|
||||
@@ -405,26 +405,26 @@ export const CorrespondentSection: React.FC<CorrespondentSectionProps> = ({
|
||||
<div className={containerClass}>
|
||||
{hasEntries
|
||||
? entries.map((entry) => {
|
||||
const key = entry.id ?? entry.name;
|
||||
return (
|
||||
<span key={key} className="correspondent-pill">
|
||||
<span className="correspondent-pill__label">
|
||||
{entry.name}
|
||||
{showCount && entry.count ? ` (${entry.count})` : ''}
|
||||
</span>
|
||||
{onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
className="correspondent-pill__remove"
|
||||
onClick={() => onRemove(entry)}
|
||||
aria-label={`Remove ${entry.name}`}
|
||||
>
|
||||
<IconX className="icon-inline" aria-hidden="true" />
|
||||
</button>
|
||||
) : null}
|
||||
const key = entry.id ?? entry.name;
|
||||
return (
|
||||
<span key={key} className="correspondent-pill">
|
||||
<span className="correspondent-pill__label">
|
||||
{entry.name}
|
||||
{showCount && entry.count ? ` (${entry.count})` : ''}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
{onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
className="correspondent-pill__remove"
|
||||
onClick={() => onRemove(entry)}
|
||||
aria-label={`Remove ${entry.name}`}
|
||||
>
|
||||
<IconX className="icon-inline" aria-hidden="true" />
|
||||
</button>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
: !showQuickAdd && <span className="meta">No correspondents yet.</span>}
|
||||
{showQuickAdd ? (
|
||||
<SelectionAssignmentMenu
|
||||
@@ -518,7 +518,7 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
|
||||
if (active) {
|
||||
setFolderName(name);
|
||||
}
|
||||
}).catch(() => {});
|
||||
}).catch(() => { });
|
||||
}
|
||||
return () => {
|
||||
active = false;
|
||||
@@ -674,21 +674,21 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
|
||||
const titleMetaDisplay = editableTitle && isTitleEditing
|
||||
? renderTitleEditForm('doc-title-edit--inline')
|
||||
: (
|
||||
<>
|
||||
<span className="detail-meta__value">{document?.title}</span>
|
||||
{editableTitle ? (
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={startTitleEdit}
|
||||
aria-label="Edit title"
|
||||
title="Edit title"
|
||||
>
|
||||
<EditIcon className="icon-inline" />
|
||||
</button>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
<>
|
||||
<span className="detail-meta__value">{document?.title}</span>
|
||||
{editableTitle ? (
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={startTitleEdit}
|
||||
aria-label="Edit title"
|
||||
title="Edit title"
|
||||
>
|
||||
<EditIcon className="icon-inline" />
|
||||
</button>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
const issuedDisplay = editableIssued && isIssuedEditing ? (
|
||||
<form className="doc-issued-edit" onSubmit={submitIssuedEdit}>
|
||||
@@ -758,20 +758,20 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
|
||||
onRemove={
|
||||
onCorrespondentRemove
|
||||
? (entry) =>
|
||||
onCorrespondentRemove({
|
||||
documentId: document.id,
|
||||
correspondentId: entry.id,
|
||||
})
|
||||
onCorrespondentRemove({
|
||||
documentId: document.id,
|
||||
correspondentId: entry.id,
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
onAdd={
|
||||
onCorrespondentAdd
|
||||
? ({ name, option }) =>
|
||||
onCorrespondentAdd({
|
||||
document,
|
||||
name,
|
||||
option,
|
||||
})
|
||||
onCorrespondentAdd({
|
||||
document,
|
||||
name,
|
||||
option,
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
showCount
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { isPlainObject } from '../../utils/typeGuards';
|
||||
|
||||
|
||||
type ApiClient = {
|
||||
post: (path: string, body?: unknown) => Promise<{ data: unknown }>;
|
||||
@@ -124,16 +124,14 @@ const useDocumentCorrespondentActions = ({
|
||||
if (!option) {
|
||||
return null;
|
||||
}
|
||||
if (isPlainObject(option) && 'id' in option) {
|
||||
return option as CorrespondentOption;
|
||||
}
|
||||
if (typeof option === 'string') {
|
||||
const trimmed = option.trim();
|
||||
if (trimmed) {
|
||||
return { id: null, name: trimmed };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return option;
|
||||
};
|
||||
|
||||
const handleCorrespondentAdd = useCallback(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { isPlainObject } from '../../utils/typeGuards';
|
||||
|
||||
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
||||
import { DEFAULT_FOLDER_NAME, getRowId, isDocumentRowKey } from '../../app/appLayoutUtils';
|
||||
import {
|
||||
@@ -66,7 +66,7 @@ interface DocumentLike {
|
||||
|
||||
interface FolderContents {
|
||||
documents?: DocumentLike[];
|
||||
subfolders?: Array<{ id?: FolderId; [key: string]: unknown }>;
|
||||
subfolders?: Array<{ id?: FolderId;[key: string]: unknown }>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ interface UseDocumentMutationsResult {
|
||||
|
||||
const normalizeDocumentId = (value: unknown): DocumentId | null => {
|
||||
if (!value) return null;
|
||||
if (isPlainObject(value) && 'id' in value && value.id != null) {
|
||||
if (value && typeof value === 'object' && 'id' in value && value.id != null) {
|
||||
return value.id as DocumentId;
|
||||
}
|
||||
return value as DocumentId;
|
||||
@@ -446,7 +446,7 @@ const useDocumentMutations = ({
|
||||
},
|
||||
[
|
||||
token,
|
||||
documentLookup,
|
||||
|
||||
removeDocumentsFromCaches,
|
||||
previewDocumentId,
|
||||
closeDocumentPreview,
|
||||
@@ -495,7 +495,8 @@ const useDocumentMutations = ({
|
||||
);
|
||||
|
||||
const handleDocumentIssuedUpdate = useCallback(
|
||||
async (documentId: DocumentId, nextIssuedDate: number | null) => {const payload = { issued_at: nextIssuedDate || null };
|
||||
async (documentId: DocumentId, nextIssuedDate: number | null) => {
|
||||
const payload = { issued_at: nextIssuedDate || null };
|
||||
try {
|
||||
const data = await updateDocument(documentId, payload);
|
||||
const updatedDocument = extractDocumentFromResponse?.(data);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from 'react';
|
||||
import type { JSX } from 'react';
|
||||
import { CheckIcon, ChevronDownIcon } from '../../ui/icons';
|
||||
import { isPlainObject } from '../../utils/typeGuards';
|
||||
|
||||
|
||||
type CapabilityValue = string | number;
|
||||
|
||||
@@ -29,9 +29,7 @@ interface CapabilityDropdownProps {
|
||||
summaryLabel?: string;
|
||||
}
|
||||
|
||||
const isCapabilityOption = (
|
||||
option: CapabilityDropdownOption | CapabilityValue | null,
|
||||
): option is CapabilityDropdownOption => isPlainObject(option);
|
||||
|
||||
|
||||
const resolveCapabilityValue = (
|
||||
option: CapabilityDropdownOption | CapabilityValue | null,
|
||||
@@ -39,8 +37,8 @@ const resolveCapabilityValue = (
|
||||
if (option == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isCapabilityOption(option)) {
|
||||
return option as CapabilityValue;
|
||||
if (typeof option !== 'object') {
|
||||
return option;
|
||||
}
|
||||
if (option.value != null) {
|
||||
return option.value;
|
||||
@@ -170,7 +168,7 @@ const CapabilityDropdown = ({
|
||||
}
|
||||
const label = formatLabel
|
||||
? formatLabel(value)
|
||||
: (isCapabilityOption(option) && option?.label) || String(value);
|
||||
: (typeof option === 'object' && option?.label) || String(value);
|
||||
const selected = selectedValues.includes(value);
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -8,7 +8,7 @@ import React, {
|
||||
import type { SettingsSectionConfig } from '../SettingsModal';
|
||||
import { IconX } from '../../ui/icons';
|
||||
import CapabilityDropdown, { CapabilityDropdownOption } from '../components/CapabilityDropdown';
|
||||
import { isPlainObject } from '../../utils/typeGuards';
|
||||
|
||||
|
||||
type CapabilityValue = string | number;
|
||||
type CapabilitySetId = string | number;
|
||||
@@ -55,15 +55,14 @@ interface CapabilitySetsSectionProps {
|
||||
|
||||
type CapabilityOptionInput = CapabilityDropdownOption | CapabilityValue | null;
|
||||
|
||||
const isCapabilityOption = (option: CapabilityOptionInput): option is CapabilityDropdownOption =>
|
||||
isPlainObject(option);
|
||||
|
||||
|
||||
const resolveCapabilityValue = (option: CapabilityOptionInput): CapabilityValue | null => {
|
||||
if (option == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isCapabilityOption(option)) {
|
||||
return option as CapabilityValue;
|
||||
if (typeof option === 'string' || typeof option === 'number') {
|
||||
return option;
|
||||
}
|
||||
if (option.value != null) {
|
||||
return option.value as CapabilityValue;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { CSSProperties, ReactNode, FormEvent, MutableRefObject } from 'react';
|
||||
import { PlusIcon } from './icons';
|
||||
import { isPlainObject } from '../utils/typeGuards';
|
||||
|
||||
import useFloatingMenu from './useFloatingMenu';
|
||||
|
||||
type QuickAddOption = string | number | { id?: string | number; label?: string; name?: string; [key: string]: unknown };
|
||||
type QuickAddOption = string | number | { id?: string | number; label?: string; name?: string;[key: string]: unknown };
|
||||
|
||||
interface NormalizedOption {
|
||||
id?: string | number;
|
||||
@@ -17,7 +17,7 @@ const normalizeOption = (option: QuickAddOption | null, index: number): Normaliz
|
||||
if (option == null) {
|
||||
return null;
|
||||
}
|
||||
if (isPlainObject(option)) {
|
||||
if (typeof option === 'object') {
|
||||
const label = option.label ?? option.name;
|
||||
if (label == null) {
|
||||
return null;
|
||||
@@ -190,19 +190,19 @@ const QuickAddMenu = ({
|
||||
const menuClassName = 'menu menu--floating';
|
||||
const anchoredMenuStyle = isAnchoredMenu && menuStyle
|
||||
? {
|
||||
top: menuStyle.top,
|
||||
left: menuStyle.left,
|
||||
...(menuStyle.width ? { width: menuStyle.width } : null),
|
||||
}
|
||||
top: menuStyle.top,
|
||||
left: menuStyle.left,
|
||||
...(menuStyle.width ? { width: menuStyle.width } : null),
|
||||
}
|
||||
: undefined;
|
||||
const menuInlineStyle = (isAnchoredMenu ? anchoredMenuStyle : menuStyle || undefined) as CSSVarStyle | undefined;
|
||||
const hasFloatingWidthVar = Boolean(menuInlineStyle && Object.prototype.hasOwnProperty.call(menuInlineStyle, '--floating-min-width'));
|
||||
const menuStyleWithVar: CSSVarStyle | undefined = hasFloatingWidthVar
|
||||
? menuInlineStyle
|
||||
: {
|
||||
...(menuInlineStyle || {}),
|
||||
'--floating-min-width': `${Math.max(menuMinWidth, 0)}px`,
|
||||
};
|
||||
...(menuInlineStyle || {}),
|
||||
'--floating-min-width': `${Math.max(menuMinWidth, 0)}px`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className ? `quick-add ${className}` : 'quick-add'}>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
const objectToString = Object.prototype.toString;
|
||||
|
||||
export const isPlainObject = (value: unknown): value is Record<string, unknown> =>
|
||||
value !== null && !Array.isArray(value) && Object(value) === value;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user