mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
Refactor to use MutableRefObject instead of RefObject
Replaces all instances of React.RefObject with React.MutableRefObject for refs that are mutated directly throughout the codebase. This change clarifies intent and improves type safety for refs that are updated outside of React's ref assignment.
This commit is contained in:
parent
3fa0535246
commit
57b2fcaf2c
@ -23,7 +23,7 @@ interface PageThumbnailProps {
|
||||
selectionMode: boolean;
|
||||
movingPage: number | null;
|
||||
isAnimating: boolean;
|
||||
pageRefs: React.RefObject<Map<string, HTMLDivElement>>;
|
||||
pageRefs: React.MutableRefObject<Map<string, HTMLDivElement>>;
|
||||
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
|
||||
onTogglePage: (pageId: string) => void;
|
||||
onAnimateReorder: () => void;
|
||||
|
@ -71,7 +71,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
||||
[visibleSections]
|
||||
);
|
||||
|
||||
const scrollTo = (ref: React.RefObject<HTMLDivElement | null>) => {
|
||||
const scrollTo = (ref: React.MutableRefObject<HTMLDivElement | null>) => {
|
||||
const container = scrollableRef.current;
|
||||
const target = ref.current;
|
||||
if (container && target) {
|
||||
|
@ -13,7 +13,7 @@ interface FileManagerContextValue {
|
||||
searchTerm: string;
|
||||
selectedFiles: StirlingFileStub[];
|
||||
filteredFiles: StirlingFileStub[];
|
||||
fileInputRef: React.RefObject<HTMLInputElement | null>;
|
||||
fileInputRef: React.MutableRefObject<HTMLInputElement | null>;
|
||||
selectedFilesSet: Set<FileId>;
|
||||
expandedFileIds: Set<FileId>;
|
||||
fileGroups: Map<FileId, StirlingFileStub[]>;
|
||||
|
@ -165,8 +165,8 @@ interface AddFileOptions {
|
||||
*/
|
||||
export async function addFiles(
|
||||
options: AddFileOptions,
|
||||
stateRef: React.RefObject<FileContextState>,
|
||||
filesRef: React.RefObject<Map<FileId, File>>,
|
||||
stateRef: React.MutableRefObject<FileContextState>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
||||
dispatch: React.Dispatch<FileContextAction>,
|
||||
lifecycleManager: FileLifecycleManager,
|
||||
enablePersistence: boolean = false
|
||||
@ -278,7 +278,7 @@ export async function consumeFiles(
|
||||
inputFileIds: FileId[],
|
||||
outputStirlingFiles: StirlingFile[],
|
||||
outputStirlingFileStubs: StirlingFileStub[],
|
||||
filesRef: React.RefObject<Map<FileId, File>>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
||||
dispatch: React.Dispatch<FileContextAction>
|
||||
): Promise<FileId[]> {
|
||||
if (DEBUG) console.log(`📄 consumeFiles: Processing ${inputFileIds.length} input files, ${outputStirlingFiles.length} output files with pre-created stubs`);
|
||||
@ -357,7 +357,7 @@ export async function consumeFiles(
|
||||
async function restoreFilesAndCleanup(
|
||||
filesToRestore: { file: File; record: StirlingFileStub }[],
|
||||
fileIdsToRemove: FileId[],
|
||||
filesRef: React.RefObject<Map<FileId, File>>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
||||
indexedDB?: { deleteFile: (fileId: FileId) => Promise<void> } | null
|
||||
): Promise<void> {
|
||||
// Remove files from filesRef
|
||||
@ -406,7 +406,7 @@ export async function undoConsumeFiles(
|
||||
inputFiles: File[],
|
||||
inputStirlingFileStubs: StirlingFileStub[],
|
||||
outputFileIds: FileId[],
|
||||
filesRef: React.RefObject<Map<FileId, File>>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
||||
dispatch: React.Dispatch<FileContextAction>,
|
||||
indexedDB?: { saveFile: (file: File, fileId: FileId, existingThumbnail?: string) => Promise<any>; deleteFile: (fileId: FileId) => Promise<void> } | null
|
||||
): Promise<void> {
|
||||
@ -468,8 +468,8 @@ export async function undoConsumeFiles(
|
||||
export async function addStirlingFileStubs(
|
||||
stirlingFileStubs: StirlingFileStub[],
|
||||
options: { insertAfterPageId?: string; selectFiles?: boolean } = {},
|
||||
stateRef: React.RefObject<FileContextState>,
|
||||
filesRef: React.RefObject<Map<FileId, File>>,
|
||||
stateRef: React.MutableRefObject<FileContextState>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
||||
dispatch: React.Dispatch<FileContextAction>,
|
||||
_lifecycleManager: FileLifecycleManager
|
||||
): Promise<StirlingFile[]> {
|
||||
|
@ -15,8 +15,8 @@ import {
|
||||
* Create stable selectors using stateRef and filesRef
|
||||
*/
|
||||
export function createFileSelectors(
|
||||
stateRef: React.RefObject<FileContextState>,
|
||||
filesRef: React.RefObject<Map<FileId, File>>
|
||||
stateRef: React.MutableRefObject<FileContextState>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>
|
||||
): FileContextSelectors {
|
||||
return {
|
||||
getFile: (id: FileId) => {
|
||||
@ -125,8 +125,8 @@ export function buildQuickKeySetFromMetadata(metadata: { name: string; size: num
|
||||
* Get primary file (first in list) - commonly used pattern
|
||||
*/
|
||||
export function getPrimaryFile(
|
||||
stateRef: React.RefObject<FileContextState>,
|
||||
filesRef: React.RefObject<Map<FileId, File>>
|
||||
stateRef: React.MutableRefObject<FileContextState>,
|
||||
filesRef: React.MutableRefObject<Map<FileId, File>>
|
||||
): { file?: File; record?: StirlingFileStub } {
|
||||
const primaryFileId = stateRef.current.files.ids[0];
|
||||
if (!primaryFileId) return {};
|
||||
|
@ -16,7 +16,7 @@ export class FileLifecycleManager {
|
||||
private fileGenerations = new Map<string, number>(); // Generation tokens to prevent stale cleanup
|
||||
|
||||
constructor(
|
||||
private filesRef: React.RefObject<Map<FileId, File>>,
|
||||
private filesRef: React.MutableRefObject<Map<FileId, File>>,
|
||||
private dispatch: React.Dispatch<FileContextAction>
|
||||
) {}
|
||||
|
||||
@ -34,7 +34,7 @@ export class FileLifecycleManager {
|
||||
/**
|
||||
* Clean up resources for a specific file (with stateRef access for complete cleanup)
|
||||
*/
|
||||
cleanupFile = (fileId: FileId, stateRef?: React.RefObject<any>): void => {
|
||||
cleanupFile = (fileId: FileId, stateRef?: React.MutableRefObject<any>): void => {
|
||||
// Use comprehensive cleanup (same as removeFiles)
|
||||
this.cleanupAllResourcesForFile(fileId, stateRef);
|
||||
|
||||
@ -68,7 +68,7 @@ export class FileLifecycleManager {
|
||||
/**
|
||||
* Schedule delayed cleanup for a file with generation token to prevent stale cleanup
|
||||
*/
|
||||
scheduleCleanup = (fileId: FileId, delay: number = 30000, stateRef?: React.RefObject<any>): void => {
|
||||
scheduleCleanup = (fileId: FileId, delay: number = 30000, stateRef?: React.MutableRefObject<any>): void => {
|
||||
// Cancel existing timer
|
||||
const existingTimer = this.cleanupTimers.get(fileId);
|
||||
if (existingTimer) {
|
||||
@ -101,7 +101,7 @@ export class FileLifecycleManager {
|
||||
/**
|
||||
* Remove a file immediately with complete resource cleanup
|
||||
*/
|
||||
removeFiles = (fileIds: FileId[], stateRef?: React.RefObject<any>): void => {
|
||||
removeFiles = (fileIds: FileId[], stateRef?: React.MutableRefObject<any>): void => {
|
||||
fileIds.forEach(fileId => {
|
||||
// Clean up all resources for this file
|
||||
this.cleanupAllResourcesForFile(fileId, stateRef);
|
||||
@ -114,7 +114,7 @@ export class FileLifecycleManager {
|
||||
/**
|
||||
* Complete resource cleanup for a single file
|
||||
*/
|
||||
private cleanupAllResourcesForFile = (fileId: FileId, stateRef?: React.RefObject<any>): void => {
|
||||
private cleanupAllResourcesForFile = (fileId: FileId, stateRef?: React.MutableRefObject<any>): void => {
|
||||
// Remove from files ref
|
||||
this.filesRef.current.delete(fileId);
|
||||
|
||||
@ -166,7 +166,7 @@ export class FileLifecycleManager {
|
||||
/**
|
||||
* Update file record with race condition guards
|
||||
*/
|
||||
updateStirlingFileStub = (fileId: FileId, updates: Partial<StirlingFileStub>, stateRef?: React.RefObject<any>): void => {
|
||||
updateStirlingFileStub = (fileId: FileId, updates: Partial<StirlingFileStub>, stateRef?: React.MutableRefObject<any>): void => {
|
||||
// Guard against updating removed files (race condition protection)
|
||||
if (!this.filesRef.current.has(fileId)) {
|
||||
if (DEBUG) console.warn(`🗂️ Attempted to update removed file (filesRef): ${fileId}`);
|
||||
|
@ -32,7 +32,7 @@ import * as React from 'react';
|
||||
*/
|
||||
|
||||
|
||||
export const useIsOverflowing = (ref: React.RefObject<HTMLElement | null>, callback?: (isOverflow: boolean) => void) => {
|
||||
export const useIsOverflowing = (ref: React.MutableRefObject<HTMLElement | null>, callback?: (isOverflow: boolean) => void) => {
|
||||
// State to track overflow status
|
||||
const [isOverflow, setIsOverflow] = React.useState<boolean | undefined>(undefined);
|
||||
|
||||
@ -42,11 +42,11 @@ export const useIsOverflowing = (ref: React.RefObject<HTMLElement | null>, callb
|
||||
// Function to check if element is overflowing
|
||||
const trigger = () => {
|
||||
if (!current) return;
|
||||
|
||||
|
||||
// Compare scroll height (total content height) vs client height (visible height)
|
||||
const hasOverflow = current.scrollHeight > current.clientHeight;
|
||||
setIsOverflow(hasOverflow);
|
||||
|
||||
|
||||
// Call optional callback with overflow state
|
||||
if (callback) callback(hasOverflow);
|
||||
};
|
||||
@ -56,13 +56,13 @@ export const useIsOverflowing = (ref: React.RefObject<HTMLElement | null>, callb
|
||||
if ('ResizeObserver' in window) {
|
||||
const resizeObserver = new ResizeObserver(trigger);
|
||||
resizeObserver.observe(current);
|
||||
|
||||
|
||||
// Cleanup function to disconnect observer
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Fallback for browsers without ResizeObserver support
|
||||
// Add a small delay to ensure the element is fully rendered
|
||||
setTimeout(trigger, 0);
|
||||
@ -70,4 +70,4 @@ export const useIsOverflowing = (ref: React.RefObject<HTMLElement | null>, callb
|
||||
}, [callback, ref]);
|
||||
|
||||
return isOverflow;
|
||||
};
|
||||
};
|
||||
|
@ -60,8 +60,8 @@ export function useTooltipPosition({
|
||||
sidebarTooltip: boolean;
|
||||
position: Position;
|
||||
gap: number;
|
||||
triggerRef: React.RefObject<HTMLElement | null>;
|
||||
tooltipRef: React.RefObject<HTMLDivElement | null>;
|
||||
triggerRef: React.MutableRefObject<HTMLElement | null>;
|
||||
tooltipRef: React.MutableRefObject<HTMLDivElement | null>;
|
||||
sidebarRefs?: SidebarRefs;
|
||||
sidebarState?: SidebarState;
|
||||
}): PositionState {
|
||||
|
@ -5,8 +5,8 @@ export interface SidebarState {
|
||||
}
|
||||
|
||||
export interface SidebarRefs {
|
||||
quickAccessRef: React.RefObject<HTMLDivElement | null>;
|
||||
toolPanelRef: React.RefObject<HTMLDivElement | null>;
|
||||
quickAccessRef: React.MutableRefObject<HTMLDivElement | null>;
|
||||
toolPanelRef: React.MutableRefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export interface SidebarInfo {
|
||||
|
Loading…
Reference in New Issue
Block a user