Standardize create-flow option steps on one question-and-options layout and Selection sizes.
Method cards, chips, and the right rail now share the two-column shell; last options clear the footer fade, See all stays under the stack, and the left heading no longer jumps when the list expands. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
2fc7fbf818
commit
24a9615c0c
26 files changed
+354
-245
No files matched your search
@@ -41,10 +41,7 @@ import {
|
||||
createFlowStepPathWithSyncDraft,
|
||||
} from "./utils/createFlowPaths";
|
||||
import { getProportionBarProgressForCreateFlowStep } from "./utils/createFlowProportionProgress";
|
||||
import {
|
||||
createFlowStepUsesCenteredTextLayout,
|
||||
createFlowStepUsesCardLayout,
|
||||
} from "./utils/createFlowScreenRegistry";
|
||||
import { createFlowStepUsesCenteredTextLayout } from "./utils/createFlowScreenRegistry";
|
||||
import Button from "../../components/buttons/Button";
|
||||
import { isValidCreateFlowSaveEmail } from "../../../lib/create/isValidCreateFlowSaveEmail";
|
||||
import { buildCreateFlowDraftPayload } from "../../../lib/create/buildCreateFlowDraftPayload";
|
||||
@@ -557,20 +554,19 @@ function CreateFlowLayoutContent({
|
||||
const isFinalReviewLike =
|
||||
currentStep === "final-review" || currentStep === "edit-rule";
|
||||
const isEditRuleStep = currentStep === "edit-rule";
|
||||
const isCardLayoutStep = createFlowStepUsesCardLayout(currentStep);
|
||||
/** Two-column select / right-rail: below `lg` main scrolls; at `lg+` only the right column scrolls. */
|
||||
/** Two-column question-and-options: below `lg` main scrolls; at `lg+` only the right column scrolls. */
|
||||
const isSelectSplitScrollStep = createFlowStepUsesSelectSplitScroll(
|
||||
currentStep,
|
||||
);
|
||||
|
||||
/** Lockup+card / card-stack: `items-start` + shell `my-auto` so overflow scrolls from the top. */
|
||||
/** Lockup+card review: `items-start` + shell `my-auto` so overflow scrolls from the top. */
|
||||
const mainContentClass = isCompletedStep
|
||||
? "items-stretch overflow-y-auto md:overflow-hidden"
|
||||
: isSelectSplitScrollStep
|
||||
? "items-start justify-start overflow-y-auto max-lg:overflow-y-auto lg:min-h-0 lg:items-stretch lg:overflow-hidden"
|
||||
: isCreateTemplatesPickerRoute
|
||||
? "items-start justify-start overflow-y-auto"
|
||||
: isTemplateReviewRoute || isFinalReviewLike || isCardLayoutStep
|
||||
: isTemplateReviewRoute || isFinalReviewLike
|
||||
? CREATE_FLOW_MD_CENTERED_MAIN_CLASS
|
||||
: "items-start justify-center overflow-y-auto md:items-center";
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { CreateFlowHeaderLockup } from "./CreateFlowHeaderLockup";
|
||||
import { CreateFlowStepShell } from "./CreateFlowStepShell";
|
||||
import {
|
||||
CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS,
|
||||
CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
|
||||
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
|
||||
CREATE_FLOW_WIDE_MAX_CLASS,
|
||||
} from "./createFlowLayoutTokens";
|
||||
|
||||
type CreateFlowCardStackStepShellProps = {
|
||||
lockupTitle: string;
|
||||
lockupDescription?: ReactNode;
|
||||
/** When true, the stack region exposes `aria-busy` while recommendations load. */
|
||||
stackBusy?: boolean;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Centered lockup + card-stack layout shared by communication, membership, and
|
||||
* conflict-management steps (Figma compact card stack).
|
||||
*/
|
||||
export function CreateFlowCardStackStepShell({
|
||||
lockupTitle,
|
||||
lockupDescription,
|
||||
stackBusy = false,
|
||||
children,
|
||||
}: CreateFlowCardStackStepShellProps) {
|
||||
return (
|
||||
<CreateFlowStepShell
|
||||
variant="wideGridLoosePadding"
|
||||
contentTopBelowMd="space-800"
|
||||
className={CREATE_FLOW_MD_CENTERED_SHELL_CLASS}
|
||||
>
|
||||
<div
|
||||
className={`flex w-full min-w-0 flex-col items-center gap-6 ${CREATE_FLOW_WIDE_MAX_CLASS}`}
|
||||
>
|
||||
<div className={CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}>
|
||||
<CreateFlowHeaderLockup
|
||||
title={lockupTitle}
|
||||
description={lockupDescription}
|
||||
justification="center"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS}
|
||||
aria-busy={stackBusy}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</CreateFlowStepShell>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from "./CreateFlowStepShell";
|
||||
import {
|
||||
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
|
||||
CREATE_FLOW_SCROLL_END_PAD_CLASS,
|
||||
CREATE_FLOW_SCROLL_REGION_CLASS,
|
||||
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
|
||||
} from "./createFlowLayoutTokens";
|
||||
@@ -23,15 +24,17 @@ interface CreateFlowTwoColumnSelectShellProps {
|
||||
contentTopBelowMd?: CreateFlowContentTopBelowMd;
|
||||
/**
|
||||
* At `lg+`, layout variant: `"center"` = vertically centered pair (community size/structure).
|
||||
* `"start"` = top-weighted layout with a scrollable right column (core values, right-rail): uses `items-stretch`
|
||||
* so the right column gets a bounded height; `items-start` would grow with content and break scroll.
|
||||
* `"start"` = full-height split with a scrollable right column (core values, method cards,
|
||||
* right-rail). Both columns stretch so the left lockup stays in one place when the right
|
||||
* list grows (Figma Flow — Right Rail / Expanded Card Stack). The lockup is centered in
|
||||
* the left pane; `items-start` on the row would let the title track card-stack height.
|
||||
*/
|
||||
lgVerticalAlign?: CreateFlowSelectShellLgVerticalAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Question-and-options layout for create-flow select steps (community size/structure,
|
||||
* core values, stakeholders) and {@link DecisionApproachesScreen}.
|
||||
* core values, stakeholders), method-card steps, and {@link DecisionApproachesScreen}.
|
||||
* Below `lg` (1024px), one column uses available width and main scrolls.
|
||||
* At `lg+`, static header column + scrollable options column (`min-h-0` + overflow).
|
||||
*/
|
||||
@@ -45,9 +48,6 @@ export function CreateFlowTwoColumnSelectShell({
|
||||
const rowLgCrossAlignClass =
|
||||
lgVerticalAlign === "start" ? "lg:items-stretch" : "lg:items-center";
|
||||
|
||||
const leftLgMainJustifyClass =
|
||||
lgVerticalAlign === "start" ? "lg:justify-start" : "lg:justify-center";
|
||||
|
||||
return (
|
||||
<CreateFlowStepShell
|
||||
variant="centeredNarrow"
|
||||
@@ -68,7 +68,7 @@ export function CreateFlowTwoColumnSelectShell({
|
||||
<div
|
||||
className={
|
||||
`flex w-full min-w-0 shrink-0 flex-col items-start gap-[var(--measures-spacing-200,8px)] ` +
|
||||
`lg:flex-1 ${leftLgMainJustifyClass} lg:py-[12px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`
|
||||
`lg:flex-1 lg:justify-center lg:py-[12px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`
|
||||
}
|
||||
>
|
||||
{header}
|
||||
@@ -76,7 +76,7 @@ export function CreateFlowTwoColumnSelectShell({
|
||||
<div
|
||||
className={
|
||||
`relative flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-800,32px)] ` +
|
||||
`overflow-x-hidden lg:min-h-0 lg:flex-1 lg:overflow-y-auto lg:pb-[var(--measures-spacing-300,12px)] ` +
|
||||
`overflow-x-hidden lg:min-h-0 lg:flex-1 lg:overflow-y-auto ${CREATE_FLOW_SCROLL_END_PAD_CLASS} ` +
|
||||
`${CREATE_FLOW_SCROLL_REGION_CLASS} ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`
|
||||
}
|
||||
>
|
||||
|
||||
@@ -31,6 +31,12 @@ export const CREATE_FLOW_SCROLL_REGION_CLASS = "scrollbar-design";
|
||||
export const CREATE_FLOW_FOOTER_SCRIM_CLASS =
|
||||
"pointer-events-none absolute inset-x-0 top-0 z-[1] h-8 -translate-y-full bg-gradient-to-t from-[var(--color-surface-default-primary,#000)] to-transparent";
|
||||
|
||||
/**
|
||||
* Bottom padding in create-flow option scrollports so the last control can
|
||||
* rest above the footer scrim (`h-8`) instead of sitting in the fade.
|
||||
*/
|
||||
export const CREATE_FLOW_SCROLL_END_PAD_CLASS = "pb-12";
|
||||
|
||||
/**
|
||||
* Single column: full width below `lg` so tablet widths use available space;
|
||||
* 640px from `--breakpoint-lg` up (two-column layouts start at `lg`).
|
||||
@@ -46,7 +52,9 @@ export const CREATE_FLOW_MD_UP_GRID_CELL_CLASS =
|
||||
export const CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS =
|
||||
"w-full min-w-0 lg:max-w-[1328px]";
|
||||
|
||||
/** Review / card-stack frames (Figma Flow — Review max 1440). */
|
||||
/**
|
||||
* Review frames (Figma Flow — Review max 1440).
|
||||
*/
|
||||
export const CREATE_FLOW_WIDE_MAX_CLASS = "w-full min-w-0 lg:max-w-[1440px]";
|
||||
|
||||
/**
|
||||
@@ -62,14 +70,6 @@ export const CREATE_FLOW_MD_CENTERED_MAIN_CLASS =
|
||||
*/
|
||||
export const CREATE_FLOW_MD_CENTERED_SHELL_CLASS = "md:my-auto md:pt-0";
|
||||
|
||||
/**
|
||||
* Card-stack steps only (Figma compact card stack): wider than header lockup so the card grid /
|
||||
* pyramid fits (max 860px). Header lockup stays {@link CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}.
|
||||
* Card–card gap uses `gap-2` in `CardStack` (same on mobile and md+).
|
||||
*/
|
||||
export const CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS =
|
||||
"w-full min-w-0 md:max-w-[min(100%,860px)]";
|
||||
|
||||
/**
|
||||
* Inner max-width for step content and the action bar, so the primary button
|
||||
* lines up with the nearest content column above 1440.
|
||||
@@ -88,11 +88,10 @@ export function getCreateFlowContentMaxClass(options: {
|
||||
switch (CREATE_FLOW_SCREEN_REGISTRY[options.step].layoutKind) {
|
||||
case "select":
|
||||
case "right-rail":
|
||||
case "card":
|
||||
case "completed":
|
||||
case "review":
|
||||
return CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS;
|
||||
case "card":
|
||||
return CREATE_FLOW_WIDE_MAX_CLASS;
|
||||
default:
|
||||
return CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* `communication-methods` step — Figma “Flow — Compact Card Stack” (node `20246-15828`).
|
||||
* Registry: `layoutKind: "card"` (`CREATE_FLOW_SCREEN_REGISTRY["communication-methods"]`).
|
||||
*
|
||||
* Lives under `screens/card/` (not `select/`): Figma **card stack** layout is a distinct shell from
|
||||
* two-column chip **select** frames. Future card-stack steps get their own `*Screen.tsx` here and
|
||||
* reuse `CardStack` / `CreateFlowCardStackStepShell` as needed.
|
||||
* Page chrome is the shared question-and-options shell
|
||||
* ({@link CreateFlowTwoColumnSelectShell}); options are {@link CardStack} with
|
||||
* `layout="singleStack"` and {@link Selection} size `l`.
|
||||
*
|
||||
* Card click opens the Figma create modal (node `20246-15829`) with three
|
||||
* editable sections rendered by {@link CommunicationMethodEditFields}. The
|
||||
@@ -18,11 +18,11 @@
|
||||
import { useState, useCallback, useMemo, useRef } from "react";
|
||||
import { useMessages } from "../../../../contexts/MessagesContext";
|
||||
import { useCreateFlow } from "../../context/CreateFlowContext";
|
||||
import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
|
||||
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
|
||||
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
|
||||
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
|
||||
import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
|
||||
import CardStack from "../../../../components/cards/CardStack";
|
||||
import Create from "../../../../components/modals/Create";
|
||||
import InlineTextButton from "../../../../components/buttons/InlineTextButton";
|
||||
@@ -61,7 +61,6 @@ export function CommunicationMethodsScreen() {
|
||||
const m = useMessages();
|
||||
const comm = m.create.customRule.communication;
|
||||
const modalKebabMenu = m.create.customRule.modalKebabMenu;
|
||||
const mdUp = useCreateFlowMdUp();
|
||||
const { confirmDiscard, confirmDialog } = useDiscardCustomizeConfirm();
|
||||
const { state, updateState, replaceState, markCreateFlowInteraction } =
|
||||
useCreateFlow();
|
||||
@@ -120,17 +119,9 @@ export function CommunicationMethodsScreen() {
|
||||
setAddCustomWizardOpen(true);
|
||||
}, [markCreateFlowInteraction]);
|
||||
|
||||
const title = expanded ? comm.page.expandedTitle : comm.page.compactTitle;
|
||||
const title = comm.page.compactTitle;
|
||||
|
||||
const description = expanded ? (
|
||||
<>
|
||||
{comm.page.expandedDescriptionBefore}
|
||||
<InlineTextButton onClick={handleOpenAddWizard}>
|
||||
{comm.page.compactDescriptionLinkLabel}
|
||||
</InlineTextButton>
|
||||
{comm.page.expandedDescriptionAfter}
|
||||
</>
|
||||
) : (
|
||||
const description = (
|
||||
<>
|
||||
{comm.page.compactDescriptionBefore}
|
||||
<InlineTextButton onClick={handleOpenAddWizard}>
|
||||
@@ -694,30 +685,41 @@ export function CommunicationMethodsScreen() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateFlowCardStackStepShell
|
||||
lockupTitle={title}
|
||||
lockupDescription={description}
|
||||
stackBusy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
<CardStack
|
||||
cards={sampleCards}
|
||||
selectedIds={selectedIds}
|
||||
onCardSelect={handleCardClick}
|
||||
expanded={expanded}
|
||||
onToggleExpand={() => {
|
||||
markCreateFlowInteraction();
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
hasMore={true}
|
||||
toggleLabel={comm.page.seeAllLink}
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
compactDesktopLayout="flexWrap"
|
||||
headerLockupSize={mdUp ? "L" : "M"}
|
||||
<CreateFlowTwoColumnSelectShell
|
||||
contentTopBelowMd="space-800"
|
||||
lgVerticalAlign="start"
|
||||
header={
|
||||
<CreateFlowHeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="left"
|
||||
/>
|
||||
)}
|
||||
</CreateFlowCardStackStepShell>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="flex w-full min-w-0 flex-col items-stretch gap-6 py-0 lg:h-full lg:min-h-0 lg:flex-1"
|
||||
aria-busy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
<CardStack
|
||||
cards={sampleCards}
|
||||
selectedIds={selectedIds}
|
||||
onCardSelect={handleCardClick}
|
||||
expanded={expanded}
|
||||
onToggleExpand={() => {
|
||||
markCreateFlowInteraction();
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
hasMore={true}
|
||||
toggleLabel={comm.page.seeAllLink}
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
layout="singleStack"
|
||||
className="w-full lg:h-full lg:min-h-0 lg:flex-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CreateFlowTwoColumnSelectShell>
|
||||
<Create
|
||||
isOpen={createModalOpen}
|
||||
onClose={handleCreateModalClose}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* `conflict-management` step — Figma compact card stack (node `20879-15979`).
|
||||
* Registry: `CREATE_FLOW_SCREEN_REGISTRY["conflict-management"]`.
|
||||
*
|
||||
* Page chrome is the shared question-and-options shell
|
||||
* (`CreateFlowTwoColumnSelectShell`); options are CardStack `singleStack`.
|
||||
*
|
||||
* Card click opens the Figma "Add Approach" create modal (node `20874-172292`)
|
||||
* with four controls rendered by {@link ConflictManagementEditFields}: Core
|
||||
* Principle, Applicable Scope (text area), Process Protocol, and Restoration
|
||||
@@ -15,11 +18,11 @@
|
||||
import { useState, useCallback, useMemo, useRef } from "react";
|
||||
import { useMessages } from "../../../../contexts/MessagesContext";
|
||||
import { useCreateFlow } from "../../context/CreateFlowContext";
|
||||
import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
|
||||
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
|
||||
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
|
||||
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
|
||||
import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
|
||||
import CardStack from "../../../../components/cards/CardStack";
|
||||
import Create from "../../../../components/modals/Create";
|
||||
import InlineTextButton from "../../../../components/buttons/InlineTextButton";
|
||||
@@ -58,7 +61,6 @@ export function ConflictManagementScreen() {
|
||||
const m = useMessages();
|
||||
const cm = m.create.customRule.conflictManagement;
|
||||
const modalKebabMenu = m.create.customRule.modalKebabMenu;
|
||||
const mdUp = useCreateFlowMdUp();
|
||||
const { confirmDiscard, confirmDialog } = useDiscardCustomizeConfirm();
|
||||
const { state, updateState, replaceState, markCreateFlowInteraction } =
|
||||
useCreateFlow();
|
||||
@@ -117,17 +119,9 @@ export function ConflictManagementScreen() {
|
||||
setAddCustomWizardOpen(true);
|
||||
}, [markCreateFlowInteraction]);
|
||||
|
||||
const title = expanded ? cm.page.expandedTitle : cm.page.compactTitle;
|
||||
const title = cm.page.compactTitle;
|
||||
|
||||
const description = expanded ? (
|
||||
<>
|
||||
{cm.page.expandedDescriptionBefore}
|
||||
<InlineTextButton onClick={handleOpenAddWizard}>
|
||||
{cm.page.compactDescriptionLinkLabel}
|
||||
</InlineTextButton>
|
||||
{cm.page.expandedDescriptionAfter}
|
||||
</>
|
||||
) : (
|
||||
const description = (
|
||||
<>
|
||||
{cm.page.compactDescriptionBefore}
|
||||
<InlineTextButton onClick={handleOpenAddWizard}>
|
||||
@@ -695,30 +689,41 @@ export function ConflictManagementScreen() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateFlowCardStackStepShell
|
||||
lockupTitle={title}
|
||||
lockupDescription={description}
|
||||
stackBusy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
<CardStack
|
||||
cards={sampleCards}
|
||||
selectedIds={selectedIds}
|
||||
onCardSelect={handleCardClick}
|
||||
expanded={expanded}
|
||||
onToggleExpand={() => {
|
||||
markCreateFlowInteraction();
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
hasMore={true}
|
||||
toggleLabel={cm.page.seeAllLink}
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
compactDesktopLayout="pyramidFive"
|
||||
headerLockupSize={mdUp ? "L" : "M"}
|
||||
<CreateFlowTwoColumnSelectShell
|
||||
contentTopBelowMd="space-800"
|
||||
lgVerticalAlign="start"
|
||||
header={
|
||||
<CreateFlowHeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="left"
|
||||
/>
|
||||
)}
|
||||
</CreateFlowCardStackStepShell>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="flex w-full min-w-0 flex-col items-stretch gap-6 py-0 lg:h-full lg:min-h-0 lg:flex-1"
|
||||
aria-busy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
<CardStack
|
||||
cards={sampleCards}
|
||||
selectedIds={selectedIds}
|
||||
onCardSelect={handleCardClick}
|
||||
expanded={expanded}
|
||||
onToggleExpand={() => {
|
||||
markCreateFlowInteraction();
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
hasMore={true}
|
||||
toggleLabel={cm.page.seeAllLink}
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
layout="singleStack"
|
||||
className="w-full lg:h-full lg:min-h-0 lg:flex-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CreateFlowTwoColumnSelectShell>
|
||||
<Create
|
||||
isOpen={createModalOpen}
|
||||
onClose={handleCreateModalClose}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* `membership-methods` step — Figma compact card stack (node `20858-13947`).
|
||||
* Registry: `CREATE_FLOW_SCREEN_REGISTRY["membership-methods"]`.
|
||||
*
|
||||
* Page chrome is the shared question-and-options shell
|
||||
* (`CreateFlowTwoColumnSelectShell`); options are CardStack `singleStack`.
|
||||
*
|
||||
* Card click opens the Figma create modal (node `20858-13948`) with three
|
||||
* editable sections rendered by {@link MembershipMethodEditFields}. The same
|
||||
* field set is reused on `/create/final-review` — see `FinalReviewChipEditModal`.
|
||||
@@ -16,11 +19,11 @@
|
||||
import { useState, useCallback, useMemo, useRef } from "react";
|
||||
import { useMessages } from "../../../../contexts/MessagesContext";
|
||||
import { useCreateFlow } from "../../context/CreateFlowContext";
|
||||
import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
|
||||
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
|
||||
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
|
||||
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
|
||||
import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell";
|
||||
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
|
||||
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
|
||||
import CardStack from "../../../../components/cards/CardStack";
|
||||
import Create from "../../../../components/modals/Create";
|
||||
import InlineTextButton from "../../../../components/buttons/InlineTextButton";
|
||||
@@ -59,7 +62,6 @@ export function MembershipMethodsScreen() {
|
||||
const m = useMessages();
|
||||
const mem = m.create.customRule.membership;
|
||||
const modalKebabMenu = m.create.customRule.modalKebabMenu;
|
||||
const mdUp = useCreateFlowMdUp();
|
||||
const { confirmDiscard, confirmDialog } = useDiscardCustomizeConfirm();
|
||||
const { state, updateState, replaceState, markCreateFlowInteraction } =
|
||||
useCreateFlow();
|
||||
@@ -118,17 +120,9 @@ export function MembershipMethodsScreen() {
|
||||
setAddCustomWizardOpen(true);
|
||||
}, [markCreateFlowInteraction]);
|
||||
|
||||
const title = expanded ? mem.page.expandedTitle : mem.page.compactTitle;
|
||||
const title = mem.page.compactTitle;
|
||||
|
||||
const description = expanded ? (
|
||||
<>
|
||||
{mem.page.expandedDescriptionBefore}
|
||||
<InlineTextButton onClick={handleOpenAddWizard}>
|
||||
{mem.page.compactDescriptionLinkLabel}
|
||||
</InlineTextButton>
|
||||
{mem.page.expandedDescriptionAfter}
|
||||
</>
|
||||
) : (
|
||||
const description = (
|
||||
<>
|
||||
{mem.page.compactDescriptionBefore}
|
||||
<InlineTextButton onClick={handleOpenAddWizard}>
|
||||
@@ -688,30 +682,41 @@ export function MembershipMethodsScreen() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateFlowCardStackStepShell
|
||||
lockupTitle={title}
|
||||
lockupDescription={description}
|
||||
stackBusy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
<CardStack
|
||||
cards={sampleCards}
|
||||
selectedIds={selectedIds}
|
||||
onCardSelect={handleCardClick}
|
||||
expanded={expanded}
|
||||
onToggleExpand={() => {
|
||||
markCreateFlowInteraction();
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
hasMore={true}
|
||||
toggleLabel={mem.page.seeAllLink}
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
compactDesktopLayout="pyramidFive"
|
||||
headerLockupSize={mdUp ? "L" : "M"}
|
||||
<CreateFlowTwoColumnSelectShell
|
||||
contentTopBelowMd="space-800"
|
||||
lgVerticalAlign="start"
|
||||
header={
|
||||
<CreateFlowHeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="left"
|
||||
/>
|
||||
)}
|
||||
</CreateFlowCardStackStepShell>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="flex w-full min-w-0 flex-col items-stretch gap-6 py-0 lg:h-full lg:min-h-0 lg:flex-1"
|
||||
aria-busy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
<CardStack
|
||||
cards={sampleCards}
|
||||
selectedIds={selectedIds}
|
||||
onCardSelect={handleCardClick}
|
||||
expanded={expanded}
|
||||
onToggleExpand={() => {
|
||||
markCreateFlowInteraction();
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
hasMore={true}
|
||||
toggleLabel={mem.page.seeAllLink}
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
layout="singleStack"
|
||||
className="w-full lg:h-full lg:min-h-0 lg:flex-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CreateFlowTwoColumnSelectShell>
|
||||
<Create
|
||||
isOpen={createModalOpen}
|
||||
onClose={handleCreateModalClose}
|
||||
|
||||
@@ -766,7 +766,7 @@ export function DecisionApproachesScreen() {
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="flex w-full min-w-0 flex-col items-stretch gap-6 py-0"
|
||||
className="flex w-full min-w-0 flex-col items-stretch gap-6 py-0 lg:h-full lg:min-h-0 lg:flex-1"
|
||||
aria-busy={!recommendationsReady}
|
||||
>
|
||||
{recommendationsReady && (
|
||||
@@ -796,7 +796,7 @@ export function DecisionApproachesScreen() {
|
||||
layout="singleStack"
|
||||
compactRecommendedLimit={5}
|
||||
compactCardIds={compactCardIds}
|
||||
className="w-full"
|
||||
className="w-full lg:h-full lg:min-h-0 lg:flex-1"
|
||||
headerLockupSize={mdUp ? "L" : "M"}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -150,7 +150,7 @@ export function createFlowStepUsesCenteredTextLayout(
|
||||
return CREATE_FLOW_SCREEN_REGISTRY[step].centeredBodyBelowMd;
|
||||
}
|
||||
|
||||
/** Steps whose main area uses the CardStack-style layout (`layoutKind: "card"`). */
|
||||
/** Steps whose Figma frame is a compact card stack (`layoutKind: "card"`). */
|
||||
export function createFlowStepUsesCardLayout(
|
||||
step: CreateFlowStep | null,
|
||||
): boolean {
|
||||
|
||||
@@ -201,7 +201,10 @@ export const CREATE_FLOW_SELECT_SPLIT_SCROLL_STEPS: readonly CreateFlowStep[] =
|
||||
"community-size",
|
||||
"community-structure",
|
||||
"core-values",
|
||||
"communication-methods",
|
||||
"membership-methods",
|
||||
"decision-approaches",
|
||||
"conflict-management",
|
||||
"confirm-stakeholders",
|
||||
] as const;
|
||||
|
||||
|
||||
Reference in new issue
Block a user