Skip to content

Commit dfe350f

Browse files
petekpclaude
andcommitted
refactor: replace DecisionPrompt with OptionList, add shared ActionButtons
- Add shared/ directory with reusable ActionButtons component - Add option-list component as replacement for decision-prompt - Add footerActions support to DataTable, MediaCard, and SocialPost - Rename action files for clarity (media-actions.tsx, post-actions.tsx) - Add documentation comments to _ui.tsx/_cn.ts for portability - Remove layout prop from ActionButtons (responsive by default) - Update docs, presets, and gallery for OptionList 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 83f1509 commit dfe350f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1502
-1574
lines changed

app/components/home/chat-showcase.tsx

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ import {
2020
SocialPost,
2121
type SerializableSocialPost,
2222
} from "@/components/tool-ui/social-post";
23-
import {
24-
DecisionPrompt,
25-
type DecisionPromptAction,
26-
} from "@/components/tool-ui/decision-prompt";
2723

2824
type BubbleProps = {
2925
role: "user" | "assistant";
@@ -297,10 +293,10 @@ const SOCIAL_POST: SerializableSocialPost = {
297293
language: "en-US",
298294
};
299295

300-
const DECISION_ACTIONS: DecisionPromptAction[] = [
301-
{ id: "cancel", label: "Discard", variant: "ghost" },
302-
{ id: "edit", label: "Revise", variant: "outline" },
303-
{ id: "send", label: "Post Now", variant: "default" },
296+
const SOCIAL_POST_ACTIONS = [
297+
{ id: "cancel", label: "Discard", variant: "ghost" as const },
298+
{ id: "edit", label: "Revise", variant: "outline" as const },
299+
{ id: "send", label: "Post Now", variant: "default" as const },
304300
];
305301

306302
function createSceneConfigs(): SceneConfig[] {
@@ -327,18 +323,17 @@ function createSceneConfigs(): SceneConfig[] {
327323
toolUI: <MediaCard {...MEDIA_CARD} maxWidth="420px" />,
328324
toolFallbackHeight: 260,
329325
},
330-
// Scene 3: Open Source Release / SocialPost + DecisionPrompt
326+
// Scene 3: Open Source Release / SocialPost with footerActions
331327
{
332328
userMessage: "Draft a tweet about our open-source release",
333329
preamble: "Here's a draft announcement:",
334330
toolUI: (
335-
<div className="w-full max-w-[600px] min-w-0 space-y-3">
336-
<SocialPost {...SOCIAL_POST} className="w-full" maxWidth="100%" />
337-
<DecisionPrompt
338-
prompt="Ready to announce?"
339-
actions={DECISION_ACTIONS}
340-
align="right"
341-
layout="inline"
331+
<div className="w-full max-w-[600px] min-w-0">
332+
<SocialPost
333+
{...SOCIAL_POST}
334+
className="w-full"
335+
maxWidth="100%"
336+
footerActions={SOCIAL_POST_ACTIONS}
342337
/>
343338
</div>
344339
),

app/docs/[component]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getComponentById } from "@/lib/docs/component-registry";
33
import { DataTablePreview } from "./previews/data-table-preview";
44
import { SocialPostPreview } from "./previews/social-post-preview";
55
import { MediaCardPreview } from "./previews/media-card-preview";
6-
import { DecisionPromptPreview } from "./previews/decision-prompt-preview";
6+
import { OptionListPreview } from "./previews/option-list-preview";
77

88
export default async function ComponentPage({
99
params,
@@ -24,8 +24,8 @@ export default async function ComponentPage({
2424
return <SocialPostPreview />;
2525
case "media-card":
2626
return <MediaCardPreview />;
27-
case "decision-prompt":
28-
return <DecisionPromptPreview />;
27+
case "option-list":
28+
return <OptionListPreview />;
2929
default:
3030
notFound();
3131
}

app/docs/[component]/previews/data-table-preview.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,6 @@ export function DataTablePreview({
130130
className="h-full w-full"
131131
componentId="data-table"
132132
config={currentConfig}
133-
socialPostConfig={undefined}
134-
mediaCardConfig={undefined}
135-
decisionPromptConfig={undefined}
136-
decisionPromptSelectedAction={undefined}
137-
decisionPromptSelectedActions={[]}
138-
mediaCardMaxWidth={undefined}
139133
sort={sort}
140134
isLoading={loading}
141135
emptyMessage={emptyMessage}

app/docs/[component]/previews/decision-prompt-preview.tsx

Lines changed: 0 additions & 123 deletions
This file was deleted.

app/docs/[component]/previews/media-card-preview.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,9 @@ export function MediaCardPreview({
8888
<CodePanel
8989
className="h-full w-full"
9090
componentId="media-card"
91-
config={undefined}
92-
socialPostConfig={undefined}
9391
mediaCardConfig={currentConfig}
94-
decisionPromptConfig={undefined}
95-
decisionPromptSelectedAction={undefined}
96-
decisionPromptSelectedActions={[]}
9792
mediaCardMaxWidth="420px"
98-
sort={{}}
9993
isLoading={loading}
100-
emptyMessage=""
10194
mode="plain"
10295
/>
10396
)}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"use client";
2+
3+
import { useCallback, useState, useEffect } from "react";
4+
import { useSearchParams, usePathname, useRouter } from "next/navigation";
5+
import { ComponentPreviewShell } from "../component-preview-shell";
6+
import { PresetSelector } from "../../_components/preset-selector";
7+
import { CodePanel } from "../../_components/code-panel";
8+
import { OptionList } from "@/components/tool-ui/option-list";
9+
import {
10+
OptionListPresetName,
11+
optionListPresets,
12+
} from "@/lib/presets/option-list";
13+
14+
export function OptionListPreview({
15+
withContainer = true,
16+
}: {
17+
withContainer?: boolean;
18+
}) {
19+
const router = useRouter();
20+
const pathname = usePathname();
21+
const searchParams = useSearchParams();
22+
23+
const presetParam = searchParams.get("preset");
24+
const defaultPreset = "export";
25+
const initialPreset: OptionListPresetName =
26+
presetParam && presetParam in optionListPresets
27+
? (presetParam as OptionListPresetName)
28+
: defaultPreset;
29+
30+
const [currentPreset, setCurrentPreset] =
31+
useState<OptionListPresetName>(initialPreset);
32+
const [isLoading, setIsLoading] = useState(false);
33+
const [selection, setSelection] = useState<string[] | string | null>(null);
34+
35+
useEffect(() => {
36+
const presetParam = searchParams.get("preset");
37+
if (
38+
presetParam &&
39+
presetParam in optionListPresets &&
40+
presetParam !== currentPreset
41+
) {
42+
setCurrentPreset(presetParam as OptionListPresetName);
43+
setIsLoading(false);
44+
setSelection(null);
45+
}
46+
}, [searchParams, currentPreset]);
47+
48+
const currentConfig = optionListPresets[currentPreset];
49+
50+
const handleSelectPreset = useCallback(
51+
(preset: unknown) => {
52+
const presetName = preset as OptionListPresetName;
53+
setCurrentPreset(presetName);
54+
setIsLoading(false);
55+
setSelection(null);
56+
57+
const params = new URLSearchParams(searchParams.toString());
58+
params.set("preset", presetName);
59+
router.push(`${pathname}?${params.toString()}`, { scroll: false });
60+
},
61+
[router, pathname, searchParams],
62+
);
63+
64+
return (
65+
<ComponentPreviewShell
66+
withContainer={withContainer}
67+
isLoading={isLoading}
68+
onLoadingChange={setIsLoading}
69+
presetSelector={
70+
<PresetSelector
71+
componentId="option-list"
72+
currentPreset={currentPreset}
73+
onSelectPreset={handleSelectPreset}
74+
/>
75+
}
76+
renderPreview={() => (
77+
<div className="mx-auto" style={{ maxWidth: "420px" }}>
78+
<OptionList
79+
{...currentConfig.optionList}
80+
value={selection}
81+
onChange={setSelection}
82+
onConfirm={(sel) => {
83+
console.log("OptionList confirmed:", sel);
84+
alert(`Selection confirmed: ${JSON.stringify(sel)}`);
85+
}}
86+
/>
87+
</div>
88+
)}
89+
renderCodePanel={() => (
90+
<CodePanel
91+
className="h-full w-full"
92+
componentId="option-list"
93+
optionListConfig={currentConfig}
94+
optionListSelection={selection}
95+
mode="plain"
96+
/>
97+
)}
98+
/>
99+
);
100+
}

app/docs/[component]/previews/social-post-preview.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,8 @@ export function SocialPostPreview({
8686
<CodePanel
8787
className="h-full w-full"
8888
componentId="social-post"
89-
config={undefined}
9089
socialPostConfig={currentConfig}
91-
mediaCardConfig={undefined}
92-
decisionPromptConfig={undefined}
93-
decisionPromptSelectedAction={undefined}
94-
decisionPromptSelectedActions={[]}
95-
mediaCardMaxWidth={undefined}
96-
sort={{}}
9790
isLoading={loading}
98-
emptyMessage=""
9991
mode="plain"
10092
/>
10193
)}

0 commit comments

Comments
 (0)