@workbench-kit/react 0.0.1-prototype.0 → 0.0.2-prototype.0.1.2

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.
Files changed (155) hide show
  1. package/package.json +33 -9
  2. package/src/index.ts +545 -43
  3. package/src/jdw/JdwPreview.tsx +38 -0
  4. package/src/jdw/JsonCodeEditorPane.tsx +274 -0
  5. package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +29 -0
  6. package/src/jdw/builtins/renderBuiltinWidgetNode.tsx +126 -0
  7. package/src/jdw/createBuiltinJdwRegistry.ts +111 -0
  8. package/src/jdw/cssRenderBackend.tsx +134 -0
  9. package/src/jdw/document.ts +38 -0
  10. package/src/jdw/fixtures/jdw-fixtures.ts +52 -0
  11. package/src/jdw/fixtures/jdw-sample-screens.ts +522 -0
  12. package/src/jdw/index.ts +45 -0
  13. package/src/jdw/parse.ts +1 -0
  14. package/src/jdw/renderJdw.tsx +32 -0
  15. package/src/json-config/JsonConfigWorkbench.tsx +316 -0
  16. package/src/json-config/index.ts +6 -0
  17. package/src/json-config/json-config-editor-state.ts +42 -0
  18. package/src/layout/Panel.tsx +146 -1
  19. package/src/layout/SideBarViewFrame.tsx +367 -328
  20. package/src/layout/WorkbenchCanvas.tsx +849 -0
  21. package/src/layout/WorkbenchFullscreen.tsx +368 -0
  22. package/src/layout/WorkbenchLayout.tsx +5 -0
  23. package/src/layout/WorkbenchLayoutBase.tsx +294 -0
  24. package/src/layout/WorkbenchPropertyPanel.tsx +497 -0
  25. package/src/layout/WorkbenchSidebarActions.tsx +274 -0
  26. package/src/layout/WorkbenchTree.tsx +351 -0
  27. package/src/layout/layoutHelpers.ts +25 -0
  28. package/src/modal/ConfirmDialog.tsx +61 -53
  29. package/src/modal/Modal.tsx +295 -35
  30. package/src/overlay/ContextMenu.tsx +146 -145
  31. package/src/primitives/AbsoluteBox.tsx +46 -0
  32. package/src/primitives/Badge.tsx +12 -12
  33. package/src/primitives/Button.tsx +44 -14
  34. package/src/primitives/Checkbox.tsx +11 -5
  35. package/src/primitives/EmptyState.tsx +27 -26
  36. package/src/primitives/Field.tsx +38 -38
  37. package/src/primitives/IconButton.tsx +5 -2
  38. package/src/primitives/List.tsx +111 -0
  39. package/src/primitives/NumberInput.tsx +38 -0
  40. package/src/primitives/ScrollArea.tsx +42 -0
  41. package/src/primitives/Select.tsx +1 -12
  42. package/src/primitives/StatusBar.tsx +46 -0
  43. package/src/primitives/TextArea.tsx +36 -0
  44. package/src/primitives/TextInput.tsx +11 -2
  45. package/src/primitives/WorkbenchChrome.tsx +424 -0
  46. package/src/primitives/WorkbenchEditor.tsx +394 -0
  47. package/src/primitives/index.ts +105 -0
  48. package/src/primitives/select/Select.tsx +266 -0
  49. package/src/primitives/select/index.ts +2 -0
  50. package/src/primitives/select/options.ts +38 -0
  51. package/src/primitives/select/overlay.ts +63 -0
  52. package/src/primitives/select/select.app.css +9 -0
  53. package/src/primitives/select/select.css +140 -0
  54. package/src/primitives/select/types.ts +18 -0
  55. package/src/primitives/styles.css +955 -0
  56. package/src/scrollbars.css +69 -0
  57. package/src/styles.css +7738 -1762
  58. package/src/utils/codicon.ts +17 -0
  59. package/src/widget-asset/WidgetAssetEditor.tsx +140 -0
  60. package/src/widget-asset/WidgetAssetMetadataForm.tsx +82 -0
  61. package/src/widget-asset/WidgetAssetModeControls.tsx +42 -0
  62. package/src/widget-asset/WidgetAssetSourceEditor.tsx +50 -0
  63. package/src/widget-asset/WidgetAssetWorkbench.tsx +112 -0
  64. package/src/widget-asset/index.ts +17 -0
  65. package/src/widget-asset/widget-asset-document.ts +37 -0
  66. package/src/widget-asset/widget-asset-mode.ts +3 -0
  67. package/src/widget-studio/builtin-widget-asset-catalog.ts +12 -0
  68. package/src/widget-studio/create-widget-studio-workspace-editor.tsx +79 -0
  69. package/src/widget-studio/index.ts +7 -0
  70. package/src/widget-studio/resolve-widget-studio-asset-catalog.ts +26 -0
  71. package/src/widget-tree/WidgetAssetPalette.tsx +77 -0
  72. package/src/widget-tree/WidgetInspectorPanel.tsx +266 -0
  73. package/src/widget-tree/WidgetSourceEditor.tsx +56 -0
  74. package/src/widget-tree/WidgetTreeLab.tsx +272 -0
  75. package/src/widget-tree/WidgetTreeModeControls.tsx +44 -0
  76. package/src/widget-tree/WidgetTreeSidePanel.tsx +49 -0
  77. package/src/widget-tree/WidgetTreeView.tsx +97 -0
  78. package/src/widget-tree/WidgetTreeWorkbench.tsx +111 -0
  79. package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +154 -0
  80. package/src/widget-tree/demo-registry.ts +32 -0
  81. package/src/widget-tree/demo-widget-assets.ts +4 -0
  82. package/src/widget-tree/index.ts +27 -0
  83. package/src/widget-tree/widget-tree-document.ts +20 -0
  84. package/src/widget-tree/widget-tree-layout.ts +31 -0
  85. package/src/widget-tree/widget-tree-mode.ts +7 -0
  86. package/src/widget-tree/widget-tree-play-helpers.ts +74 -0
  87. package/src/workbench/ActivityBar.tsx +52 -52
  88. package/src/workbench/ArtifactShell.tsx +397 -0
  89. package/src/workbench/CommandPalette.tsx +819 -0
  90. package/src/workbench/ConfirmationFlow.tsx +245 -0
  91. package/src/workbench/MarkdownPreview.tsx +151 -0
  92. package/src/workbench/ShortcutCommandBridge.tsx +368 -0
  93. package/src/workbench/SplitView.tsx +136 -136
  94. package/src/workbench/StatusBar.tsx +178 -123
  95. package/src/workbench/StructuredArtifactEditor.tsx +205 -0
  96. package/src/workbench/Timeline.tsx +302 -0
  97. package/src/workbench/WorkbenchCanvasShell.tsx +941 -0
  98. package/src/workbench/WorkbenchShell.tsx +74 -70
  99. package/src/workbench/WorkbenchStandaloneShell.tsx +318 -291
  100. package/src/workbench/auth/WorkbenchAuthGate.tsx +61 -0
  101. package/src/workbench/auth/WorkbenchLoginView.tsx +635 -0
  102. package/src/workbench/auth/index.ts +19 -0
  103. package/src/workbench/chat/ChatComposer.tsx +165 -148
  104. package/src/workbench/chat/ChatMessageItem.tsx +10 -1
  105. package/src/workbench/chat/index.ts +9 -0
  106. package/src/workbench/chat/slashCommand.ts +28 -0
  107. package/src/workbench/chat/useChatRuntimeState.ts +76 -0
  108. package/src/workbench/chat/useSlashCommandSuggest.ts +101 -0
  109. package/src/workbench/commands.ts +520 -490
  110. package/src/workbench/index.ts +443 -98
  111. package/src/workbench/keyboard.ts +53 -0
  112. package/src/workbench/schema/index.ts +48 -0
  113. package/src/workbench/schema/workbenchDocument.ts +31 -0
  114. package/src/workbench/schema/workbenchDocumentActions.ts +6 -0
  115. package/src/workbench/schema/workbenchDocumentAdapter.ts +9 -0
  116. package/src/workbench/schema/workbenchDocumentPatch.ts +13 -0
  117. package/src/workbench/schema/workbenchDocumentRenderer.tsx +702 -0
  118. package/src/workbench/settings/NavigationPanel.tsx +47 -0
  119. package/src/workbench/settings/SchemaForm.tsx +420 -0
  120. package/src/workbench/settings/SectionedPanel.tsx +415 -0
  121. package/src/workbench/settings/StructuredDataForm.tsx +539 -0
  122. package/src/workbench/settings/StructuredDataSchemaPanel.tsx +559 -0
  123. package/src/workbench/settings/StructuredDataSchemaPanelEmbed.tsx +21 -0
  124. package/src/workbench/settings/StructuredDataSchemaPanelFrame.tsx +76 -0
  125. package/src/workbench/settings/StructuredDataTableView.tsx +127 -0
  126. package/src/workbench/settings/WorkbenchPanelRegion.tsx +46 -0
  127. package/src/workbench/settings/WorkbenchSettingsModal.tsx +21 -12
  128. package/src/workbench/settings/WorkbenchSettingsNav.tsx +38 -22
  129. package/src/workbench/settings/index.ts +135 -0
  130. package/src/workbench/settings/panel-region.css +21 -0
  131. package/src/workbench/settings/sectionedPanelScrollSpy.ts +143 -0
  132. package/src/workbench/settings/structured-data-schema-panel.css +330 -0
  133. package/src/workbench/settings/structured-data-table-view.css +85 -0
  134. package/src/workbench/settings/structuredDataSchema.ts +886 -0
  135. package/src/workbench/shell.ts +15 -0
  136. package/src/workbench/shellState.ts +203 -203
  137. package/src/workbench/standalone.ts +101 -94
  138. package/src/workbench/status.ts +129 -0
  139. package/src/workbench/theme.ts +44 -0
  140. package/src/workbench/workspace/MultiProviderExplorer.tsx +625 -0
  141. package/src/workbench/workspace/WorkspaceDraftsContext.tsx +111 -0
  142. package/src/workbench/workspace/WorkspaceEditor.tsx +47 -12
  143. package/src/workbench/workspace/WorkspaceEditorPanel.tsx +334 -353
  144. package/src/workbench/workspace/WorkspaceExplorer.tsx +532 -524
  145. package/src/workbench/workspace/WorkspaceFileIcon.tsx +32 -2
  146. package/src/workbench/workspace/WorkspaceSearchPanel.tsx +113 -113
  147. package/src/workbench/workspace/WorkspaceSearchResults.tsx +47 -47
  148. package/src/workbench/workspace/index.ts +156 -98
  149. package/src/workbench/workspace/mimeType.ts +45 -0
  150. package/src/workbench/workspace/path.ts +10 -10
  151. package/src/workbench/workspace/search.ts +6 -6
  152. package/src/workbench/workspace/tree.ts +1 -1
  153. package/src/workbench/workspace/types.ts +10 -10
  154. package/src/workbench/workspace/useVirtualWorkspace.ts +111 -98
  155. package/src/workbench/workspace/workspaceJsonDiagnostics.ts +83 -0
