Skip to content

Commit f5f6f2c

Browse files
1980computerclaude
andcommitted
feat(apollo-react): add NodePropertyPanel editor stories with inline editing
- Editor Full, Compact, and Inline stories for NodePropertyPanel - Inline title/category editing on all editor stories (click-to-edit) - CasePanel with inline title editing and delete-on-hover for Compact - InlineCaseRow with 40px Monaco field and Fixed/Expression mode toggle - Monaco Future Dark/Light backgrounds updated to surface-overlay - Switch component replaced with apollo-wind Switch in all editor stories - @monaco-editor/react added as devDependency - biome: noAutofocus off for stories files; noUnused cleanup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 21cc941 commit f5f6f2c

8 files changed

Lines changed: 748 additions & 333 deletions

File tree

packages/apollo-react/biome.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@
7575
}
7676
},
7777
"overrides": [
78+
{
79+
"includes": ["**/*.stories.tsx"],
80+
"linter": {
81+
"rules": {
82+
"a11y": {
83+
"noAutofocus": "off"
84+
}
85+
}
86+
}
87+
},
7888
{
7989
"includes": ["src/canvas/**"],
8090
"linter": {

packages/apollo-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@
239239
"zustand": "^5.0.9"
240240
},
241241
"devDependencies": {
242+
"@monaco-editor/react": "^4.7.0",
242243
"@lingui/cli": "^5.6.1",
243244
"@lingui/conf": "^5.6.1",
244245
"@lingui/format-json": "^5.6.1",

packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.stories.tsx

Lines changed: 705 additions & 307 deletions
Large diffs are not rendered by default.

packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export function NodePropertyPanel({
4848
resetKey,
4949
className,
5050
contentInset = '1.5rem',
51+
children,
5152
}: NodePropertyPanelProps) {
5253
const hasNodeHeader = !!(nodeLabel || nodeCategory || nodeIcon || action);
5354

@@ -60,10 +61,13 @@ export function NodePropertyPanel({
6061
);
6162

6263
return (
63-
<div className={cn('flex min-h-0 flex-col bg-surface-raised', className)}>
64+
<div
65+
className={cn('flex min-h-0 flex-col bg-surface-raised', className)}
66+
style={{ '--mf-content-inset': contentInset } as CSSProperties}
67+
>
6468
{/* ── Title bar (optional; host panel system may own it) ── */}
6569
{panelTitle && (
66-
<div className="flex h-10 shrink-0 items-center justify-between border-b border-border-subtle px-2">
70+
<div className="flex h-10 shrink-0 items-center justify-between px-2">
6771
<div className="flex items-center gap-1">
6872
<div className="grid size-8 place-items-center text-foreground-subtle">
6973
<GripVertical size={14} />
@@ -86,8 +90,8 @@ export function NodePropertyPanel({
8690

8791
{/* ── Node identity row ── */}
8892
{hasNodeHeader && (
89-
<div className="flex shrink-0 items-center justify-between gap-4 border-b border-border-subtle px-6 py-4">
90-
<div className="flex min-w-0 flex-1 items-center gap-3">
93+
<div className="flex shrink-0 items-center justify-between gap-4 py-4 [padding-inline:var(--mf-content-inset,1.5rem)]">
94+
<div className="flex min-w-0 flex-1 items-center gap-3.5">
9195
{nodeIcon && (
9296
<div className="grid size-11 shrink-0 place-items-center rounded-xl bg-surface-overlay text-foreground-subtle [&>svg]:size-5">
9397
{nodeIcon}
@@ -97,12 +101,12 @@ export function NodePropertyPanel({
97101
{nodeLabel && (
98102
// <span>, not <p>: host apps (e.g. Angular Material's `.mat-typography p`)
99103
// inject a bottom margin on <p> that breaks the header alignment.
100-
<span className="block truncate text-lg font-semibold leading-6 tracking-[-0.4px] text-foreground">
104+
<span className="block truncate text-base font-semibold leading-5 tracking-[-0.3px] text-foreground">
101105
{nodeLabel}
102106
</span>
103107
)}
104108
{nodeCategory && (
105-
<span className="block truncate text-sm leading-5 text-foreground-muted">
109+
<span className="block truncate text-xs leading-4 text-foreground-muted">
106110
{nodeCategory}
107111
</span>
108112
)}
@@ -112,19 +116,16 @@ export function NodePropertyPanel({
112116
</div>
113117
)}
114118

115-
{/* ── Form ── */}
116-
{!formSchema ? (
119+
{/* ── Content (children) or Form ── */}
120+
{children ? (
121+
<div className="min-h-0 flex-1 overflow-auto">{children}</div>
122+
) : !formSchema ? (
117123
<div className="px-6 py-4 text-xs text-foreground-subtle">
118124
No form schema defined for this node.
119125
</div>
120126
) : (
121127
<div className="min-h-0 flex-1 overflow-auto">
122-
<div
123-
style={{ ...SURFACE_REMAP, '--mf-content-inset': contentInset } as CSSProperties}
124-
className="[&_label]:text-foreground-muted"
125-
>
126-
{/* Horizontal padding + the tab underline's full-bleed both derive from
127-
`--mf-content-inset`, so they stay in lockstep. */}
128+
<div style={SURFACE_REMAP} className="[&_label]:text-foreground-muted">
128129
<MetadataForm
129130
key={resetKey}
130131
schema={formSchema}

packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,11 @@ export interface NodePropertyPanelProps {
4646
* edges and re-insets its labels to line up with the fields. Default `1.5rem`.
4747
*/
4848
contentInset?: string;
49+
/**
50+
* Arbitrary content rendered in the panel body instead of a `MetadataForm`.
51+
* Use when the node's properties are not schema-driven (e.g. an expression
52+
* editor, a preview pane, or a custom layout). When provided, `schema` is
53+
* ignored and the children fill the scrollable content area.
54+
*/
55+
children?: ReactNode;
4956
}

packages/apollo-wind/src/components/forms/metadata-form.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -387,20 +387,15 @@ function TabbedStepForm({
387387
return (
388388
<>
389389
<Tabs value={currentTab} onValueChange={setActiveTab} className="flex flex-col gap-4">
390-
{/* Underline tabs (properties-panel style), overriding the primitive's
391-
default segmented/pill look. Scoped here so the shared Tabs primitive
392-
stays segmented for other consumers. */}
393-
{/* Horizontal scroll when the tabs overflow a narrow panel (scrollbar hidden; tabs
394-
stay on one line instead of clipping). The underline can optionally bleed past the
395-
form's horizontal padding to the panel edges: a consumer sets `--mf-content-inset`
396-
to its content inset and the list bleeds by that, re-insetting the labels. Default
397-
0 keeps the underline at content width — correct for any padding. */}
398-
<TabsList className="h-auto justify-start gap-4 overflow-x-auto rounded-none border-b border-border bg-transparent py-0 text-muted-foreground [-ms-overflow-style:none] [margin-inline:calc(var(--mf-content-inset,0px)*-1)] [padding-inline:var(--mf-content-inset,0px)] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
390+
{/* Segmented pill tabs (properties-panel style). Horizontal scroll when
391+
tabs overflow a narrow panel; scrollbar is hidden so tabs stay on one
392+
line without clipping. */}
393+
<TabsList className="h-auto justify-start gap-0.5 overflow-x-auto rounded-lg bg-transparent p-0.5 text-muted-foreground [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
399394
{visibleSteps.map((step) => (
400395
<TabsTrigger
401396
key={step.id}
402397
value={step.id}
403-
className="-mb-px shrink-0 whitespace-nowrap rounded-none border-b-2 border-transparent bg-transparent px-1 pb-2 pt-1 font-medium text-muted-foreground shadow-none transition-colors hover:text-foreground data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:shadow-none"
398+
className="inline-flex h-6 shrink-0 items-center whitespace-nowrap rounded-md px-2.5 text-xs font-medium text-muted-foreground shadow-none transition-colors hover:text-foreground data-[state=active]:bg-surface-overlay data-[state=active]:text-foreground data-[state=active]:shadow-sm"
404399
>
405400
{step.title}
406401
</TabsTrigger>

packages/apollo-wind/src/editor-themes/monaco.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export const apolloFutureDarkMonaco = {
105105
inherit: false,
106106
rules: darkRules,
107107
colors: {
108-
'editor.background': '#18181b', // zinc-900 surface-raised
108+
'editor.background': '#27272a', // zinc-800 surface-overlay
109109
'editor.foreground': '#a1a1aa', // zinc-400 --code-rest
110110
'editorLineNumber.foreground': '#52525b', // zinc-600 comment level
111111
'editorLineNumber.activeForeground': '#a1a1aa', // zinc-400
@@ -149,7 +149,7 @@ export const apolloFutureLightMonaco = {
149149
inherit: false,
150150
rules: lightRules,
151151
colors: {
152-
'editor.background': '#f4f4f5', // zinc-100 surface-raised
152+
'editor.background': '#e4e4e7', // zinc-200 surface-overlay
153153
'editor.foreground': '#52525b', // zinc-600 --code-rest
154154
'editorLineNumber.foreground': '#a1a1aa', // zinc-400 comment level
155155
'editorLineNumber.activeForeground': '#71717a', // zinc-500

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)