Give MultiSelect chips accessible names and start creation fields empty.
Catalog chips now announce Select or Deselect so they are named for assistive tech. Method and value modals open blank; Generate draft inserts the catalog copy that used to be prefilled, and Customize still keeps the section headings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fd9f82ea19
commit
20ea4a2f30
35 files changed
+498
-156
No files matched your search
@@ -35,6 +35,10 @@ import {
|
||||
conflictManagementPresetFor,
|
||||
coreValuePresetFor,
|
||||
decisionApproachPresetFor,
|
||||
emptyCommunicationDetails,
|
||||
emptyConflictManagementDetails,
|
||||
emptyDecisionApproachTextFields,
|
||||
emptyMembershipDetails,
|
||||
membershipPresetFor,
|
||||
} from "../../../../lib/create/finalReviewChipPresets";
|
||||
import { isCustomMethodCardId } from "../../../../lib/create/isCustomMethodCardId";
|
||||
@@ -1129,6 +1133,7 @@ export function FinalReviewChipEditModal({
|
||||
<CoreValueEditFields
|
||||
value={draft.value}
|
||||
onChange={(value) => setDraft({ groupKey: "coreValues", value })}
|
||||
generateDraft={coreValuePresetFor(target.overrideKey)}
|
||||
/>
|
||||
))}
|
||||
{draft?.groupKey === "communication" &&
|
||||
@@ -1156,6 +1161,7 @@ export function FinalReviewChipEditModal({
|
||||
onChange={(value) =>
|
||||
setDraft({ groupKey: "communication", value })
|
||||
}
|
||||
generateDraft={communicationPresetFor(target.overrideKey)}
|
||||
/>
|
||||
))}
|
||||
{draft?.groupKey === "membership" &&
|
||||
@@ -1183,6 +1189,7 @@ export function FinalReviewChipEditModal({
|
||||
onChange={(value) =>
|
||||
setDraft({ groupKey: "membership", value })
|
||||
}
|
||||
generateDraft={membershipPresetFor(target.overrideKey)}
|
||||
/>
|
||||
))}
|
||||
{draft?.groupKey === "decisionApproaches" &&
|
||||
@@ -1210,6 +1217,7 @@ export function FinalReviewChipEditModal({
|
||||
onChange={(value) =>
|
||||
setDraft({ groupKey: "decisionApproaches", value })
|
||||
}
|
||||
generateDraft={decisionApproachPresetFor(target.overrideKey)}
|
||||
/>
|
||||
))}
|
||||
{draft?.groupKey === "conflictManagement" &&
|
||||
@@ -1237,6 +1245,7 @@ export function FinalReviewChipEditModal({
|
||||
onChange={(value) =>
|
||||
setDraft({ groupKey: "conflictManagement", value })
|
||||
}
|
||||
generateDraft={conflictManagementPresetFor(target.overrideKey)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -1324,52 +1333,63 @@ function seedDraftForTarget(
|
||||
switch (target.groupKey) {
|
||||
case "coreValues": {
|
||||
const saved = state.coreValueDetailsByChipId?.[target.overrideKey];
|
||||
const preset = coreValuePresetFor(target.overrideKey);
|
||||
if (saved) {
|
||||
return {
|
||||
groupKey: "coreValues",
|
||||
value: {
|
||||
meaning: saved.meaning,
|
||||
signals: saved.signals,
|
||||
...(saved.supportText ? { supportText: saved.supportText } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
groupKey: "coreValues",
|
||||
value: {
|
||||
meaning: saved?.meaning ?? preset.meaning,
|
||||
signals: saved?.signals ?? preset.signals,
|
||||
...(saved?.supportText ? { supportText: saved.supportText } : {}),
|
||||
},
|
||||
value: { meaning: "", signals: "" },
|
||||
};
|
||||
}
|
||||
case "communication": {
|
||||
const saved =
|
||||
state.communicationMethodDetailsById?.[target.overrideKey] ??
|
||||
communicationPresetFor(target.overrideKey);
|
||||
return { groupKey: "communication", value: { ...saved } };
|
||||
const saved = state.communicationMethodDetailsById?.[target.overrideKey];
|
||||
return {
|
||||
groupKey: "communication",
|
||||
value: { ...(saved ?? emptyCommunicationDetails()) },
|
||||
};
|
||||
}
|
||||
case "membership": {
|
||||
const saved =
|
||||
state.membershipMethodDetailsById?.[target.overrideKey] ??
|
||||
membershipPresetFor(target.overrideKey);
|
||||
return { groupKey: "membership", value: { ...saved } };
|
||||
const saved = state.membershipMethodDetailsById?.[target.overrideKey];
|
||||
return {
|
||||
groupKey: "membership",
|
||||
value: { ...(saved ?? emptyMembershipDetails()) },
|
||||
};
|
||||
}
|
||||
case "decisionApproaches": {
|
||||
const saved =
|
||||
state.decisionApproachDetailsById?.[target.overrideKey] ??
|
||||
decisionApproachPresetFor(target.overrideKey);
|
||||
const saved = state.decisionApproachDetailsById?.[target.overrideKey];
|
||||
const value = saved
|
||||
? {
|
||||
...saved,
|
||||
applicableScope: [...saved.applicableScope],
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
}
|
||||
: emptyDecisionApproachTextFields(
|
||||
decisionApproachPresetFor(target.overrideKey),
|
||||
);
|
||||
return {
|
||||
groupKey: "decisionApproaches",
|
||||
value: {
|
||||
...saved,
|
||||
applicableScope: [...saved.applicableScope],
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
},
|
||||
value,
|
||||
};
|
||||
}
|
||||
case "conflictManagement": {
|
||||
const saved =
|
||||
state.conflictManagementDetailsById?.[target.overrideKey] ??
|
||||
conflictManagementPresetFor(target.overrideKey);
|
||||
const saved = state.conflictManagementDetailsById?.[target.overrideKey];
|
||||
const value = saved
|
||||
? {
|
||||
...saved,
|
||||
applicableScope: [...saved.applicableScope],
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
}
|
||||
: emptyConflictManagementDetails();
|
||||
return {
|
||||
groupKey: "conflictManagement",
|
||||
value: {
|
||||
...saved,
|
||||
applicableScope: [...saved.applicableScope],
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
},
|
||||
value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Shared "labelled text area" field used by every create flow modal section.
|
||||
* Pairs an `InputLabel` with a `TextArea` set to the embedded appearance —
|
||||
* matching the Figma "Control / Text Area" pattern. Section help marks stay
|
||||
* off until tooltip behavior ships.
|
||||
* Shared labelled text area for create-flow modal sections.
|
||||
* Figma: "Step Modal" Text Area Section (23746:49780) when `generateDraftText`
|
||||
* is set — tall compose field + Generate draft control. Otherwise the
|
||||
* embedded Control / Text Area pattern.
|
||||
*/
|
||||
|
||||
import { memo, useId } from "react";
|
||||
@@ -26,9 +26,40 @@ export interface ModalTextAreaFieldProps {
|
||||
placeholder?: string;
|
||||
/** Disable the field. */
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* Catalog/preset copy inserted by Generate draft. When a non-empty string
|
||||
* is passed, the Figma compose field + ghost control are shown.
|
||||
*/
|
||||
generateDraftText?: string;
|
||||
/** Visible label for the Generate draft control. */
|
||||
generateDraftLabel?: string;
|
||||
/** Hint beside Generate draft. */
|
||||
generateDraftHint?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function PlusGlyph() {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="shrink-0 text-[var(--color-content-default-brand-primary,#fefcc9)]"
|
||||
>
|
||||
<path
|
||||
d="M7 3V11M3 7H11"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function ModalTextAreaFieldComponent({
|
||||
label,
|
||||
helpIcon = false,
|
||||
@@ -37,12 +68,21 @@ function ModalTextAreaFieldComponent({
|
||||
rows = 4,
|
||||
placeholder,
|
||||
disabled = false,
|
||||
generateDraftText,
|
||||
generateDraftLabel,
|
||||
generateDraftHint,
|
||||
className = "",
|
||||
}: ModalTextAreaFieldProps) {
|
||||
const labelId = useId();
|
||||
const draftSource = generateDraftText?.trim() ?? "";
|
||||
const showGenerateDraft =
|
||||
draftSource.length > 0 && Boolean(generateDraftLabel);
|
||||
const compose = showGenerateDraft;
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col gap-2 ${className}`.trim()}>
|
||||
<div
|
||||
className={`flex flex-col ${compose ? "gap-[12px]" : "gap-2"} ${className}`.trim()}
|
||||
>
|
||||
<div id={labelId}>
|
||||
<InputLabel
|
||||
label={label}
|
||||
@@ -56,12 +96,29 @@ function ModalTextAreaFieldComponent({
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
size="large"
|
||||
rows={rows}
|
||||
appearance="embedded"
|
||||
rows={compose ? 12 : rows}
|
||||
appearance={compose ? "compose" : "embedded"}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
aria-labelledby={labelId}
|
||||
/>
|
||||
{showGenerateDraft && !disabled ? (
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange(draftSource)}
|
||||
className="flex shrink-0 items-center justify-center gap-[var(--measures-spacing-050,2px)] overflow-clip rounded-[var(--measures-radius-full,9999px)] px-[var(--space-250,10px)] py-[var(--measures-spacing-200,8px)] text-x-small-label text-[color:var(--color-content-default-primary,white)] hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-default-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent"
|
||||
>
|
||||
<PlusGlyph />
|
||||
{generateDraftLabel}
|
||||
</button>
|
||||
{generateDraftHint ? (
|
||||
<p className="text-[length:var(--sizing-300,12px)] leading-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
|
||||
{generateDraftHint}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ export interface CommunicationMethodEditFieldsProps {
|
||||
onChange: (_next: CommunicationMethodDetailEntry) => void;
|
||||
/** When true, fields are not editable (view mode). */
|
||||
readOnly?: boolean;
|
||||
/** Catalog copy inserted by Generate draft. */
|
||||
generateDraft?: CommunicationMethodDetailEntry;
|
||||
}
|
||||
|
||||
const FIELDS: ReadonlyArray<keyof CommunicationMethodDetailEntry> = [
|
||||
@@ -29,9 +31,11 @@ function CommunicationMethodEditFieldsComponent({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false,
|
||||
generateDraft,
|
||||
}: CommunicationMethodEditFieldsProps) {
|
||||
const m = useMessages();
|
||||
const t = m.create.customRule.communication;
|
||||
const draftField = m.create.draftField;
|
||||
|
||||
const patch = useCallback(
|
||||
<K extends keyof CommunicationMethodDetailEntry>(
|
||||
@@ -53,6 +57,10 @@ function CommunicationMethodEditFieldsComponent({
|
||||
value={value[field]}
|
||||
onChange={(v) => patch(field, v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.[field]}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -38,15 +38,19 @@ export interface ConflictManagementEditFieldsProps {
|
||||
value: ConflictManagementDetailEntry;
|
||||
onChange: (_next: ConflictManagementDetailEntry) => void;
|
||||
readOnly?: boolean;
|
||||
/** Catalog copy inserted by Generate draft. */
|
||||
generateDraft?: ConflictManagementDetailEntry;
|
||||
}
|
||||
|
||||
function ConflictManagementEditFieldsComponent({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false,
|
||||
generateDraft,
|
||||
}: ConflictManagementEditFieldsProps) {
|
||||
const m = useMessages();
|
||||
const t = m.create.customRule.conflictManagement;
|
||||
const draftField = m.create.draftField;
|
||||
|
||||
const patch = useCallback(
|
||||
<K extends keyof ConflictManagementDetailEntry>(
|
||||
@@ -65,6 +69,10 @@ function ConflictManagementEditFieldsComponent({
|
||||
value={value.corePrinciple}
|
||||
onChange={(v) => patch("corePrinciple", v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.corePrinciple}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
<ModalTextAreaField
|
||||
label={t.sectionHeadings.applicableScope}
|
||||
@@ -73,18 +81,35 @@ function ConflictManagementEditFieldsComponent({
|
||||
onChange={(v) => onChange(conflictDetailWithScopeTextarea(value, v))}
|
||||
rows={4}
|
||||
disabled={readOnly}
|
||||
generateDraftText={
|
||||
generateDraft && generateDraft.applicableScope.length > 0
|
||||
? generateDraft.applicableScope.join("\n")
|
||||
: generateDraft
|
||||
? conflictScopeTextareaValue(generateDraft)
|
||||
: undefined
|
||||
}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
<ModalTextAreaField
|
||||
label={t.sectionHeadings.processProtocol}
|
||||
value={value.processProtocol}
|
||||
onChange={(v) => patch("processProtocol", v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.processProtocol}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
<ModalTextAreaField
|
||||
label={t.sectionHeadings.restorationFallbacks}
|
||||
value={value.restorationFallbacks}
|
||||
onChange={(v) => patch("restorationFallbacks", v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.restorationFallbacks}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,15 +17,19 @@ export interface CoreValueEditFieldsProps {
|
||||
onChange: (_next: CoreValueDetailEntry) => void;
|
||||
/** Disable meaning/signals. Facet and final-review callers omit this so fields stay editable on open. */
|
||||
readOnly?: boolean;
|
||||
/** Catalog copy inserted by Generate draft. */
|
||||
generateDraft?: CoreValueDetailEntry;
|
||||
}
|
||||
|
||||
function CoreValueEditFieldsComponent({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false,
|
||||
generateDraft,
|
||||
}: CoreValueEditFieldsProps) {
|
||||
const m = useMessages();
|
||||
const t = m.create.customRule.coreValues.detailModal;
|
||||
const draftField = m.create.draftField;
|
||||
|
||||
const patch = useCallback(
|
||||
<K extends keyof CoreValueDetailEntry>(
|
||||
@@ -45,6 +49,10 @@ function CoreValueEditFieldsComponent({
|
||||
onChange={(v) => patch("meaning", v)}
|
||||
rows={4}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.meaning}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
<ModalTextAreaField
|
||||
label={t.signalsLabel}
|
||||
@@ -52,6 +60,10 @@ function CoreValueEditFieldsComponent({
|
||||
onChange={(v) => patch("signals", v)}
|
||||
rows={4}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.signals}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface DecisionApproachEditFieldsProps {
|
||||
value: DecisionApproachDetailEntry;
|
||||
onChange: (_next: DecisionApproachDetailEntry) => void;
|
||||
readOnly?: boolean;
|
||||
/** Catalog copy inserted by Generate draft on text fields. */
|
||||
generateDraft?: DecisionApproachDetailEntry;
|
||||
}
|
||||
|
||||
const CONSENSUS_LEVEL_MIN = 0;
|
||||
@@ -29,9 +31,11 @@ function DecisionApproachEditFieldsComponent({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false,
|
||||
generateDraft,
|
||||
}: DecisionApproachEditFieldsProps) {
|
||||
const m = useMessages();
|
||||
const t = m.create.customRule.decisionApproaches;
|
||||
const draftField = m.create.draftField;
|
||||
|
||||
const scopes = useMemo(
|
||||
() => withDecisionApproachKeyResourceScopes(value.applicableScope),
|
||||
@@ -55,6 +59,10 @@ function DecisionApproachEditFieldsComponent({
|
||||
value={value.corePrinciple}
|
||||
onChange={(v) => patch("corePrinciple", v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.corePrinciple}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
<ApplicableScopeField
|
||||
label={t.sectionHeadings.applicableScope}
|
||||
@@ -79,6 +87,10 @@ function DecisionApproachEditFieldsComponent({
|
||||
value={value.stepByStepInstructions}
|
||||
onChange={(v) => patch("stepByStepInstructions", v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.stepByStepInstructions}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
{value.consensusLevel !== undefined ? (
|
||||
<IncrementerBlock
|
||||
@@ -100,6 +112,10 @@ function DecisionApproachEditFieldsComponent({
|
||||
value={value.objectionsDeadlocks}
|
||||
onChange={(v) => patch("objectionsDeadlocks", v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.objectionsDeadlocks}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface MembershipMethodEditFieldsProps {
|
||||
value: MembershipMethodDetailEntry;
|
||||
onChange: (_next: MembershipMethodDetailEntry) => void;
|
||||
readOnly?: boolean;
|
||||
/** Catalog copy inserted by Generate draft. */
|
||||
generateDraft?: MembershipMethodDetailEntry;
|
||||
}
|
||||
|
||||
const FIELDS: ReadonlyArray<keyof MembershipMethodDetailEntry> = [
|
||||
@@ -28,9 +30,11 @@ function MembershipMethodEditFieldsComponent({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false,
|
||||
generateDraft,
|
||||
}: MembershipMethodEditFieldsProps) {
|
||||
const m = useMessages();
|
||||
const t = m.create.customRule.membership;
|
||||
const draftField = m.create.draftField;
|
||||
|
||||
const patch = useCallback(
|
||||
<K extends keyof MembershipMethodDetailEntry>(
|
||||
@@ -52,6 +56,10 @@ function MembershipMethodEditFieldsComponent({
|
||||
value={value[field]}
|
||||
onChange={(v) => patch(field, v)}
|
||||
disabled={readOnly}
|
||||
placeholder={draftField.placeholder}
|
||||
generateDraftText={generateDraft?.[field]}
|
||||
generateDraftLabel={draftField.generateDraft}
|
||||
generateDraftHint={draftField.generateDraftHint}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ import InlineTextButton from "../../../../components/buttons/InlineTextButton";
|
||||
import { CommunicationMethodEditFields } from "../../components/methodEditFields";
|
||||
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
|
||||
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
|
||||
import { communicationPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import { communicationPresetFor, emptyCommunicationDetails } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
|
||||
import { mergePresetMethodsWithCustom } from "../../../../../lib/create/mergePresetMethodsWithCustom";
|
||||
import { moveFacetSelectionIdToFront } from "../../../../../lib/create/methodCardSelectionOrder";
|
||||
@@ -146,7 +146,7 @@ export function CommunicationMethodsScreen() {
|
||||
if (saved) {
|
||||
return { ...saved };
|
||||
}
|
||||
return communicationPresetFor(id);
|
||||
return emptyCommunicationDetails();
|
||||
},
|
||||
[state.communicationMethodDetailsById],
|
||||
);
|
||||
@@ -756,6 +756,7 @@ export function CommunicationMethodsScreen() {
|
||||
<CommunicationMethodEditFields
|
||||
value={pendingDraft}
|
||||
onChange={handleDraftChange}
|
||||
generateDraft={communicationPresetFor(pendingCardId)}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
|
||||
@@ -26,7 +26,7 @@ import InlineTextButton from "../../../../components/buttons/InlineTextButton";
|
||||
import { ConflictManagementEditFields } from "../../components/methodEditFields";
|
||||
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
|
||||
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
|
||||
import { conflictManagementPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import { conflictManagementPresetFor, emptyConflictManagementDetails } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
|
||||
import { mergePresetMethodsWithCustom } from "../../../../../lib/create/mergePresetMethodsWithCustom";
|
||||
import { moveFacetSelectionIdToFront } from "../../../../../lib/create/methodCardSelectionOrder";
|
||||
@@ -147,7 +147,7 @@ export function ConflictManagementScreen() {
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
};
|
||||
}
|
||||
return conflictManagementPresetFor(id);
|
||||
return emptyConflictManagementDetails();
|
||||
},
|
||||
[state.conflictManagementDetailsById],
|
||||
);
|
||||
@@ -757,6 +757,7 @@ export function ConflictManagementScreen() {
|
||||
<ConflictManagementEditFields
|
||||
value={pendingDraft}
|
||||
onChange={handleDraftChange}
|
||||
generateDraft={conflictManagementPresetFor(pendingCardId)}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
|
||||
@@ -27,7 +27,7 @@ import InlineTextButton from "../../../../components/buttons/InlineTextButton";
|
||||
import { MembershipMethodEditFields } from "../../components/methodEditFields";
|
||||
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
|
||||
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
|
||||
import { membershipPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import { membershipPresetFor, emptyMembershipDetails } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
|
||||
import { mergePresetMethodsWithCustom } from "../../../../../lib/create/mergePresetMethodsWithCustom";
|
||||
import { moveFacetSelectionIdToFront } from "../../../../../lib/create/methodCardSelectionOrder";
|
||||
@@ -144,7 +144,7 @@ export function MembershipMethodsScreen() {
|
||||
if (saved) {
|
||||
return { ...saved };
|
||||
}
|
||||
return membershipPresetFor(id);
|
||||
return emptyMembershipDetails();
|
||||
},
|
||||
[state.membershipMethodDetailsById],
|
||||
);
|
||||
@@ -750,6 +750,7 @@ export function MembershipMethodsScreen() {
|
||||
<MembershipMethodEditFields
|
||||
value={pendingDraft}
|
||||
onChange={handleDraftChange}
|
||||
generateDraft={membershipPresetFor(pendingCardId)}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
|
||||
@@ -33,7 +33,7 @@ import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoCo
|
||||
import { DecisionApproachEditFields } from "../../components/methodEditFields";
|
||||
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
|
||||
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
|
||||
import { decisionApproachPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import { decisionApproachPresetFor, emptyDecisionApproachTextFields } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import {
|
||||
applyDecisionApproachKeyResources,
|
||||
decisionApproachKeyResourceCheckboxIds,
|
||||
@@ -186,7 +186,7 @@ export function DecisionApproachesScreen() {
|
||||
if (saved) {
|
||||
return structuredClone(saved);
|
||||
}
|
||||
return decisionApproachPresetFor(id);
|
||||
return emptyDecisionApproachTextFields(decisionApproachPresetFor(id));
|
||||
},
|
||||
[state.decisionApproachDetailsById],
|
||||
);
|
||||
@@ -840,6 +840,7 @@ export function DecisionApproachesScreen() {
|
||||
<DecisionApproachEditFields
|
||||
value={pendingDraft}
|
||||
onChange={handleDraftChange}
|
||||
generateDraft={decisionApproachPresetFor(pendingCardId)}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
|
||||
@@ -183,16 +183,16 @@ export function CoreValuesSelectScreen() {
|
||||
);
|
||||
|
||||
const getInitialTexts = useCallback(
|
||||
(chipId: string, valueLabel: string): CoreValueDetailEntry => {
|
||||
(chipId: string, _valueLabel: string): CoreValueDetailEntry => {
|
||||
const saved = state.coreValueDetailsByChipId?.[chipId];
|
||||
const preset = getPresetTexts(valueLabel);
|
||||
if (!saved) return EMPTY_DETAIL;
|
||||
return {
|
||||
meaning: saved?.meaning ?? preset.meaning,
|
||||
signals: saved?.signals ?? preset.signals,
|
||||
...(saved?.supportText ? { supportText: saved.supportText } : {}),
|
||||
meaning: saved.meaning,
|
||||
signals: saved.signals,
|
||||
...(saved.supportText ? { supportText: saved.supportText } : {}),
|
||||
};
|
||||
},
|
||||
[state.coreValueDetailsByChipId, getPresetTexts],
|
||||
[state.coreValueDetailsByChipId],
|
||||
);
|
||||
|
||||
const openModal = useCallback(
|
||||
@@ -799,6 +799,7 @@ export function CoreValuesSelectScreen() {
|
||||
<CoreValueEditFields
|
||||
value={draft}
|
||||
onChange={handleDraftChange}
|
||||
generateDraft={getPresetTexts(modalChipLabel)}
|
||||
/>
|
||||
)}
|
||||
</Create>
|
||||
|
||||
@@ -93,7 +93,7 @@ const ChipContainer = memo<ChipProps>(
|
||||
onInputChange={isCustom ? setInputValue : undefined}
|
||||
onInputKeyDown={isCustom ? handleKeyDown : undefined}
|
||||
inputRef={isCustom ? inputRef : undefined}
|
||||
ariaLabel={ariaLabel}
|
||||
ariaLabel={ariaLabel ?? (isCustom ? undefined : label)}
|
||||
confirmAriaLabel={t("chipConfirm")}
|
||||
typeToAddPlaceholder={t("chipTypeToAdd")}
|
||||
closeAriaLabel={t("chipClose")}
|
||||
|
||||
@@ -156,7 +156,7 @@ function ChipView({
|
||||
};
|
||||
|
||||
const sharedA11y = {
|
||||
"aria-label": ariaLabel,
|
||||
...(ariaLabel ? { "aria-label": ariaLabel } : {}),
|
||||
...(isToggle ? { "aria-pressed": isSelected } : {}),
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ const MultiSelectContainer = memo<MultiSelectProps>(
|
||||
addButtonAriaLabel={
|
||||
addButtonText || t("multiSelectAddFallback")
|
||||
}
|
||||
chipSelectAriaLabel={t("chipSelect")}
|
||||
chipDeselectAriaLabel={t("chipDeselect")}
|
||||
formHeader={formHeader}
|
||||
onCustomChipConfirm={onCustomChipConfirm}
|
||||
onCustomChipClose={onCustomChipClose}
|
||||
|
||||
@@ -85,6 +85,8 @@ export interface MultiSelectViewProps {
|
||||
addButton: boolean;
|
||||
addButtonText?: string;
|
||||
addButtonAriaLabel: string;
|
||||
chipSelectAriaLabel: string;
|
||||
chipDeselectAriaLabel: string;
|
||||
formHeader: boolean;
|
||||
onCustomChipConfirm?: (chipId: string, value: string) => void;
|
||||
onCustomChipClose?: (chipId: string) => void;
|
||||
|
||||
@@ -16,6 +16,8 @@ function MultiSelectView({
|
||||
addButton,
|
||||
addButtonText,
|
||||
addButtonAriaLabel,
|
||||
chipSelectAriaLabel,
|
||||
chipDeselectAriaLabel,
|
||||
formHeader = true,
|
||||
onCustomChipConfirm,
|
||||
onCustomChipClose,
|
||||
@@ -88,6 +90,14 @@ function MultiSelectView({
|
||||
palette={palette}
|
||||
size={chipSize}
|
||||
disabled={chipDisabled}
|
||||
ariaLabel={
|
||||
isCustom || !option.label
|
||||
? undefined
|
||||
: (isSelected
|
||||
? chipDeselectAriaLabel
|
||||
: chipSelectAriaLabel
|
||||
).replace("{label}", option.label)
|
||||
}
|
||||
onClick={() => {
|
||||
if (!isCustom && !chipDisabled && onChipClick) {
|
||||
onChipClick(option.id);
|
||||
|
||||
@@ -86,6 +86,21 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
textarea: string;
|
||||
label: string;
|
||||
} => {
|
||||
if (appearance === "compose") {
|
||||
if (disabled) {
|
||||
return {
|
||||
textarea:
|
||||
"border-0 bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-tertiary,#b4b4b4)] cursor-not-allowed opacity-60 min-h-[356px] h-[356px] px-[var(--measures-spacing-300,12px)] py-[var(--space-250,10px)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
return {
|
||||
textarea:
|
||||
"border-0 bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] min-h-[356px] h-[356px] px-[var(--measures-spacing-300,12px)] py-[var(--space-250,10px)] focus:outline-none focus:ring-0",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
|
||||
if (appearance === "embedded") {
|
||||
if (disabled) {
|
||||
return {
|
||||
@@ -161,8 +176,8 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
|
||||
const textareaClasses = `
|
||||
scrollbar-design w-full transition-all duration-200 ease-in-out resize-none
|
||||
${appearance === "embedded" ? "rounded-[var(--radius-300,12px)]" : "border"}
|
||||
${currentSize.textarea}
|
||||
${appearance === "embedded" || appearance === "compose" ? "rounded-[var(--radius-300,12px)]" : "border"}
|
||||
${appearance === "compose" ? "text-medium-paragraph" : currentSize.textarea}
|
||||
${stateStyles.textarea}
|
||||
${className}
|
||||
`.trim();
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { InputStateValue } from "../../../../lib/propNormalization";
|
||||
import type {
|
||||
InputStateValue,
|
||||
TextAreaAppearanceValue,
|
||||
} from "../../../../lib/propNormalization";
|
||||
|
||||
export type TextAreaSizeValue = "small" | "medium" | "large";
|
||||
export type TextAreaLabelVariantValue = "default" | "horizontal";
|
||||
|
||||
export type TextAreaAppearanceValue = "default" | "embedded";
|
||||
export type { TextAreaAppearanceValue };
|
||||
|
||||
export interface TextAreaProps extends Omit<
|
||||
React.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
@@ -49,7 +52,8 @@ export interface TextAreaProps extends Omit<
|
||||
/**
|
||||
* Visual appearance. "embedded" matches Create modal sections (Figma 20736-12668):
|
||||
* borderless, darker grey background, tertiary text in default (primary on
|
||||
* focus). "default" is the standard bordered input.
|
||||
* focus). "compose" is the tall empty creation field (Figma 23746:49780).
|
||||
* "default" is the standard bordered input.
|
||||
* @default "default"
|
||||
*/
|
||||
appearance?: TextAreaAppearanceValue;
|
||||
@@ -79,7 +83,7 @@ export interface TextAreaViewProps {
|
||||
textHint?: boolean | string;
|
||||
formHeader?: boolean;
|
||||
showHelpIcon?: boolean;
|
||||
appearance?: "default" | "embedded";
|
||||
appearance?: TextAreaAppearanceValue;
|
||||
helpIconAlt: string;
|
||||
hintDefault: string;
|
||||
}
|
||||
Reference in new issue
Block a user