@@ -0,0 +1,47 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { cx } from '../../utils/cx';
3
+
4
+ export interface WorkbenchNavigationPanelProps extends Omit<
5
+ ComponentPropsWithRef<'div'>,
6
+ 'children' | 'content'
7
+ > {
8
+ content: ReactNode;
9
+ contentClassName?: string | undefined;
10
+ contentProps?: Omit<ComponentPropsWithRef<'div'>, 'children' | 'className'> | undefined;
11
+ nav?: ReactNode | undefined;
12
+ navClassName?: string | undefined;
13
+ navProps?: Omit<ComponentPropsWithRef<'nav'>, 'children' | 'className'> | undefined;
14
+ }
15
+
16
+ export function WorkbenchNavigationPanel({
17
+ className,
18
+ content,
19
+ contentClassName,
20
+ contentProps,
21
+ nav,
22
+ navClassName,
23
+ navProps,
24
+ ...props
25
+ }: WorkbenchNavigationPanelProps) {
26
+ const hasNav = nav !== undefined && nav !== null && nav !== false;
27
+
28
+ return (
29
+ <div
30
+ className={cx('ui-workbench-navigation-panel', className)}
31
+ data-has-nav={hasNav ? 'true' : undefined}
32
+ {...props}
33
+ >
34
+ {hasNav ? (
35
+ <nav className={cx('ui-workbench-navigation-panel__nav', navClassName)} {...navProps}>
36
+ {nav}
37
+ </nav>
38
+ ) : null}
39
+ <div
40
+ className={cx('ui-workbench-navigation-panel__content', contentClassName)}
41
+ {...contentProps}
42
+ >
43
+ {content}
44
+ </div>
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,420 @@
1
+ import {
2
+ useId,
3
+ useMemo,
4
+ useState,
5
+ type ComponentPropsWithRef,
6
+ type FormEvent,
7
+ type ReactNode,
8
+ } from 'react';
9
+ import { Button } from '../../primitives/Button';
10
+ import { Checkbox } from '../../primitives/Checkbox';
11
+ import { EmptyState } from '../../primitives/EmptyState';
12
+ import { Field } from '../../primitives/Field';
13
+ import { Select } from '../../primitives/Select';
14
+ import { TextInput } from '../../primitives/TextInput';
15
+ import { cx } from '../../utils/cx';
16
+
17
+ export type WorkbenchSchemaFormFieldType = 'checkbox' | 'number' | 'select' | 'text';
18
+
19
+ export type WorkbenchSchemaFormFieldValue = boolean | number | string;
20
+
21
+ export type WorkbenchSchemaFormValues = Record<string, WorkbenchSchemaFormFieldValue>;
22
+
23
+ export interface WorkbenchSchemaFormOption {
24
+ disabled?: boolean;
25
+ label: ReactNode;
26
+ value: string;
27
+ }
28
+
29
+ export interface WorkbenchSchemaFormFieldBase {
30
+ defaultValue?: WorkbenchSchemaFormFieldValue;
31
+ description?: ReactNode;
32
+ disabled?: boolean;
33
+ id: string;
34
+ label: ReactNode;
35
+ metadata?: Record<string, unknown>;
36
+ readOnly?: boolean;
37
+ required?: boolean;
38
+ validationMessage?: ReactNode;
39
+ validate?: (
40
+ value: WorkbenchSchemaFormFieldValue,
41
+ values: WorkbenchSchemaFormValues,
42
+ field: WorkbenchSchemaFormField,
43
+ ) => ReactNode | undefined;
44
+ }
45
+
46
+ export interface WorkbenchSchemaFormTextField extends WorkbenchSchemaFormFieldBase {
47
+ autocomplete?: string;
48
+ monospace?: boolean;
49
+ placeholder?: string;
50
+ type: 'text';
51
+ }
52
+
53
+ export interface WorkbenchSchemaFormNumberField extends WorkbenchSchemaFormFieldBase {
54
+ max?: number;
55
+ min?: number;
56
+ placeholder?: string;
57
+ step?: number;
58
+ type: 'number';
59
+ }
60
+
61
+ export interface WorkbenchSchemaFormSelectField extends WorkbenchSchemaFormFieldBase {
62
+ emptyOptionLabel?: ReactNode;
63
+ options: readonly WorkbenchSchemaFormOption[];
64
+ type: 'select';
65
+ }
66
+
67
+ export interface WorkbenchSchemaFormCheckboxField extends WorkbenchSchemaFormFieldBase {
68
+ type: 'checkbox';
69
+ }
70
+
71
+ export type WorkbenchSchemaFormField =
72
+ | WorkbenchSchemaFormCheckboxField
73
+ | WorkbenchSchemaFormNumberField
74
+ | WorkbenchSchemaFormSelectField
75
+ | WorkbenchSchemaFormTextField;
76
+
77
+ export type WorkbenchSchemaFormErrors = Record<string, ReactNode>;
78
+
79
+ export interface WorkbenchSchemaFormFieldChangeContext {
80
+ field: WorkbenchSchemaFormField;
81
+ fieldId: string;
82
+ value: WorkbenchSchemaFormFieldValue;
83
+ values: WorkbenchSchemaFormValues;
84
+ }
85
+
86
+ export interface WorkbenchSchemaFormSubmitContext {
87
+ event: FormEvent<HTMLFormElement>;
88
+ values: WorkbenchSchemaFormValues;
89
+ }
90
+
91
+ export interface WorkbenchSchemaFormCancelContext {
92
+ values: WorkbenchSchemaFormValues;
93
+ }
94
+
95
+ export interface WorkbenchSchemaFormProps extends Omit<
96
+ ComponentPropsWithRef<'form'>,
97
+ 'children' | 'defaultValue' | 'onCancel' | 'onChange' | 'onSubmit'
98
+ > {
99
+ cancelLabel?: ReactNode;
100
+ defaultValues?: WorkbenchSchemaFormValues;
101
+ disabled?: boolean;
102
+ emptyLabel?: ReactNode;
103
+ errors?: WorkbenchSchemaFormErrors;
104
+ fields: readonly WorkbenchSchemaFormField[];
105
+ onCancel?: (context: WorkbenchSchemaFormCancelContext) => void;
106
+ onFieldChange?: (context: WorkbenchSchemaFormFieldChangeContext) => void;
107
+ onSubmit?: (values: WorkbenchSchemaFormValues, context: WorkbenchSchemaFormSubmitContext) => void;
108
+ onValuesChange?: (
109
+ values: WorkbenchSchemaFormValues,
110
+ context: WorkbenchSchemaFormFieldChangeContext,
111
+ ) => void;
112
+ readOnly?: boolean;
113
+ showActions?: boolean;
114
+ submitLabel?: ReactNode;
115
+ values?: WorkbenchSchemaFormValues;
116
+ }
117
+
118
+ export function getWorkbenchSchemaFormFieldDefaultValue(
119
+ field: WorkbenchSchemaFormField,
120
+ ): WorkbenchSchemaFormFieldValue {
121
+ if (field.defaultValue !== undefined) {
122
+ return coerceWorkbenchSchemaFormFieldValue(field, field.defaultValue);
123
+ }
124
+
125
+ if (field.type === 'checkbox') return false;
126
+ return '';
127
+ }
128
+
129
+ export function coerceWorkbenchSchemaFormFieldValue(
130
+ field: WorkbenchSchemaFormField,
131
+ value: unknown,
132
+ ): WorkbenchSchemaFormFieldValue {
133
+ if (field.type === 'checkbox') return Boolean(value);
134
+ if (field.type === 'number') {
135
+ if (value === '' || value === null || value === undefined) return '';
136
+ const numericValue = Number(value);
137
+ return Number.isFinite(numericValue) ? numericValue : '';
138
+ }
139
+
140
+ return value === null || value === undefined ? '' : String(value);
141
+ }
142
+
143
+ export function normalizeWorkbenchSchemaFormValues(
144
+ fields: readonly WorkbenchSchemaFormField[],
145
+ values: Partial<WorkbenchSchemaFormValues> = {},
146
+ ): WorkbenchSchemaFormValues {
147
+ return fields.reduce<WorkbenchSchemaFormValues>((normalizedValues, field) => {
148
+ const value =
149
+ values[field.id] === undefined
150
+ ? getWorkbenchSchemaFormFieldDefaultValue(field)
151
+ : values[field.id];
152
+
153
+ normalizedValues[field.id] = coerceWorkbenchSchemaFormFieldValue(field, value);
154
+ return normalizedValues;
155
+ }, {});
156
+ }
157
+
158
+ export function getWorkbenchSchemaFormFieldError({
159
+ field,
160
+ value,
161
+ values,
162
+ }: {
163
+ field: WorkbenchSchemaFormField;
164
+ value: WorkbenchSchemaFormFieldValue;
165
+ values: WorkbenchSchemaFormValues;
166
+ }) {
167
+ if (field.validationMessage) return field.validationMessage;
168
+ if (field.required && isWorkbenchSchemaFormEmptyValue(value)) {
169
+ return 'This field is required.';
170
+ }
171
+
172
+ return field.validate?.(value, values, field);
173
+ }
174
+
175
+ export function getWorkbenchSchemaFormErrors(
176
+ fields: readonly WorkbenchSchemaFormField[],
177
+ values: Partial<WorkbenchSchemaFormValues> = {},
178
+ ): WorkbenchSchemaFormErrors {
179
+ const normalizedValues = normalizeWorkbenchSchemaFormValues(fields, values);
180
+
181
+ return fields.reduce<WorkbenchSchemaFormErrors>((errors, field) => {
182
+ const error = getWorkbenchSchemaFormFieldError({
183
+ field,
184
+ value: normalizedValues[field.id],
185
+ values: normalizedValues,
186
+ });
187
+
188
+ if (isRenderableWorkbenchSchemaFormError(error)) {
189
+ errors[field.id] = error;
190
+ }
191
+
192
+ return errors;
193
+ }, {});
194
+ }
195
+
196
+ export function isWorkbenchSchemaFormSubmittable({
197
+ disabled = false,
198
+ errors = {},
199
+ readOnly = false,
200
+ }: {
201
+ disabled?: boolean;
202
+ errors?: WorkbenchSchemaFormErrors;
203
+ readOnly?: boolean;
204
+ }) {
205
+ return !disabled && !readOnly && Object.keys(errors).length === 0;
206
+ }
207
+
208
+ function isWorkbenchSchemaFormEmptyValue(value: WorkbenchSchemaFormFieldValue) {
209
+ return value === '' || value === false;
210
+ }
211
+
212
+ function isRenderableWorkbenchSchemaFormError(error: ReactNode) {
213
+ return error !== undefined && error !== null && error !== false && error !== '';
214
+ }
215
+
216
+ export function WorkbenchSchemaForm({
217
+ cancelLabel = 'Cancel',
218
+ className,
219
+ defaultValues = {},
220
+ disabled = false,
221
+ emptyLabel = 'No settings fields',
222
+ errors,
223
+ fields,
224
+ onCancel,
225
+ onFieldChange,
226
+ onSubmit,
227
+ onValuesChange,
228
+ readOnly = false,
229
+ showActions = true,
230
+ submitLabel = 'Save',
231
+ values,
232
+ ...props
233
+ }: WorkbenchSchemaFormProps) {
234
+ const generatedId = useId().replace(/:/g, '');
235
+ const [uncontrolledValues, setUncontrolledValues] = useState(() =>
236
+ normalizeWorkbenchSchemaFormValues(fields, defaultValues),
237
+ );
238
+ const resolvedValues = useMemo(
239
+ () => normalizeWorkbenchSchemaFormValues(fields, values ?? uncontrolledValues),
240
+ [fields, uncontrolledValues, values],
241
+ );
242
+ const resolvedErrors = useMemo(
243
+ () => ({
244
+ ...getWorkbenchSchemaFormErrors(fields, resolvedValues),
245
+ ...(errors ?? {}),
246
+ }),
247
+ [errors, fields, resolvedValues],
248
+ );
249
+ const submittable = isWorkbenchSchemaFormSubmittable({
250
+ disabled,
251
+ errors: resolvedErrors,
252
+ readOnly,
253
+ });
254
+
255
+ const updateFieldValue = (
256
+ field: WorkbenchSchemaFormField,
257
+ value: WorkbenchSchemaFormFieldValue,
258
+ ) => {
259
+ const nextValues = {
260
+ ...resolvedValues,
261
+ [field.id]: coerceWorkbenchSchemaFormFieldValue(field, value),
262
+ };
263
+ const context: WorkbenchSchemaFormFieldChangeContext = {
264
+ field,
265
+ fieldId: field.id,
266
+ value: nextValues[field.id],
267
+ values: nextValues,
268
+ };
269
+
270
+ if (values === undefined) {
271
+ setUncontrolledValues(nextValues);
272
+ }
273
+
274
+ onFieldChange?.(context);
275
+ onValuesChange?.(nextValues, context);
276
+ };
277
+
278
+ const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
279
+ event.preventDefault();
280
+ if (!submittable) return;
281
+
282
+ onSubmit?.(resolvedValues, { event, values: resolvedValues });
283
+ };
284
+
285
+ return (
286
+ <form
287
+ className={cx('ui-workbench-schema-form', className)}
288
+ data-readonly={readOnly ? 'true' : undefined}
289
+ onSubmit={handleSubmit}
290
+ {...props}
291
+ >
292
+ {fields.length === 0 ? (
293
+ <EmptyState compact icon="codicon-settings-gear">
294
+ {emptyLabel}
295
+ </EmptyState>
296
+ ) : (
297
+ <div className="ui-workbench-schema-form__fields">
298
+ {fields.map((field) => {
299
+ const fieldInputId = `${generatedId}-${field.id}`;
300
+ const error = resolvedErrors[field.id];
301
+ const errorId = isRenderableWorkbenchSchemaFormError(error)
302
+ ? `${fieldInputId}-error`
303
+ : undefined;
304
+ const fieldDisabled = disabled || Boolean(field.disabled);
305
+ const fieldReadOnly = readOnly || Boolean(field.readOnly);
306
+
307
+ return (
308
+ <div
309
+ key={field.id}
310
+ className="ui-workbench-schema-form__field"
311
+ data-field-id={field.id}
312
+ data-field-type={field.type}
313
+ >
314
+ {renderWorkbenchSchemaFormField({
315
+ disabled: fieldDisabled,
316
+ errorId,
317
+ field,
318
+ id: fieldInputId,
319
+ readOnly: fieldReadOnly,
320
+ value: resolvedValues[field.id],
321
+ onChange: (value) => updateFieldValue(field, value),
322
+ })}
323
+ {isRenderableWorkbenchSchemaFormError(error) ? (
324
+ <div id={errorId} className="ui-workbench-schema-form__error" role="alert">
325
+ {error}
326
+ </div>
327
+ ) : null}
328
+ </div>
329
+ );
330
+ })}
331
+ </div>
332
+ )}
333
+ {showActions ? (
334
+ <div className="ui-workbench-schema-form__actions">
335
+ <Button disabled={disabled} onClick={() => onCancel?.({ values: resolvedValues })}>
336
+ {cancelLabel}
337
+ </Button>
338
+ <Button disabled={!submittable} type="submit" variant="primary">
339
+ {submitLabel}
340
+ </Button>
341
+ </div>
342
+ ) : null}
343
+ </form>
344
+ );
345
+ }
346
+
347
+ function renderWorkbenchSchemaFormField({
348
+ disabled,
349
+ errorId,
350
+ field,
351
+ id,
352
+ onChange,
353
+ readOnly,
354
+ value,
355
+ }: {
356
+ disabled: boolean;
357
+ errorId?: string;
358
+ field: WorkbenchSchemaFormField;
359
+ id: string;
360
+ onChange: (value: WorkbenchSchemaFormFieldValue) => void;
361
+ readOnly: boolean;
362
+ value: WorkbenchSchemaFormFieldValue;
363
+ }) {
364
+ if (field.type === 'checkbox') {
365
+ return (
366
+ <Field description={field.description}>
367
+ <Checkbox
368
+ aria-describedby={errorId}
369
+ checked={Boolean(value)}
370
+ disabled={disabled || readOnly}
371
+ label={field.label}
372
+ onChange={(event) => onChange(event.currentTarget.checked)}
373
+ />
374
+ </Field>
375
+ );
376
+ }
377
+
378
+ if (field.type === 'select') {
379
+ return (
380
+ <Field description={field.description} htmlFor={id} label={field.label}>
381
+ <Select
382
+ id={id}
383
+ aria-describedby={errorId}
384
+ controlWidth="full"
385
+ disabled={disabled || readOnly}
386
+ value={String(value)}
387
+ onChange={(event) => onChange(event.currentTarget.value)}
388
+ >
389
+ {field.emptyOptionLabel ? <option value="">{field.emptyOptionLabel}</option> : null}
390
+ {field.options.map((option) => (
391
+ <option key={option.value} disabled={option.disabled} value={option.value}>
392
+ {option.label}
393
+ </option>
394
+ ))}
395
+ </Select>
396
+ </Field>
397
+ );
398
+ }
399
+
400
+ return (
401
+ <Field description={field.description} htmlFor={id} label={field.label}>
402
+ <TextInput
403
+ id={id}
404
+ aria-describedby={errorId}
405
+ autoComplete={field.type === 'text' ? field.autocomplete : undefined}
406
+ controlWidth="full"
407
+ max={field.type === 'number' ? field.max : undefined}
408
+ min={field.type === 'number' ? field.min : undefined}
409
+ monospace={field.type === 'text' ? field.monospace : false}
410
+ placeholder={field.placeholder}
411
+ readOnly={readOnly}
412
+ step={field.type === 'number' ? field.step : undefined}
413
+ type={field.type}
414
+ value={String(value)}
415
+ disabled={disabled}
416
+ onChange={(event) => onChange(event.currentTarget.value)}
417
+ />
418
+ </Field>
419
+ );
420
+ }