mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-09-21 06:06:24 +08:00
fix(config): improve handling of jump requests and enhance collapsible component state management
This commit is contained in:
@@ -262,8 +262,12 @@ export function VisualConfigEditor({
|
||||
if (mode !== 'full' || !jumpRequest || handledJumpRef.current === jumpRequest) return;
|
||||
handledJumpRef.current = jumpRequest; // handle each request once, even if deps re-fire
|
||||
const { fieldId, sectionId } = jumpRequest;
|
||||
const targetFieldId =
|
||||
(fieldId === 'tlsCert' || fieldId === 'tlsKey') && !values.tlsEnable
|
||||
? 'tlsEnable'
|
||||
: fieldId;
|
||||
|
||||
const el = document.getElementById(configFieldDomId(fieldId));
|
||||
const el = document.getElementById(configFieldDomId(targetFieldId));
|
||||
if (!el) {
|
||||
// Field not rendered right now (e.g. TLS cert while TLS is disabled) — fall back to
|
||||
// bringing its section into view horizontally.
|
||||
@@ -300,7 +304,7 @@ export function VisualConfigEditor({
|
||||
highlightTimerRef.current = null;
|
||||
highlightedElRef.current = null;
|
||||
}, 1800);
|
||||
}, [mode, jumpRequest]);
|
||||
}, [mode, jumpRequest, values.tlsEnable]);
|
||||
|
||||
// Clear the highlight timer on unmount.
|
||||
useEffect(
|
||||
@@ -481,6 +485,7 @@ export function VisualConfigEditor({
|
||||
(Object.keys(validationErrors ?? {}) as VisualConfigFieldPath[]).some(
|
||||
(field) => field !== 'port' && Boolean(validationErrors?.[field])
|
||||
) || hasPayloadValidationErrors;
|
||||
const payloadValidationKey = hasPayloadValidationErrors ? 'payload-errors' : 'payload-ok';
|
||||
const activeSection = sections.find((section) => section.id === activeSectionId) ?? sections[0];
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1725,6 +1730,7 @@ export function VisualConfigEditor({
|
||||
<SectionStack>
|
||||
<FieldAnchor fieldId="payloadDefaultRules">
|
||||
<Collapsible
|
||||
key={`payloadDefaultRules-${payloadValidationKey}`}
|
||||
label={t('config_management.visual.sections.payload.default_rules')}
|
||||
hint={t('config_management.visual.sections.payload.default_rules_desc')}
|
||||
defaultOpen={hasPayloadValidationErrors}
|
||||
@@ -1739,6 +1745,7 @@ export function VisualConfigEditor({
|
||||
|
||||
<FieldAnchor fieldId="payloadDefaultRawRules">
|
||||
<Collapsible
|
||||
key={`payloadDefaultRawRules-${payloadValidationKey}`}
|
||||
label={t('config_management.visual.sections.payload.default_raw_rules')}
|
||||
hint={t('config_management.visual.sections.payload.default_raw_rules_desc')}
|
||||
defaultOpen={hasPayloadValidationErrors}
|
||||
@@ -1754,6 +1761,7 @@ export function VisualConfigEditor({
|
||||
|
||||
<FieldAnchor fieldId="payloadOverrideRules">
|
||||
<Collapsible
|
||||
key={`payloadOverrideRules-${payloadValidationKey}`}
|
||||
label={t('config_management.visual.sections.payload.override_rules')}
|
||||
hint={t('config_management.visual.sections.payload.override_rules_desc')}
|
||||
defaultOpen={hasPayloadValidationErrors}
|
||||
@@ -1769,6 +1777,7 @@ export function VisualConfigEditor({
|
||||
|
||||
<FieldAnchor fieldId="payloadOverrideRawRules">
|
||||
<Collapsible
|
||||
key={`payloadOverrideRawRules-${payloadValidationKey}`}
|
||||
label={t('config_management.visual.sections.payload.override_raw_rules')}
|
||||
hint={t('config_management.visual.sections.payload.override_raw_rules_desc')}
|
||||
defaultOpen={hasPayloadValidationErrors}
|
||||
@@ -1785,6 +1794,7 @@ export function VisualConfigEditor({
|
||||
|
||||
<FieldAnchor fieldId="payloadFilterRules">
|
||||
<Collapsible
|
||||
key={`payloadFilterRules-${payloadValidationKey}`}
|
||||
label={t('config_management.visual.sections.payload.filter_rules')}
|
||||
hint={t('config_management.visual.sections.payload.filter_rules_desc')}
|
||||
defaultOpen={hasPayloadValidationErrors}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react';
|
||||
import {
|
||||
useState,
|
||||
type HTMLAttributes,
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { IconChevronDown } from '../icons';
|
||||
import styles from './Collapsible.module.scss';
|
||||
|
||||
@@ -22,12 +27,23 @@ export function Collapsible({
|
||||
className,
|
||||
...rest
|
||||
}: PropsWithChildren<CollapsibleProps>) {
|
||||
const detailsProps =
|
||||
open !== undefined ? { open } : { defaultOpen };
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
||||
const resolvedOpen = open ?? uncontrolledOpen;
|
||||
const cls = [styles.root, className].filter(Boolean).join(' ');
|
||||
const contentCls = flush ? styles.contentFlush : styles.content;
|
||||
|
||||
return (
|
||||
<details className={cls} onToggle={onToggle} {...detailsProps} {...rest}>
|
||||
<details
|
||||
className={cls}
|
||||
open={resolvedOpen}
|
||||
onToggle={(event) => {
|
||||
if (open === undefined) {
|
||||
setUncontrolledOpen(event.currentTarget.open);
|
||||
}
|
||||
onToggle?.(event);
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<summary className={styles.summary}>
|
||||
<span className={styles.summaryLabel}>
|
||||
<span>{label}</span>
|
||||
|
||||
Reference in New Issue
Block a user