182 lines
5.5 KiB
TypeScript
182 lines
5.5 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Figma: Overlay / Modal / Card (24973:1852) with Has Text off and Has Steps
|
|
* on. A value opens a card of meaning + signals rows; each row opens that
|
|
* field's Step Modal (23746:49780) with Generate draft on an empty field.
|
|
*/
|
|
|
|
import { memo, useCallback, useEffect, useState, type ReactNode } from "react";
|
|
import Create from "../../../components/modals/Create";
|
|
import { useMessages } from "../../../contexts/MessagesContext";
|
|
import type { ModalHeaderMenuItem } from "../../../components/modals/ModalHeader/ModalHeader.types";
|
|
import type { CoreValueDetailEntry } from "../types";
|
|
import ModalTextAreaField from "./ModalTextAreaField";
|
|
import { ValueDialogStepCard } from "./ValueDialogStepCard";
|
|
|
|
export type ValueDialogSession = "pending" | "editing";
|
|
export type ValueDialogStep = "meaning" | "signals";
|
|
|
|
export type ValueDialogProps = {
|
|
isOpen: boolean;
|
|
valueLabel: string;
|
|
session: ValueDialogSession | null;
|
|
draft: CoreValueDetailEntry;
|
|
onDraftChange: (_next: CoreValueDetailEntry) => void;
|
|
generateDraft?: CoreValueDetailEntry;
|
|
onClose: () => void;
|
|
onConfirm: () => void;
|
|
onDeselect?: () => void;
|
|
nextButtonDisabled?: boolean;
|
|
kebabTriggerAriaLabel?: string;
|
|
kebabMenuAriaLabel?: string;
|
|
kebabMenuItems?: ModalHeaderMenuItem[];
|
|
ariaLabel?: string;
|
|
/** Customized field-blocks body. Replaces the meaning/signals step cards. */
|
|
children?: ReactNode;
|
|
};
|
|
|
|
function ValueDialogComponent({
|
|
isOpen,
|
|
valueLabel,
|
|
session,
|
|
draft,
|
|
onDraftChange,
|
|
generateDraft,
|
|
onClose,
|
|
onConfirm,
|
|
onDeselect,
|
|
nextButtonDisabled = false,
|
|
kebabTriggerAriaLabel,
|
|
kebabMenuAriaLabel,
|
|
kebabMenuItems,
|
|
ariaLabel,
|
|
children,
|
|
}: ValueDialogProps) {
|
|
const m = useMessages();
|
|
const t = m.create.customRule.coreValues.detailModal;
|
|
const draftField = m.create.draftField;
|
|
const [activeStep, setActiveStep] = useState<ValueDialogStep | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!isOpen) {
|
|
setActiveStep(null);
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const handleRequestClose = useCallback(() => {
|
|
if (activeStep != null) {
|
|
setActiveStep(null);
|
|
return;
|
|
}
|
|
onClose();
|
|
}, [activeStep, onClose]);
|
|
|
|
const meaningDone = draft.meaning.trim().length > 0;
|
|
const signalsDone = draft.signals.trim().length > 0;
|
|
const useCustomBody = Boolean(children);
|
|
const showSteps = !useCustomBody && activeStep == null;
|
|
const editing = session === "editing";
|
|
const primaryLabel = editing ? t.save : t.addToRule;
|
|
|
|
const stepTitle =
|
|
activeStep === "meaning"
|
|
? t.meaningStepTitle.replace("{value}", valueLabel)
|
|
: activeStep === "signals"
|
|
? t.signalsLabel
|
|
: valueLabel;
|
|
const stepDescription =
|
|
activeStep === "meaning"
|
|
? t.meaningDescription
|
|
: activeStep === "signals"
|
|
? t.signalsStepDescription.replace("{value}", valueLabel)
|
|
: undefined;
|
|
|
|
return (
|
|
<Create
|
|
isOpen={isOpen}
|
|
onClose={handleRequestClose}
|
|
title={stepTitle}
|
|
description={activeStep != null ? stepDescription : undefined}
|
|
showBackButton={false}
|
|
showCancelButton
|
|
showNextButton
|
|
showRemoveButton={editing && activeStep == null}
|
|
onCancel={handleRequestClose}
|
|
onNext={onConfirm}
|
|
onRemove={onDeselect}
|
|
cancelButtonText={t.cancel}
|
|
nextButtonText={primaryLabel}
|
|
removeButtonText={t.deselect}
|
|
nextButtonDisabled={nextButtonDisabled}
|
|
stepper={false}
|
|
backdropVariant="blurredYellow"
|
|
kebabTriggerAriaLabel={kebabTriggerAriaLabel}
|
|
kebabMenuAriaLabel={kebabMenuAriaLabel}
|
|
kebabMenuItems={
|
|
activeStep == null && kebabMenuItems && kebabMenuItems.length > 0
|
|
? kebabMenuItems
|
|
: undefined
|
|
}
|
|
ariaLabel={ariaLabel || valueLabel}
|
|
>
|
|
{useCustomBody ? (
|
|
children
|
|
) : showSteps ? (
|
|
<div className="flex w-full flex-col gap-[8px]">
|
|
<ValueDialogStepCard
|
|
stepNumber={1}
|
|
stepBadgeLabel={t.stepBadge}
|
|
title={t.meaningLabel}
|
|
description={
|
|
meaningDone ? draft.meaning : t.meaningDescription
|
|
}
|
|
done={meaningDone}
|
|
onClick={() => setActiveStep("meaning")}
|
|
/>
|
|
<ValueDialogStepCard
|
|
stepNumber={2}
|
|
stepBadgeLabel={t.stepBadge}
|
|
title={t.signalsLabel}
|
|
description={
|
|
signalsDone ? draft.signals : t.signalsDescription
|
|
}
|
|
done={signalsDone}
|
|
onClick={() => setActiveStep("signals")}
|
|
/>
|
|
</div>
|
|
) : activeStep === "meaning" ? (
|
|
<ModalTextAreaField
|
|
hideLabel
|
|
label={t.meaningLabel}
|
|
value={draft.meaning}
|
|
onChange={(meaning) => onDraftChange({ ...draft, meaning })}
|
|
placeholder={t.placeholder}
|
|
generateDraftText={generateDraft?.meaning}
|
|
generateDraftLabel={draftField.generateDraft}
|
|
generateDraftHint={t.generateDraftHint}
|
|
filledHint={t.filledHint}
|
|
autoFocus
|
|
/>
|
|
) : (
|
|
<ModalTextAreaField
|
|
hideLabel
|
|
label={t.signalsLabel}
|
|
value={draft.signals}
|
|
onChange={(signals) => onDraftChange({ ...draft, signals })}
|
|
placeholder={t.placeholder}
|
|
generateDraftText={generateDraft?.signals}
|
|
generateDraftLabel={draftField.generateDraft}
|
|
generateDraftHint={t.generateDraftHint}
|
|
filledHint={t.filledHint}
|
|
autoFocus
|
|
/>
|
|
)}
|
|
</Create>
|
|
);
|
|
}
|
|
|
|
ValueDialogComponent.displayName = "ValueDialog";
|
|
|
|
export const ValueDialog = memo(ValueDialogComponent);
|