mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-19 09:59:22 +00:00
Make accordion hook (#4464)
# Description of Changes Make accordion hook, which controls step collapsed state, enforcing only one step being open at any time
This commit is contained in:
parent
d2de8e54aa
commit
ae7be50ec2
123
frontend/src/hooks/tools/shared/useAccordionSteps.ts
Normal file
123
frontend/src/hooks/tools/shared/useAccordionSteps.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { useState, useCallback, useMemo, useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State conditions that affect accordion behavior
|
||||||
|
*/
|
||||||
|
export interface AccordionStateConditions {
|
||||||
|
/** Whether files are present (steps collapse when false) */
|
||||||
|
hasFiles?: boolean;
|
||||||
|
/** Whether results are available (steps collapse when true) */
|
||||||
|
hasResults?: boolean;
|
||||||
|
/** Whether the accordion is disabled (steps collapse when true) */
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the useAccordionSteps hook
|
||||||
|
*/
|
||||||
|
export interface UseAccordionStepsConfig<T extends string | number | symbol> {
|
||||||
|
/** Special step that represents "no step open" state */
|
||||||
|
noneValue: T;
|
||||||
|
/** Initial step to open */
|
||||||
|
initialStep: T;
|
||||||
|
/** Current state conditions that affect accordion behavior */
|
||||||
|
stateConditions?: AccordionStateConditions;
|
||||||
|
/** Callback to run when interacting with a step when we have results (usually used for resetting params) */
|
||||||
|
afterResults?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return type for the useAccordionSteps hook
|
||||||
|
*/
|
||||||
|
export interface AccordionStepsAPI<T extends string | number | symbol> {
|
||||||
|
/** Currently active/open step (noneValue if no step is open) */
|
||||||
|
currentStep: T;
|
||||||
|
/** Get whether a specific step should be collapsed */
|
||||||
|
getCollapsedState: (step: T) => boolean;
|
||||||
|
/** Toggle a step open/closed (accordion behavior - only one open at a time) */
|
||||||
|
handleStepToggle: (step: T) => void;
|
||||||
|
/** Set the currently open step */
|
||||||
|
setOpenStep: (step: T) => void;
|
||||||
|
/** Close all steps */
|
||||||
|
closeAllSteps: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accordion-style step management hook.
|
||||||
|
*
|
||||||
|
* Provides sophisticated accordion behavior where only one step can be open at a time,
|
||||||
|
* with configurable collapse conditions.
|
||||||
|
*/
|
||||||
|
export function useAccordionSteps<T extends string | number | symbol>(
|
||||||
|
config: UseAccordionStepsConfig<T>
|
||||||
|
): AccordionStepsAPI<T> {
|
||||||
|
const { initialStep, stateConditions, noneValue } = config;
|
||||||
|
|
||||||
|
const [openStep, setOpenStep] = useState<T>(initialStep);
|
||||||
|
|
||||||
|
// Determine if all steps should be collapsed based on conditions
|
||||||
|
const shouldCollapseAll = useMemo(() => {
|
||||||
|
if (!stateConditions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(stateConditions.hasFiles === false) ||
|
||||||
|
(stateConditions.hasResults === true) ||
|
||||||
|
(stateConditions.disabled === true)
|
||||||
|
);
|
||||||
|
}, [stateConditions]);
|
||||||
|
|
||||||
|
// Get collapsed state for a specific step
|
||||||
|
const getCollapsedState = useCallback((step: T): boolean => {
|
||||||
|
if (shouldCollapseAll) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return openStep !== step;
|
||||||
|
}
|
||||||
|
}, [openStep, shouldCollapseAll]);
|
||||||
|
|
||||||
|
// Handle step toggle with accordion behavior
|
||||||
|
const handleStepToggle = useCallback((step: T) => {
|
||||||
|
if (stateConditions?.hasResults) {
|
||||||
|
config.afterResults?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all steps should be collapsed, don't allow opening
|
||||||
|
if (shouldCollapseAll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accordion behavior: if clicking the open step, close it; otherwise open the clicked step
|
||||||
|
setOpenStep(currentStep => {
|
||||||
|
if (currentStep === step) {
|
||||||
|
// Clicking the open step - close it
|
||||||
|
return noneValue;
|
||||||
|
} else {
|
||||||
|
// Open the clicked step
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [shouldCollapseAll, noneValue, stateConditions?.hasResults, config.afterResults]);
|
||||||
|
|
||||||
|
// Close all steps
|
||||||
|
const closeAllSteps = useCallback(() => {
|
||||||
|
setOpenStep(noneValue);
|
||||||
|
}, [noneValue]);
|
||||||
|
|
||||||
|
// Automatically reset to first step if we have results
|
||||||
|
// Note that everything is still collapsed when this happens, it's just preparing for re-running the tool
|
||||||
|
useEffect(() => {
|
||||||
|
if (stateConditions?.hasResults) {
|
||||||
|
setOpenStep(initialStep);
|
||||||
|
}
|
||||||
|
}, [stateConditions?.hasResults, initialStep]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentStep: shouldCollapseAll ? noneValue : openStep,
|
||||||
|
getCollapsedState,
|
||||||
|
handleStepToggle,
|
||||||
|
setOpenStep,
|
||||||
|
closeAllSteps
|
||||||
|
};
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||||
|
import { useAccordionSteps } from "../hooks/tools/shared/useAccordionSteps";
|
||||||
import DeleteAllStep from "../components/tools/changeMetadata/steps/DeleteAllStep";
|
import DeleteAllStep from "../components/tools/changeMetadata/steps/DeleteAllStep";
|
||||||
import StandardMetadataStep from "../components/tools/changeMetadata/steps/StandardMetadataStep";
|
import StandardMetadataStep from "../components/tools/changeMetadata/steps/StandardMetadataStep";
|
||||||
import DocumentDatesStep from "../components/tools/changeMetadata/steps/DocumentDatesStep";
|
import DocumentDatesStep from "../components/tools/changeMetadata/steps/DocumentDatesStep";
|
||||||
@ -34,9 +34,6 @@ const ChangeMetadata = (props: BaseToolProps) => {
|
|||||||
const documentDatesTips = useDocumentDatesTips();
|
const documentDatesTips = useDocumentDatesTips();
|
||||||
const advancedOptionsTips = useAdvancedOptionsTips();
|
const advancedOptionsTips = useAdvancedOptionsTips();
|
||||||
|
|
||||||
// Individual step collapse states - only one can be open at a time
|
|
||||||
const [openStep, setOpenStep] = useState<MetadataStep>(MetadataStep.DELETE_ALL);
|
|
||||||
|
|
||||||
const base = useBaseTool(
|
const base = useBaseTool(
|
||||||
'changeMetadata',
|
'changeMetadata',
|
||||||
useChangeMetadataParameters,
|
useChangeMetadataParameters,
|
||||||
@ -47,27 +44,22 @@ const ChangeMetadata = (props: BaseToolProps) => {
|
|||||||
// Extract metadata from uploaded files
|
// Extract metadata from uploaded files
|
||||||
const { isExtractingMetadata } = useMetadataExtraction(base.params);
|
const { isExtractingMetadata } = useMetadataExtraction(base.params);
|
||||||
|
|
||||||
// Compute actual collapsed state based on results and accordion behavior
|
// Accordion step management
|
||||||
const getActualCollapsedState = (stepName: MetadataStep) => {
|
const accordion = useAccordionSteps<MetadataStep>({
|
||||||
return (!base.hasFiles || base.hasResults) ? true : openStep !== stepName;
|
noneValue: MetadataStep.NONE,
|
||||||
};
|
initialStep: MetadataStep.DELETE_ALL,
|
||||||
|
stateConditions: {
|
||||||
// Handle step toggle for accordion behavior
|
hasFiles: base.hasFiles,
|
||||||
const handleStepToggle = (stepName: MetadataStep) => {
|
hasResults: base.hasResults
|
||||||
if (base.hasResults) {
|
},
|
||||||
if (base.settingsCollapsed) {
|
afterResults: base.handleSettingsReset,
|
||||||
base.handleSettingsReset();
|
});
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOpenStep(openStep === stepName ? MetadataStep.NONE : stepName);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create step objects
|
// Create step objects
|
||||||
const createStandardMetadataStep = () => ({
|
const createStandardMetadataStep = () => ({
|
||||||
title: t("changeMetadata.standardFields.title", "Standard Fields"),
|
title: t("changeMetadata.standardFields.title", "Standard Fields"),
|
||||||
isCollapsed: getActualCollapsedState(MetadataStep.STANDARD_METADATA),
|
isCollapsed: accordion.getCollapsedState(MetadataStep.STANDARD_METADATA),
|
||||||
onCollapsedClick: () => handleStepToggle(MetadataStep.STANDARD_METADATA),
|
onCollapsedClick: () => accordion.handleStepToggle(MetadataStep.STANDARD_METADATA),
|
||||||
tooltip: standardMetadataTips,
|
tooltip: standardMetadataTips,
|
||||||
content: (
|
content: (
|
||||||
<StandardMetadataStep
|
<StandardMetadataStep
|
||||||
@ -80,8 +72,8 @@ const ChangeMetadata = (props: BaseToolProps) => {
|
|||||||
|
|
||||||
const createDocumentDatesStep = () => ({
|
const createDocumentDatesStep = () => ({
|
||||||
title: t("changeMetadata.dates.title", "Date Fields"),
|
title: t("changeMetadata.dates.title", "Date Fields"),
|
||||||
isCollapsed: getActualCollapsedState(MetadataStep.DOCUMENT_DATES),
|
isCollapsed: accordion.getCollapsedState(MetadataStep.DOCUMENT_DATES),
|
||||||
onCollapsedClick: () => handleStepToggle(MetadataStep.DOCUMENT_DATES),
|
onCollapsedClick: () => accordion.handleStepToggle(MetadataStep.DOCUMENT_DATES),
|
||||||
tooltip: documentDatesTips,
|
tooltip: documentDatesTips,
|
||||||
content: (
|
content: (
|
||||||
<DocumentDatesStep
|
<DocumentDatesStep
|
||||||
@ -94,8 +86,8 @@ const ChangeMetadata = (props: BaseToolProps) => {
|
|||||||
|
|
||||||
const createAdvancedOptionsStep = () => ({
|
const createAdvancedOptionsStep = () => ({
|
||||||
title: t("changeMetadata.advanced.title", "Advanced Options"),
|
title: t("changeMetadata.advanced.title", "Advanced Options"),
|
||||||
isCollapsed: getActualCollapsedState(MetadataStep.ADVANCED_OPTIONS),
|
isCollapsed: accordion.getCollapsedState(MetadataStep.ADVANCED_OPTIONS),
|
||||||
onCollapsedClick: () => handleStepToggle(MetadataStep.ADVANCED_OPTIONS),
|
onCollapsedClick: () => accordion.handleStepToggle(MetadataStep.ADVANCED_OPTIONS),
|
||||||
tooltip: advancedOptionsTips,
|
tooltip: advancedOptionsTips,
|
||||||
content: (
|
content: (
|
||||||
<AdvancedOptionsStep
|
<AdvancedOptionsStep
|
||||||
@ -114,8 +106,8 @@ const ChangeMetadata = (props: BaseToolProps) => {
|
|||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
title: t("changeMetadata.deleteAll.label", "Remove Existing Metadata"),
|
title: t("changeMetadata.deleteAll.label", "Remove Existing Metadata"),
|
||||||
isCollapsed: getActualCollapsedState(MetadataStep.DELETE_ALL),
|
isCollapsed: accordion.getCollapsedState(MetadataStep.DELETE_ALL),
|
||||||
onCollapsedClick: () => handleStepToggle(MetadataStep.DELETE_ALL),
|
onCollapsedClick: () => accordion.handleStepToggle(MetadataStep.DELETE_ALL),
|
||||||
tooltip: deleteAllTips,
|
tooltip: deleteAllTips,
|
||||||
content: (
|
content: (
|
||||||
<DeleteAllStep
|
<DeleteAllStep
|
||||||
|
Loading…
x
Reference in New Issue
Block a user