fix: leerer select in node im config panel leading to white screen
This commit is contained in:
parent
9e36075f0e
commit
600e0c87dc
3 changed files with 49 additions and 13 deletions
|
|
@ -246,6 +246,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
background: var(--canvas-bg, #fafafa);
|
background: var(--canvas-bg, #fafafa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -594,7 +595,7 @@
|
||||||
.canvasArea {
|
.canvasArea {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: 400px;
|
min-height: 0;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
@ -996,6 +997,16 @@
|
||||||
cursor: copy;
|
cursor: copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shell: stretches to full canvas-area height so inner `.nodeConfigPanel` can scroll. */
|
||||||
|
.nodeConfigPanelWrap {
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
/* Node Config Panel
|
/* Node Config Panel
|
||||||
* Fixed-width side panel. The `box-sizing: border-box` + `overflow-x: hidden`
|
* Fixed-width side panel. The `box-sizing: border-box` + `overflow-x: hidden`
|
||||||
* pair acts as a safety net so long unbreakable strings (type names like
|
* pair acts as a safety net so long unbreakable strings (type names like
|
||||||
|
|
@ -1005,11 +1016,12 @@
|
||||||
* a long label rather than escaping to the right.
|
* a long label rather than escaping to the right.
|
||||||
*/
|
*/
|
||||||
.nodeConfigPanel {
|
.nodeConfigPanel {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background: var(--bg-primary, #fff);
|
background: var(--bg-primary, #fff);
|
||||||
border-left: 1px solid var(--border-color, #e0e0e0);
|
border-left: 1px solid var(--border-color, #e0e0e0);
|
||||||
width: 280px;
|
width: 280px;
|
||||||
flex-shrink: 0;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
|
||||||
|
|
@ -957,8 +957,8 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
||||||
onVerboseSchemaChange={setVerboseSchema}
|
onVerboseSchemaChange={setVerboseSchema}
|
||||||
canvasEdit={canvasHeaderEdit}
|
canvasEdit={canvasHeaderEdit}
|
||||||
/>
|
/>
|
||||||
<div className={styles.canvasArea} style={{ display: 'flex', flex: 1, minWidth: 0 }}>
|
<div className={styles.canvasArea} style={{ display: 'flex', flex: 1, minWidth: 0, alignItems: 'stretch' }}>
|
||||||
<div style={{ flex: 1, minWidth: 0 }}>
|
<div style={{ flex: 1, minWidth: 0, minHeight: 0 }}>
|
||||||
<FlowCanvas
|
<FlowCanvas
|
||||||
ref={flowCanvasRef}
|
ref={flowCanvasRef}
|
||||||
nodes={canvasNodes}
|
nodes={canvasNodes}
|
||||||
|
|
@ -995,7 +995,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{configurableSelected && selectedNode && (
|
{configurableSelected && selectedNode && (
|
||||||
<div style={{ flexShrink: 0, display: 'flex', flexDirection: 'column' }}>
|
<div className={styles.nodeConfigPanelWrap}>
|
||||||
<Automation2DataFlowProvider
|
<Automation2DataFlowProvider
|
||||||
node={selectedNode}
|
node={selectedNode}
|
||||||
nodes={canvasNodes}
|
nodes={canvasNodes}
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,30 @@ const DateInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) =>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Backend may send `options: ["a","b"]` or `options: [{ value, label }, ...]` (e.g. context.extractContent). */
|
||||||
|
function _normalizedSelectOptions(raw: unknown): Array<{ value: string; label: string }> {
|
||||||
|
if (!Array.isArray(raw)) return [];
|
||||||
|
const out: Array<{ value: string; label: string }> = [];
|
||||||
|
for (const item of raw) {
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
out.push({ value: item, label: item });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item && typeof item === 'object' && 'value' in item) {
|
||||||
|
const rec = item as { value?: unknown; label?: unknown };
|
||||||
|
if (typeof rec.value === 'string') {
|
||||||
|
const label = typeof rec.label === 'string' && rec.label.length > 0 ? rec.label : rec.value;
|
||||||
|
out.push({ value: rec.value, label });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
const SelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
const SelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||||
const options: string[] =
|
const options = _normalizedSelectOptions(
|
||||||
(param.frontendOptions?.options as string[]) || (param.options as string[]) || [];
|
param.frontendOptions?.options ?? param.options ?? []
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: 8 }}>
|
<div style={{ marginBottom: 8 }}>
|
||||||
<label style={{ display: 'block', fontSize: 12, marginBottom: 2 }}>{param.description || param.name}</label>
|
<label style={{ display: 'block', fontSize: 12, marginBottom: 2 }}>{param.description || param.name}</label>
|
||||||
|
|
@ -113,7 +134,9 @@ const SelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) =
|
||||||
>
|
>
|
||||||
<option value="">—</option>
|
<option value="">—</option>
|
||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<option key={opt} value={opt}>{opt}</option>
|
<option key={opt.value} value={opt.value}>
|
||||||
|
{opt.label}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -121,8 +144,9 @@ const SelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) =
|
||||||
};
|
};
|
||||||
|
|
||||||
const MultiSelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
const MultiSelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||||
const options: string[] =
|
const options = _normalizedSelectOptions(
|
||||||
(param.frontendOptions?.options as string[]) || (param.options as string[]) || [];
|
param.frontendOptions?.options ?? param.options ?? []
|
||||||
|
);
|
||||||
const selected = Array.isArray(value) ? value : [];
|
const selected = Array.isArray(value) ? value : [];
|
||||||
const toggle = (opt: string) => {
|
const toggle = (opt: string) => {
|
||||||
const next = selected.includes(opt) ? selected.filter((v: string) => v !== opt) : [...selected, opt];
|
const next = selected.includes(opt) ? selected.filter((v: string) => v !== opt) : [...selected, opt];
|
||||||
|
|
@ -133,9 +157,9 @@ const MultiSelectInput: React.FC<FieldRendererProps> = ({ param, value, onChange
|
||||||
<label style={{ display: 'block', fontSize: 12, marginBottom: 2 }}>{param.description || param.name}</label>
|
<label style={{ display: 'block', fontSize: 12, marginBottom: 2 }}>{param.description || param.name}</label>
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
|
||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<label key={opt} style={{ fontSize: 12, display: 'flex', alignItems: 'center', gap: 2 }}>
|
<label key={opt.value} style={{ fontSize: 12, display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||||
<input type="checkbox" checked={selected.includes(opt)} onChange={() => toggle(opt)} />
|
<input type="checkbox" checked={selected.includes(opt.value)} onChange={() => toggle(opt.value)} />
|
||||||
{opt}
|
{opt.label}
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue