@workbench-kit/shell-react 0.0.2-prototype.0.2.41 → 0.0.2-prototype.0.2.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -2
- package/package.json +14 -10
- package/src/editor/workspace-reconcile.tsx +12 -7
- package/src/extensions/extension-enablement-controller.ts +40 -9
- package/src/extensions/theme-selection-protection.ts +80 -55
- package/src/field-remap/chrome-labels.ts +117 -0
- package/src/field-remap/convert-note-editor.tsx +119 -104
- package/src/field-remap/convert-palette.tsx +6 -0
- package/src/field-remap/demo.tsx +8 -2
- package/src/field-remap/detail-panel.tsx +535 -434
- package/src/field-remap/document-io.tsx +299 -0
- package/src/field-remap/drag-payload.ts +48 -0
- package/src/field-remap/flow-adapter.ts +216 -88
- package/src/field-remap/flow-ops.ts +150 -0
- package/src/field-remap/flow.tsx +1227 -272
- package/src/field-remap/index.ts +2 -0
- package/src/field-remap/io-class-browse.tsx +34 -22
- package/src/field-remap/keyboard.ts +16 -0
- package/src/field-remap/modal-detail.tsx +34 -0
- package/src/field-remap/panel.tsx +110 -14
- package/src/field-remap/transform-options-editor.tsx +68 -48
- package/src/field-remap/view.css +107 -189
- package/src/index.ts +7 -0
- package/src/keybinding-management-settings.ts +4 -0
- package/src/management/keybinding-overrides-storage.ts +48 -23
- package/src/management/keybinding-settings-view.tsx +31 -0
- package/src/management/keybinding-settings.tsx +4 -12
- package/src/management/use-keybinding-management.ts +72 -22
- package/src/shell/appearance-catalog.ts +453 -0
- package/src/shell/appearance-controller.ts +331 -0
- package/src/shell/appearance-presentation.ts +269 -0
- package/src/shell/provider.tsx +157 -19
- package/src/shell/settings.tsx +282 -153
- package/src/shell/shell.tsx +88 -18
- package/src/workbench/appearance-storage.ts +2 -2
- package/src/workbench/command-host-controller.tsx +223 -0
- package/src/workbench/command-host.tsx +100 -150
- package/src/workbench/keybinding-bridge.ts +84 -38
- package/src/workbench/shell-command-registration.ts +27 -2
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { useMemo, useState, type JSX } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import { useId, useMemo, useState, type JSX } from 'react';
|
|
2
|
+
import { WorkbenchPropertyInline } from '@workbench-kit/react/layout';
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
Select,
|
|
6
|
+
WorkbenchPropertyHint,
|
|
7
|
+
WorkbenchPropertyRow,
|
|
8
|
+
WorkbenchPropertySection,
|
|
9
|
+
WorkbenchPropertyStack,
|
|
10
|
+
} from '@workbench-kit/react/primitives';
|
|
3
11
|
import {
|
|
4
12
|
MAX_TRANSFORM_CHAIN,
|
|
5
13
|
findSourceField,
|
|
@@ -45,6 +53,7 @@ export interface FieldRemapDetailPanelProps {
|
|
|
45
53
|
readonly onDiscardDraft?: ((localId: string) => void) | undefined;
|
|
46
54
|
readonly operators?: readonly MappingOperator[] | undefined;
|
|
47
55
|
readonly onOperatorsChange?: ((operators: readonly MappingOperator[]) => void) | undefined;
|
|
56
|
+
readonly readOnly?: boolean | undefined;
|
|
48
57
|
readonly emptyDetailTitle?: string | undefined;
|
|
49
58
|
readonly emptyDetailDescription?: string | undefined;
|
|
50
59
|
}
|
|
@@ -79,6 +88,7 @@ export function FieldRemapDetailPanel({
|
|
|
79
88
|
onDiscardDraft,
|
|
80
89
|
operators = [],
|
|
81
90
|
onOperatorsChange,
|
|
91
|
+
readOnly = false,
|
|
82
92
|
emptyDetailTitle = 'Start with a convert',
|
|
83
93
|
emptyDetailDescription = 'Use the Convert palette to place a convert, then wire source → draft → target. Or select an existing binding / convert note on the canvas.',
|
|
84
94
|
}: FieldRemapDetailPanelProps): JSX.Element | null {
|
|
@@ -107,8 +117,22 @@ export function FieldRemapDetailPanel({
|
|
|
107
117
|
const [paletteId, setPaletteId] = useState<string>('');
|
|
108
118
|
const [pendingOperatorFieldId, setPendingOperatorFieldId] = useState('');
|
|
109
119
|
const [pendingOperatorSlotId, setPendingOperatorSlotId] = useState('');
|
|
120
|
+
const propertyId = useId();
|
|
121
|
+
const operatorAddInputId = `${propertyId}-operator-add-input`;
|
|
122
|
+
const operatorOutputId = `${propertyId}-operator-output`;
|
|
123
|
+
const operatorInputId = `${propertyId}-operator-input`;
|
|
124
|
+
const operatorAddOutputId = `${propertyId}-operator-add-output`;
|
|
125
|
+
const paletteSelectId = `${propertyId}-palette-select`;
|
|
126
|
+
const itemSourceId = `${propertyId}-item-source`;
|
|
127
|
+
const itemTargetId = `${propertyId}-item-target`;
|
|
110
128
|
const flatSources = useMemo(() => flattenSourceFields(sources), [sources]);
|
|
111
129
|
const flatTargets = useMemo(() => flattenTargetSlots(targets), [targets]);
|
|
130
|
+
const commitOperators = (next: readonly MappingOperator[]) => {
|
|
131
|
+
if (readOnly) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
onOperatorsChange?.(next);
|
|
135
|
+
};
|
|
112
136
|
|
|
113
137
|
if (!selection) {
|
|
114
138
|
return (
|
|
@@ -117,8 +141,16 @@ export function FieldRemapDetailPanel({
|
|
|
117
141
|
data-testid="field-remap-detail"
|
|
118
142
|
aria-label="Binding details"
|
|
119
143
|
>
|
|
120
|
-
<
|
|
121
|
-
|
|
144
|
+
<WorkbenchPropertyStack gap="sm">
|
|
145
|
+
<WorkbenchPropertySection title={emptyDetailTitle}>
|
|
146
|
+
<WorkbenchPropertyHint
|
|
147
|
+
className="workbench-field-remap-detail__muted"
|
|
148
|
+
data-testid="field-remap-detail-empty-hint"
|
|
149
|
+
>
|
|
150
|
+
{emptyDetailDescription}
|
|
151
|
+
</WorkbenchPropertyHint>
|
|
152
|
+
</WorkbenchPropertySection>
|
|
153
|
+
</WorkbenchPropertyStack>
|
|
122
154
|
</aside>
|
|
123
155
|
);
|
|
124
156
|
}
|
|
@@ -131,58 +163,71 @@ export function FieldRemapDetailPanel({
|
|
|
131
163
|
data-testid="field-remap-detail"
|
|
132
164
|
aria-label="Draft convert"
|
|
133
165
|
>
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
166
|
+
<WorkbenchPropertyStack gap="sm">
|
|
167
|
+
<WorkbenchPropertySection
|
|
168
|
+
title="Draft convert"
|
|
169
|
+
actions={
|
|
170
|
+
!readOnly ? (
|
|
171
|
+
<Button
|
|
172
|
+
compact
|
|
173
|
+
type="button"
|
|
174
|
+
data-testid="field-remap-detail-discard-draft"
|
|
175
|
+
onClick={() => {
|
|
176
|
+
onDiscardDraft?.(selection.localId);
|
|
177
|
+
onSelectionChange(null);
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
Discard
|
|
181
|
+
</Button>
|
|
182
|
+
) : null
|
|
183
|
+
}
|
|
149
184
|
>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
<WorkbenchPropertyStack gap="sm">
|
|
186
|
+
<WorkbenchPropertyRow label="Convert">
|
|
187
|
+
<code data-testid="field-remap-detail-draft-id">
|
|
188
|
+
{definition?.label ?? draft?.transformId ?? selection.localId}
|
|
189
|
+
</code>
|
|
190
|
+
</WorkbenchPropertyRow>
|
|
191
|
+
<WorkbenchPropertyHint className="workbench-field-remap-detail__muted">
|
|
192
|
+
Wire source then target ports. When both bind, the convert note editor opens.
|
|
193
|
+
</WorkbenchPropertyHint>
|
|
194
|
+
</WorkbenchPropertyStack>
|
|
195
|
+
</WorkbenchPropertySection>
|
|
196
|
+
<WorkbenchPropertySection
|
|
197
|
+
level="group"
|
|
198
|
+
title="Ports"
|
|
199
|
+
data-testid="field-remap-detail-draft-ports"
|
|
200
|
+
>
|
|
201
|
+
<WorkbenchPropertyStack gap="xs">
|
|
202
|
+
<WorkbenchPropertyRow label="Source">
|
|
203
|
+
<code>{draft?.sourceFieldId ?? 'Unwired'}</code>
|
|
204
|
+
</WorkbenchPropertyRow>
|
|
205
|
+
<WorkbenchPropertyRow label="Target">
|
|
206
|
+
<code>{draft?.targetSlotId ?? 'Unwired'}</code>
|
|
207
|
+
</WorkbenchPropertyRow>
|
|
208
|
+
</WorkbenchPropertyStack>
|
|
209
|
+
</WorkbenchPropertySection>
|
|
210
|
+
</WorkbenchPropertyStack>
|
|
173
211
|
</aside>
|
|
174
212
|
);
|
|
175
213
|
}
|
|
176
214
|
|
|
177
215
|
if (selection.kind === 'operator') {
|
|
178
|
-
if (!operator || !onOperatorsChange) {
|
|
216
|
+
if (!operator || (!readOnly && !onOperatorsChange)) {
|
|
179
217
|
return (
|
|
180
218
|
<aside className="workbench-field-remap-detail" data-testid="field-remap-detail">
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
219
|
+
<WorkbenchPropertyStack gap="sm">
|
|
220
|
+
<WorkbenchPropertySection title="Operator">
|
|
221
|
+
<WorkbenchPropertyStack gap="sm">
|
|
222
|
+
<WorkbenchPropertyRow label="Identifier">
|
|
223
|
+
<code data-testid="field-remap-detail-operator-id">{selection.operatorId}</code>
|
|
224
|
+
</WorkbenchPropertyRow>
|
|
225
|
+
<WorkbenchPropertyHint className="workbench-field-remap-detail__muted">
|
|
226
|
+
Host must wire onOperatorsChange for authoring.
|
|
227
|
+
</WorkbenchPropertyHint>
|
|
228
|
+
</WorkbenchPropertyStack>
|
|
229
|
+
</WorkbenchPropertySection>
|
|
230
|
+
</WorkbenchPropertyStack>
|
|
186
231
|
</aside>
|
|
187
232
|
);
|
|
188
233
|
}
|
|
@@ -192,192 +237,217 @@ export function FieldRemapDetailPanel({
|
|
|
192
237
|
data-testid="field-remap-detail"
|
|
193
238
|
aria-label="Operator details"
|
|
194
239
|
>
|
|
195
|
-
<
|
|
196
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
240
|
+
<WorkbenchPropertyStack gap="sm">
|
|
241
|
+
<WorkbenchPropertySection
|
|
242
|
+
title={operator.kind === 'combine' ? 'Combine (n→1)' : 'Split (1→n)'}
|
|
243
|
+
actions={
|
|
244
|
+
!readOnly ? (
|
|
245
|
+
<Button
|
|
246
|
+
compact
|
|
247
|
+
type="button"
|
|
248
|
+
data-testid="field-remap-detail-delete-operator"
|
|
249
|
+
onClick={() => {
|
|
250
|
+
commitOperators(removeMappingOperator(operators, operator.id));
|
|
251
|
+
onSelectionChange(null);
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
Delete
|
|
255
|
+
</Button>
|
|
256
|
+
) : null
|
|
257
|
+
}
|
|
208
258
|
>
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
<
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
259
|
+
<WorkbenchPropertyRow label="Identifier">
|
|
260
|
+
<code data-testid="field-remap-detail-operator-id">{operator.id}</code>
|
|
261
|
+
</WorkbenchPropertyRow>
|
|
262
|
+
</WorkbenchPropertySection>
|
|
263
|
+
{operator.kind === 'combine' ? (
|
|
264
|
+
<WorkbenchPropertySection level="group" title="Inputs">
|
|
265
|
+
<WorkbenchPropertyStack gap="sm">
|
|
266
|
+
<ul className="workbench-field-remap-detail__item-edges">
|
|
267
|
+
{operator.inputFieldIds.map((fieldId) => (
|
|
268
|
+
<li key={fieldId}>
|
|
269
|
+
<WorkbenchPropertyInline justify="between">
|
|
270
|
+
<code>{fieldLabel(fieldId, sources, targets)}</code>
|
|
271
|
+
{!readOnly ? (
|
|
272
|
+
<Button
|
|
273
|
+
compact
|
|
274
|
+
type="button"
|
|
275
|
+
onClick={() =>
|
|
276
|
+
commitOperators(
|
|
277
|
+
updateMappingOperator(operators, operator.id, (current) =>
|
|
278
|
+
removeOperatorInput(current, fieldId),
|
|
279
|
+
),
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
>
|
|
283
|
+
Remove
|
|
284
|
+
</Button>
|
|
285
|
+
) : null}
|
|
286
|
+
</WorkbenchPropertyInline>
|
|
287
|
+
</li>
|
|
288
|
+
))}
|
|
289
|
+
</ul>
|
|
290
|
+
{!readOnly ? (
|
|
291
|
+
<WorkbenchPropertyRow label="Add input" htmlFor={operatorAddInputId}>
|
|
292
|
+
<WorkbenchPropertyInline>
|
|
293
|
+
<Select
|
|
294
|
+
id={operatorAddInputId}
|
|
295
|
+
aria-label="Add input"
|
|
296
|
+
controlWidth="full"
|
|
297
|
+
data-testid="field-remap-operator-add-input"
|
|
298
|
+
value={pendingOperatorFieldId}
|
|
299
|
+
onValueChange={setPendingOperatorFieldId}
|
|
300
|
+
>
|
|
301
|
+
<option value="">Select…</option>
|
|
302
|
+
{flatSources.map((field) => (
|
|
303
|
+
<option key={field.id} value={field.id}>
|
|
304
|
+
{field.path ?? field.label}
|
|
305
|
+
</option>
|
|
306
|
+
))}
|
|
307
|
+
</Select>
|
|
308
|
+
<Button
|
|
309
|
+
compact
|
|
310
|
+
type="button"
|
|
311
|
+
data-testid="field-remap-operator-bind-input"
|
|
312
|
+
disabled={!pendingOperatorFieldId}
|
|
313
|
+
onClick={() => {
|
|
314
|
+
if (!pendingOperatorFieldId) return;
|
|
315
|
+
commitOperators(
|
|
316
|
+
updateMappingOperator(operators, operator.id, (current) =>
|
|
317
|
+
bindOperatorInput(current, pendingOperatorFieldId),
|
|
318
|
+
),
|
|
319
|
+
);
|
|
320
|
+
setPendingOperatorFieldId('');
|
|
321
|
+
}}
|
|
322
|
+
>
|
|
323
|
+
Add
|
|
324
|
+
</Button>
|
|
325
|
+
</WorkbenchPropertyInline>
|
|
326
|
+
</WorkbenchPropertyRow>
|
|
327
|
+
) : null}
|
|
328
|
+
<WorkbenchPropertyRow label="Output slot" htmlFor={operatorOutputId}>
|
|
329
|
+
<Select
|
|
330
|
+
id={operatorOutputId}
|
|
331
|
+
aria-label="Output slot"
|
|
332
|
+
controlWidth="full"
|
|
333
|
+
data-testid="field-remap-operator-output"
|
|
334
|
+
value={operator.outputSlotId}
|
|
335
|
+
disabled={readOnly}
|
|
336
|
+
onValueChange={(slotId) => {
|
|
337
|
+
commitOperators(
|
|
224
338
|
updateMappingOperator(operators, operator.id, (current) =>
|
|
225
|
-
|
|
339
|
+
slotId
|
|
340
|
+
? bindOperatorOutput(current, slotId)
|
|
341
|
+
: removeOperatorOutput(current, operator.outputSlotId),
|
|
226
342
|
),
|
|
227
|
-
)
|
|
228
|
-
}
|
|
343
|
+
);
|
|
344
|
+
}}
|
|
229
345
|
>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
type="button"
|
|
254
|
-
data-testid="field-remap-operator-bind-input"
|
|
255
|
-
disabled={!pendingOperatorFieldId}
|
|
256
|
-
onClick={() => {
|
|
257
|
-
if (!pendingOperatorFieldId) return;
|
|
258
|
-
onOperatorsChange(
|
|
259
|
-
updateMappingOperator(operators, operator.id, (current) =>
|
|
260
|
-
bindOperatorInput(current, pendingOperatorFieldId),
|
|
261
|
-
),
|
|
262
|
-
);
|
|
263
|
-
setPendingOperatorFieldId('');
|
|
264
|
-
}}
|
|
265
|
-
>
|
|
266
|
-
Add
|
|
267
|
-
</Button>
|
|
268
|
-
</div>
|
|
269
|
-
<div className="workbench-field-remap-detail__field">
|
|
270
|
-
<label>
|
|
271
|
-
<span>Output slot</span>
|
|
272
|
-
<select
|
|
273
|
-
data-testid="field-remap-operator-output"
|
|
274
|
-
value={operator.outputSlotId}
|
|
275
|
-
onChange={(event) => {
|
|
276
|
-
const slotId = event.target.value;
|
|
277
|
-
onOperatorsChange(
|
|
278
|
-
updateMappingOperator(operators, operator.id, (current) =>
|
|
279
|
-
slotId
|
|
280
|
-
? bindOperatorOutput(current, slotId)
|
|
281
|
-
: removeOperatorOutput(current, operator.outputSlotId),
|
|
282
|
-
),
|
|
283
|
-
);
|
|
284
|
-
}}
|
|
285
|
-
>
|
|
286
|
-
<option value="">Unwired…</option>
|
|
287
|
-
{flatTargets.map((slot) => (
|
|
288
|
-
<option key={slot.id} value={slot.id}>
|
|
289
|
-
{slot.path ?? slot.label}
|
|
290
|
-
</option>
|
|
291
|
-
))}
|
|
292
|
-
</select>
|
|
293
|
-
</label>
|
|
294
|
-
</div>
|
|
295
|
-
</section>
|
|
296
|
-
) : (
|
|
297
|
-
<section className="workbench-field-remap-detail__section">
|
|
298
|
-
<div className="workbench-field-remap-detail__field">
|
|
299
|
-
<label>
|
|
300
|
-
<span>Input field</span>
|
|
301
|
-
<select
|
|
302
|
-
data-testid="field-remap-operator-input"
|
|
303
|
-
value={operator.inputFieldId}
|
|
304
|
-
onChange={(event) => {
|
|
305
|
-
const fieldId = event.target.value;
|
|
306
|
-
onOperatorsChange(
|
|
307
|
-
updateMappingOperator(operators, operator.id, (current) =>
|
|
308
|
-
fieldId
|
|
309
|
-
? bindOperatorInput(current, fieldId)
|
|
310
|
-
: removeOperatorInput(current, operator.inputFieldId),
|
|
311
|
-
),
|
|
312
|
-
);
|
|
313
|
-
}}
|
|
314
|
-
>
|
|
315
|
-
<option value="">Unwired…</option>
|
|
316
|
-
{flatSources.map((field) => (
|
|
317
|
-
<option key={field.id} value={field.id}>
|
|
318
|
-
{field.path ?? field.label}
|
|
319
|
-
</option>
|
|
320
|
-
))}
|
|
321
|
-
</select>
|
|
322
|
-
</label>
|
|
323
|
-
</div>
|
|
324
|
-
<h5>Outputs</h5>
|
|
325
|
-
<ul className="workbench-field-remap-detail__item-edges">
|
|
326
|
-
{operator.outputSlotIds.map((slotId) => (
|
|
327
|
-
<li key={slotId}>
|
|
328
|
-
<code>{fieldLabel(slotId, sources, targets)}</code>
|
|
329
|
-
<Button
|
|
330
|
-
compact
|
|
331
|
-
type="button"
|
|
332
|
-
onClick={() =>
|
|
333
|
-
onOperatorsChange(
|
|
346
|
+
<option value="">Unwired…</option>
|
|
347
|
+
{flatTargets.map((slot) => (
|
|
348
|
+
<option key={slot.id} value={slot.id}>
|
|
349
|
+
{slot.path ?? slot.label}
|
|
350
|
+
</option>
|
|
351
|
+
))}
|
|
352
|
+
</Select>
|
|
353
|
+
</WorkbenchPropertyRow>
|
|
354
|
+
</WorkbenchPropertyStack>
|
|
355
|
+
</WorkbenchPropertySection>
|
|
356
|
+
) : (
|
|
357
|
+
<WorkbenchPropertySection level="group" title="Outputs">
|
|
358
|
+
<WorkbenchPropertyStack gap="sm">
|
|
359
|
+
<WorkbenchPropertyRow label="Input field" htmlFor={operatorInputId}>
|
|
360
|
+
<Select
|
|
361
|
+
id={operatorInputId}
|
|
362
|
+
aria-label="Input field"
|
|
363
|
+
controlWidth="full"
|
|
364
|
+
data-testid="field-remap-operator-input"
|
|
365
|
+
value={operator.inputFieldId}
|
|
366
|
+
disabled={readOnly}
|
|
367
|
+
onValueChange={(fieldId) => {
|
|
368
|
+
commitOperators(
|
|
334
369
|
updateMappingOperator(operators, operator.id, (current) =>
|
|
335
|
-
|
|
370
|
+
fieldId
|
|
371
|
+
? bindOperatorInput(current, fieldId)
|
|
372
|
+
: removeOperatorInput(current, operator.inputFieldId),
|
|
336
373
|
),
|
|
337
|
-
)
|
|
338
|
-
}
|
|
374
|
+
);
|
|
375
|
+
}}
|
|
339
376
|
>
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
377
|
+
<option value="">Unwired…</option>
|
|
378
|
+
{flatSources.map((field) => (
|
|
379
|
+
<option key={field.id} value={field.id}>
|
|
380
|
+
{field.path ?? field.label}
|
|
381
|
+
</option>
|
|
382
|
+
))}
|
|
383
|
+
</Select>
|
|
384
|
+
</WorkbenchPropertyRow>
|
|
385
|
+
<ul className="workbench-field-remap-detail__item-edges">
|
|
386
|
+
{operator.outputSlotIds.map((slotId) => (
|
|
387
|
+
<li key={slotId}>
|
|
388
|
+
<WorkbenchPropertyInline justify="between">
|
|
389
|
+
<code>{fieldLabel(slotId, sources, targets)}</code>
|
|
390
|
+
{!readOnly ? (
|
|
391
|
+
<Button
|
|
392
|
+
compact
|
|
393
|
+
type="button"
|
|
394
|
+
onClick={() =>
|
|
395
|
+
commitOperators(
|
|
396
|
+
updateMappingOperator(operators, operator.id, (current) =>
|
|
397
|
+
removeOperatorOutput(current, slotId),
|
|
398
|
+
),
|
|
399
|
+
)
|
|
400
|
+
}
|
|
401
|
+
>
|
|
402
|
+
Remove
|
|
403
|
+
</Button>
|
|
404
|
+
) : null}
|
|
405
|
+
</WorkbenchPropertyInline>
|
|
406
|
+
</li>
|
|
358
407
|
))}
|
|
359
|
-
</
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
408
|
+
</ul>
|
|
409
|
+
{!readOnly ? (
|
|
410
|
+
<WorkbenchPropertyRow label="Add output" htmlFor={operatorAddOutputId}>
|
|
411
|
+
<WorkbenchPropertyInline>
|
|
412
|
+
<Select
|
|
413
|
+
id={operatorAddOutputId}
|
|
414
|
+
aria-label="Add output"
|
|
415
|
+
controlWidth="full"
|
|
416
|
+
data-testid="field-remap-operator-add-output"
|
|
417
|
+
value={pendingOperatorSlotId}
|
|
418
|
+
onValueChange={setPendingOperatorSlotId}
|
|
419
|
+
>
|
|
420
|
+
<option value="">Select…</option>
|
|
421
|
+
{flatTargets.map((slot) => (
|
|
422
|
+
<option key={slot.id} value={slot.id}>
|
|
423
|
+
{slot.path ?? slot.label}
|
|
424
|
+
</option>
|
|
425
|
+
))}
|
|
426
|
+
</Select>
|
|
427
|
+
<Button
|
|
428
|
+
compact
|
|
429
|
+
type="button"
|
|
430
|
+
data-testid="field-remap-operator-bind-output"
|
|
431
|
+
disabled={!pendingOperatorSlotId}
|
|
432
|
+
onClick={() => {
|
|
433
|
+
if (!pendingOperatorSlotId) return;
|
|
434
|
+
commitOperators(
|
|
435
|
+
updateMappingOperator(operators, operator.id, (current) =>
|
|
436
|
+
bindOperatorOutput(current, pendingOperatorSlotId),
|
|
437
|
+
),
|
|
438
|
+
);
|
|
439
|
+
setPendingOperatorSlotId('');
|
|
440
|
+
}}
|
|
441
|
+
>
|
|
442
|
+
Add
|
|
443
|
+
</Button>
|
|
444
|
+
</WorkbenchPropertyInline>
|
|
445
|
+
</WorkbenchPropertyRow>
|
|
446
|
+
) : null}
|
|
447
|
+
</WorkbenchPropertyStack>
|
|
448
|
+
</WorkbenchPropertySection>
|
|
449
|
+
)}
|
|
450
|
+
</WorkbenchPropertyStack>
|
|
381
451
|
</aside>
|
|
382
452
|
);
|
|
383
453
|
}
|
|
@@ -389,23 +459,32 @@ export function FieldRemapDetailPanel({
|
|
|
389
459
|
data-testid="field-remap-detail"
|
|
390
460
|
aria-label="Binding details"
|
|
391
461
|
>
|
|
392
|
-
<
|
|
462
|
+
<WorkbenchPropertyStack gap="sm">
|
|
463
|
+
<WorkbenchPropertySection title="Binding">
|
|
464
|
+
<WorkbenchPropertyHint className="workbench-field-remap-detail__muted">
|
|
465
|
+
Select a binding for mapping details, or a convert node to open the convert editor.
|
|
466
|
+
</WorkbenchPropertyHint>
|
|
467
|
+
</WorkbenchPropertySection>
|
|
468
|
+
</WorkbenchPropertyStack>
|
|
393
469
|
</aside>
|
|
394
470
|
);
|
|
395
471
|
}
|
|
396
472
|
|
|
397
473
|
if (selection.kind === 'transformStep') {
|
|
398
474
|
return (
|
|
399
|
-
<
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
475
|
+
<div data-testid="field-remap-detail-transform-step">
|
|
476
|
+
<ConvertNoteEditor
|
|
477
|
+
edge={edge}
|
|
478
|
+
stepIndex={selection.stepIndex}
|
|
479
|
+
sources={sources}
|
|
480
|
+
targets={targets}
|
|
481
|
+
transforms={transforms}
|
|
482
|
+
edges={edges}
|
|
483
|
+
onEdgesChange={onEdgesChange}
|
|
484
|
+
onSelectionChange={onSelectionChange}
|
|
485
|
+
readOnly={readOnly}
|
|
486
|
+
/>
|
|
487
|
+
</div>
|
|
409
488
|
);
|
|
410
489
|
}
|
|
411
490
|
|
|
@@ -432,7 +511,7 @@ export function FieldRemapDetailPanel({
|
|
|
432
511
|
const itemTargetChildren = targetSlot?.children ?? [];
|
|
433
512
|
|
|
434
513
|
const applyEdge = (next: MappingEdge | null) => {
|
|
435
|
-
if (!next) {
|
|
514
|
+
if (readOnly || !next) {
|
|
436
515
|
return;
|
|
437
516
|
}
|
|
438
517
|
onEdgesChange(edges.map((item) => (item.id === edge.id ? next : item)));
|
|
@@ -444,217 +523,239 @@ export function FieldRemapDetailPanel({
|
|
|
444
523
|
data-testid="field-remap-detail"
|
|
445
524
|
aria-label="Binding details"
|
|
446
525
|
>
|
|
447
|
-
<
|
|
448
|
-
<
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
526
|
+
<WorkbenchPropertyStack gap="sm">
|
|
527
|
+
<WorkbenchPropertySection
|
|
528
|
+
title="Binding"
|
|
529
|
+
actions={
|
|
530
|
+
<Button
|
|
531
|
+
compact
|
|
532
|
+
type="button"
|
|
533
|
+
data-testid="field-remap-detail-clear"
|
|
534
|
+
onClick={() => onSelectionChange(null)}
|
|
535
|
+
>
|
|
536
|
+
Clear
|
|
537
|
+
</Button>
|
|
538
|
+
}
|
|
459
539
|
>
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
540
|
+
<WorkbenchPropertyRow label="Route">
|
|
541
|
+
<code data-testid="field-remap-detail-binding">
|
|
542
|
+
{edge.sourceFieldId} → {edge.targetSlotId}
|
|
543
|
+
</code>
|
|
544
|
+
</WorkbenchPropertyRow>
|
|
545
|
+
</WorkbenchPropertySection>
|
|
463
546
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
547
|
+
<WorkbenchPropertySection level="group" title="Convert chain">
|
|
548
|
+
<WorkbenchPropertyStack gap="sm">
|
|
549
|
+
{chain.length === 0 ? (
|
|
550
|
+
<WorkbenchPropertyHint className="workbench-field-remap-detail__muted">
|
|
551
|
+
Direct (no convert)
|
|
552
|
+
</WorkbenchPropertyHint>
|
|
553
|
+
) : (
|
|
554
|
+
<ol className="workbench-field-remap-detail__steps">
|
|
555
|
+
{chain.map((transformId, index) => {
|
|
556
|
+
const definition = transforms.get(transformId);
|
|
557
|
+
return (
|
|
558
|
+
<li key={`${edge.id}-${index}-${transformId}`}>
|
|
559
|
+
<WorkbenchPropertyInline justify="between">
|
|
560
|
+
<button
|
|
561
|
+
type="button"
|
|
562
|
+
className="workbench-field-remap-detail__step"
|
|
563
|
+
data-testid={`field-remap-detail-step-${index}`}
|
|
564
|
+
onClick={() =>
|
|
565
|
+
onSelectionChange({
|
|
566
|
+
kind: 'transformStep',
|
|
567
|
+
edgeId: edge.id,
|
|
568
|
+
stepIndex: index,
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
>
|
|
572
|
+
<strong>{definition?.label ?? transformId}</strong>
|
|
573
|
+
<code>{transformId}</code>
|
|
574
|
+
<WorkbenchPropertyHint className="workbench-field-remap-detail__step-hint">
|
|
575
|
+
{readOnly ? 'Inspect convert' : 'Edit convert'}
|
|
576
|
+
</WorkbenchPropertyHint>
|
|
577
|
+
</button>
|
|
578
|
+
{!readOnly ? (
|
|
579
|
+
<Button
|
|
580
|
+
compact
|
|
581
|
+
type="button"
|
|
582
|
+
aria-label={`Remove convert step ${index + 1}`}
|
|
583
|
+
data-testid={`field-remap-detail-remove-step-${index}`}
|
|
584
|
+
onClick={() => {
|
|
585
|
+
const next = removeTransformStepFromEdge(edge, index);
|
|
586
|
+
applyEdge(next);
|
|
587
|
+
}}
|
|
588
|
+
>
|
|
589
|
+
×
|
|
590
|
+
</Button>
|
|
591
|
+
) : null}
|
|
592
|
+
</WorkbenchPropertyInline>
|
|
593
|
+
</li>
|
|
594
|
+
);
|
|
595
|
+
})}
|
|
596
|
+
</ol>
|
|
597
|
+
)}
|
|
598
|
+
|
|
599
|
+
{!readOnly && chain.length < MAX_TRANSFORM_CHAIN ? (
|
|
600
|
+
<WorkbenchPropertyRow label="Add convert" htmlFor={paletteSelectId}>
|
|
601
|
+
<WorkbenchPropertyInline
|
|
602
|
+
className="workbench-field-remap-detail__palette"
|
|
603
|
+
data-testid="field-remap-transform-palette"
|
|
604
|
+
>
|
|
605
|
+
<Select
|
|
606
|
+
id={paletteSelectId}
|
|
607
|
+
aria-label="Convert palette"
|
|
608
|
+
controlWidth="full"
|
|
609
|
+
data-testid="field-remap-palette-select"
|
|
610
|
+
value={effectivePaletteId}
|
|
611
|
+
disabled={appendCatalog.length === 0}
|
|
612
|
+
onValueChange={setPaletteId}
|
|
488
613
|
>
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
614
|
+
{appendCatalog.length === 0 ? (
|
|
615
|
+
<option value="">No compatible converts</option>
|
|
616
|
+
) : null}
|
|
617
|
+
{appendCatalog.map((item) => (
|
|
618
|
+
<option key={item.id} value={item.id}>
|
|
619
|
+
{item.label}
|
|
620
|
+
</option>
|
|
621
|
+
))}
|
|
622
|
+
</Select>
|
|
493
623
|
<Button
|
|
494
624
|
compact
|
|
495
625
|
type="button"
|
|
496
|
-
|
|
497
|
-
|
|
626
|
+
data-testid="field-remap-palette-add"
|
|
627
|
+
disabled={!effectivePaletteId}
|
|
498
628
|
onClick={() => {
|
|
499
|
-
const next =
|
|
629
|
+
const next = addTransformStepToEdge(edge, effectivePaletteId, {
|
|
630
|
+
registry: transforms,
|
|
631
|
+
sourceType: portTypes.sourceType,
|
|
632
|
+
targetType: portTypes.targetType,
|
|
633
|
+
});
|
|
634
|
+
if (!next) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
500
637
|
applyEdge(next);
|
|
638
|
+
onSelectionChange({
|
|
639
|
+
kind: 'transformStep',
|
|
640
|
+
edgeId: edge.id,
|
|
641
|
+
stepIndex: (next.transformIds?.length ?? 1) - 1,
|
|
642
|
+
});
|
|
501
643
|
}}
|
|
502
644
|
>
|
|
503
|
-
|
|
645
|
+
Add
|
|
504
646
|
</Button>
|
|
505
|
-
</
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
</
|
|
509
|
-
|
|
647
|
+
</WorkbenchPropertyInline>
|
|
648
|
+
</WorkbenchPropertyRow>
|
|
649
|
+
) : null}
|
|
650
|
+
</WorkbenchPropertyStack>
|
|
651
|
+
</WorkbenchPropertySection>
|
|
510
652
|
|
|
511
|
-
{
|
|
512
|
-
<
|
|
513
|
-
|
|
514
|
-
|
|
653
|
+
{listContextEnabled ? (
|
|
654
|
+
<WorkbenchPropertySection
|
|
655
|
+
level="group"
|
|
656
|
+
title="List context"
|
|
657
|
+
data-testid="field-remap-list-context"
|
|
658
|
+
actions={
|
|
659
|
+
!readOnly && !edge.itemEdges ? (
|
|
660
|
+
<Button
|
|
661
|
+
compact
|
|
662
|
+
type="button"
|
|
663
|
+
data-testid="field-remap-enable-list-context"
|
|
664
|
+
onClick={() => applyEdge(enableListContextOnEdge(edge))}
|
|
665
|
+
>
|
|
666
|
+
Enable item fields
|
|
667
|
+
</Button>
|
|
668
|
+
) : null
|
|
669
|
+
}
|
|
515
670
|
>
|
|
516
|
-
|
|
517
|
-
<
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
>
|
|
525
|
-
{appendCatalog.length === 0 ? (
|
|
526
|
-
<option value="">No compatible converts</option>
|
|
671
|
+
{edge.itemEdges ? (
|
|
672
|
+
<WorkbenchPropertyStack gap="sm">
|
|
673
|
+
{!readOnly ? (
|
|
674
|
+
<WorkbenchPropertyHint className="workbench-field-remap-detail__muted">
|
|
675
|
+
{pendingItemSourceId
|
|
676
|
+
? 'Select a target item field to complete the item binding.'
|
|
677
|
+
: 'Select a source item field, then a target item field.'}
|
|
678
|
+
</WorkbenchPropertyHint>
|
|
527
679
|
) : null}
|
|
528
|
-
{
|
|
529
|
-
<
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
))}
|
|
533
|
-
</select>
|
|
534
|
-
</label>
|
|
535
|
-
<Button
|
|
536
|
-
compact
|
|
537
|
-
type="button"
|
|
538
|
-
data-testid="field-remap-palette-add"
|
|
539
|
-
disabled={!effectivePaletteId}
|
|
540
|
-
onClick={() => {
|
|
541
|
-
const next = addTransformStepToEdge(edge, effectivePaletteId, {
|
|
542
|
-
registry: transforms,
|
|
543
|
-
sourceType: portTypes.sourceType,
|
|
544
|
-
targetType: portTypes.targetType,
|
|
545
|
-
});
|
|
546
|
-
if (!next) {
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
applyEdge(next);
|
|
550
|
-
onSelectionChange({
|
|
551
|
-
kind: 'transformStep',
|
|
552
|
-
edgeId: edge.id,
|
|
553
|
-
stepIndex: (next.transformIds?.length ?? 1) - 1,
|
|
554
|
-
});
|
|
555
|
-
}}
|
|
556
|
-
>
|
|
557
|
-
Add
|
|
558
|
-
</Button>
|
|
559
|
-
</div>
|
|
560
|
-
) : null}
|
|
561
|
-
</section>
|
|
562
|
-
|
|
563
|
-
{listContextEnabled ? (
|
|
564
|
-
<section
|
|
565
|
-
className="workbench-field-remap-detail__section"
|
|
566
|
-
aria-labelledby="field-remap-detail-list-context"
|
|
567
|
-
data-testid="field-remap-list-context"
|
|
568
|
-
>
|
|
569
|
-
<div className="workbench-field-remap-detail__list-context-bar">
|
|
570
|
-
<h5 id="field-remap-detail-list-context">List context</h5>
|
|
571
|
-
{!edge.itemEdges ? (
|
|
572
|
-
<Button
|
|
573
|
-
compact
|
|
574
|
-
type="button"
|
|
575
|
-
data-testid="field-remap-enable-list-context"
|
|
576
|
-
onClick={() => applyEdge(enableListContextOnEdge(edge))}
|
|
577
|
-
>
|
|
578
|
-
Enable item fields
|
|
579
|
-
</Button>
|
|
580
|
-
) : null}
|
|
581
|
-
</div>
|
|
582
|
-
|
|
583
|
-
{edge.itemEdges ? (
|
|
584
|
-
<>
|
|
585
|
-
<p className="workbench-field-remap-detail__muted">
|
|
586
|
-
{pendingItemSourceId
|
|
587
|
-
? 'Select a target item field to complete the item binding.'
|
|
588
|
-
: 'Select a source item field, then a target item field.'}
|
|
589
|
-
</p>
|
|
590
|
-
<div className="workbench-field-remap-detail__item-pickers">
|
|
591
|
-
<label>
|
|
592
|
-
<span>Source item field</span>
|
|
593
|
-
<select
|
|
594
|
-
aria-label="Source item field"
|
|
595
|
-
data-testid="field-remap-item-source"
|
|
596
|
-
value={pendingItemSourceId ?? ''}
|
|
597
|
-
onChange={(event) => setPendingItemSourceId(event.target.value || null)}
|
|
680
|
+
{!readOnly ? (
|
|
681
|
+
<WorkbenchPropertyStack
|
|
682
|
+
className="workbench-field-remap-detail__item-pickers"
|
|
683
|
+
gap="xs"
|
|
598
684
|
>
|
|
599
|
-
<
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
685
|
+
<WorkbenchPropertyRow label="Source item field" htmlFor={itemSourceId}>
|
|
686
|
+
<Select
|
|
687
|
+
id={itemSourceId}
|
|
688
|
+
aria-label="Source item field"
|
|
689
|
+
controlWidth="full"
|
|
690
|
+
data-testid="field-remap-item-source"
|
|
691
|
+
value={pendingItemSourceId ?? ''}
|
|
692
|
+
onValueChange={(sourceId) => setPendingItemSourceId(sourceId || null)}
|
|
693
|
+
>
|
|
694
|
+
<option value="">Select…</option>
|
|
695
|
+
{itemSourceChildren.map((child) => (
|
|
696
|
+
<option key={child.id} value={child.id}>
|
|
697
|
+
{child.path ?? child.label}
|
|
698
|
+
</option>
|
|
699
|
+
))}
|
|
700
|
+
</Select>
|
|
701
|
+
</WorkbenchPropertyRow>
|
|
702
|
+
<WorkbenchPropertyRow label="Target item field" htmlFor={itemTargetId}>
|
|
703
|
+
<Select
|
|
704
|
+
id={itemTargetId}
|
|
705
|
+
aria-label="Target item field"
|
|
706
|
+
controlWidth="full"
|
|
707
|
+
data-testid="field-remap-item-target"
|
|
708
|
+
value=""
|
|
709
|
+
disabled={!pendingItemSourceId}
|
|
710
|
+
onValueChange={(targetId) => {
|
|
711
|
+
if (!pendingItemSourceId || !targetId) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const itemEdge: MappingEdge = {
|
|
715
|
+
id: `ie-${pendingItemSourceId}-${targetId}-${Date.now()}`,
|
|
716
|
+
sourceFieldId: pendingItemSourceId,
|
|
717
|
+
targetSlotId: targetId,
|
|
718
|
+
};
|
|
719
|
+
applyEdge(upsertItemEdgeOnParent(edge, itemEdge));
|
|
720
|
+
setPendingItemSourceId(null);
|
|
721
|
+
}}
|
|
722
|
+
>
|
|
723
|
+
<option value="">Select…</option>
|
|
724
|
+
{itemTargetChildren.map((child) => (
|
|
725
|
+
<option key={child.id} value={child.id}>
|
|
726
|
+
{child.path ?? child.label}
|
|
727
|
+
</option>
|
|
728
|
+
))}
|
|
729
|
+
</Select>
|
|
730
|
+
</WorkbenchPropertyRow>
|
|
731
|
+
</WorkbenchPropertyStack>
|
|
732
|
+
) : null}
|
|
733
|
+
<ul className="workbench-field-remap-detail__item-edges">
|
|
734
|
+
{(edge.itemEdges ?? []).map((itemEdge) => (
|
|
735
|
+
<li key={itemEdge.id} data-testid={`field-remap-item-edge-${itemEdge.id}`}>
|
|
736
|
+
<WorkbenchPropertyInline justify="between">
|
|
737
|
+
<code>
|
|
738
|
+
{fieldLabel(itemEdge.sourceFieldId, sources, targets)} →{' '}
|
|
739
|
+
{fieldLabel(itemEdge.targetSlotId, sources, targets)}
|
|
740
|
+
</code>
|
|
741
|
+
{!readOnly ? (
|
|
742
|
+
<Button
|
|
743
|
+
compact
|
|
744
|
+
type="button"
|
|
745
|
+
onClick={() => applyEdge(removeItemEdgeFromParent(edge, itemEdge.id))}
|
|
746
|
+
>
|
|
747
|
+
Remove
|
|
748
|
+
</Button>
|
|
749
|
+
) : null}
|
|
750
|
+
</WorkbenchPropertyInline>
|
|
751
|
+
</li>
|
|
752
|
+
))}
|
|
753
|
+
</ul>
|
|
754
|
+
</WorkbenchPropertyStack>
|
|
755
|
+
) : null}
|
|
756
|
+
</WorkbenchPropertySection>
|
|
757
|
+
) : null}
|
|
758
|
+
</WorkbenchPropertyStack>
|
|
658
759
|
</aside>
|
|
659
760
|
);
|
|
660
761
|
}
|