in sdks/python/apache_beam/runners/interactive/extensions/apache-beam-jupyterlab-sidepanel/src/yaml/EditablePanel.tsx [176:405]
render() {
const { localData, collapsedKeys } = this.state;
const depth = this.props.depth ?? 0;
return (
<div style={{ fontFamily: 'monospace', fontSize: 14 }}>
{Object.entries(localData).map(([key, value]) => {
const isObject =
typeof value === 'object' &&
value !== null &&
!Array.isArray(value);
const isCollapsed = collapsedKeys.has(key);
return (
<div key={key} style={{ marginBottom: 4 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
{/* Toggle Button or Spacer */}
{isObject ? (
<button
onClick={() => this.toggleCollapse(key)}
style={{
width: 24,
height: 32,
cursor: 'pointer',
border: 'none',
background: 'none',
fontWeight: 'bold',
userSelect: 'none',
flexShrink: 0
}}
>
{isCollapsed ? '▶' : '▼'}
</button>
) : (
<div style={{ width: 24, height: 32 }} />
)}
{/* Key input */}
<input
type="text"
value={key}
onChange={e => this.handleKeyChange(key, e.target.value)}
className="editor-input"
style={{
width: 120,
height: 32,
boxSizing: 'border-box',
padding: '4px 6px',
borderRadius: 4,
border: '1px solid #ccc',
fontFamily: 'inherit',
fontSize: 'inherit'
}}
/>
{/* Value input or collapsed preview */}
<div style={{ flexGrow: 1 }}>
{isObject ? (
isCollapsed ? (
<span style={{ color: '#888' }}>{'{...}'}</span>
) : (
<span style={{ color: '#444', fontStyle: 'italic' }}>
{'{...}'}
</span>
)
) : (
this.renderValueEditor(key, value)
)}
</div>
{/* Action buttons */}
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
{!isObject && (
<button
onClick={() => this.convertToObject(key)}
style={{
width: 70,
height: 32,
padding: '4px 8px',
borderRadius: 4,
border: '1px solid #4caf50',
backgroundColor: '#e8f5e9',
color: '#2e7d32',
cursor: 'pointer'
}}
>
+ Sub
</button>
)}
<button
onClick={() => this.handleDelete(key)}
style={{
height: 32,
padding: '4px 8px',
borderRadius: 4,
border: '1px solid #f44336',
backgroundColor: '#ffebee',
color: '#b71c1c',
cursor: 'pointer'
}}
>
×
</button>
</div>
</div>
{isObject && !isCollapsed && (
<div
style={{
marginLeft: 20,
marginTop: 4,
borderLeft: '1px solid #ccc',
paddingLeft: 8
}}
>
<EditableKeyValuePanel
node={{ id: key, data: value } as Node}
onChange={newVal => this.handleValueChange(key, newVal)}
depth={depth + 1}
/>
</div>
)}
</div>
);
})}
<button
onClick={this.handleAddPair}
style={{
marginTop: 8,
padding: '6px 12px',
borderRadius: 4,
border: '1px solid #2196f3',
backgroundColor: '#e3f2fd',
color: '#0d47a1',
cursor: 'pointer'
}}
>
+ Add {depth > 0 ? 'Nested ' : ''}Field
</button>
{/* Reference Doc */}
{depth === 0 && (
<div
style={{
marginTop: 14,
padding: '14px 20px',
borderRadius: 12,
background: 'linear-gradient(135deg, #f7f9fc, #e3f0ff)',
border: '1px solid #4facfe',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontFamily: 'sans-serif',
fontSize: 14,
color: '#0d47a1',
transition: 'transform 0.15s ease, box-shadow 0.15s ease'
}}
onMouseEnter={e => {
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow =
'0 6px 16px rgba(0, 0, 0, 0.12)';
}}
onMouseLeave={e => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow =
'0 4px 12px rgba(0, 0, 0, 0.08)';
}}
>
{/* Emoji + label */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div
style={{
fontSize: 22,
width: 28,
height: 28,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
{transformEmojiMap[localData.label || ''] || '📄'}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<span
style={{ fontWeight: 600, fontSize: 14, color: '#0d47a1' }}
>
{localData.label}
</span>
<span style={{ fontSize: 12, color: '#555' }}>
Reference for Beam YAML transform
</span>
</div>
</div>
{/* Button */}
<a
href={`https://beam.apache.org/releases/yamldoc/current/#${encodeURIComponent(
localData.label?.toLowerCase() || ''
)}`}
target="_blank"
rel="noopener noreferrer"
style={{
padding: '6px 14px',
borderRadius: 6,
backgroundColor: '#2196f3',
color: 'white',
fontWeight: 500,
fontSize: 13,
textDecoration: 'none',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
transition: 'all 0.2s ease'
}}
onMouseEnter={e => {
e.currentTarget.style.backgroundColor = '#1976d2';
e.currentTarget.style.transform = 'translateY(-1px)';
e.currentTarget.style.boxShadow = '0 4px 8px rgba(0,0,0,0.15)';
}}
onMouseLeave={e => {
e.currentTarget.style.backgroundColor = '#2196f3';
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
}}
>
Open Doc
</a>
</div>
)}
</div>