@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.
- package/package.json +33 -9
- package/src/index.ts +545 -43
- package/src/jdw/JdwPreview.tsx +38 -0
- package/src/jdw/JsonCodeEditorPane.tsx +274 -0
- package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +29 -0
- package/src/jdw/builtins/renderBuiltinWidgetNode.tsx +126 -0
- package/src/jdw/createBuiltinJdwRegistry.ts +111 -0
- package/src/jdw/cssRenderBackend.tsx +134 -0
- package/src/jdw/document.ts +38 -0
- package/src/jdw/fixtures/jdw-fixtures.ts +52 -0
- package/src/jdw/fixtures/jdw-sample-screens.ts +522 -0
- package/src/jdw/index.ts +45 -0
- package/src/jdw/parse.ts +1 -0
- package/src/jdw/renderJdw.tsx +32 -0
- package/src/json-config/JsonConfigWorkbench.tsx +316 -0
- package/src/json-config/index.ts +6 -0
- package/src/json-config/json-config-editor-state.ts +42 -0
- package/src/layout/Panel.tsx +146 -1
- package/src/layout/SideBarViewFrame.tsx +367 -328
- package/src/layout/WorkbenchCanvas.tsx +849 -0
- package/src/layout/WorkbenchFullscreen.tsx +368 -0
- package/src/layout/WorkbenchLayout.tsx +5 -0
- package/src/layout/WorkbenchLayoutBase.tsx +294 -0
- package/src/layout/WorkbenchPropertyPanel.tsx +497 -0
- package/src/layout/WorkbenchSidebarActions.tsx +274 -0
- package/src/layout/WorkbenchTree.tsx +351 -0
- package/src/layout/layoutHelpers.ts +25 -0
- package/src/modal/ConfirmDialog.tsx +61 -53
- package/src/modal/Modal.tsx +295 -35
- package/src/overlay/ContextMenu.tsx +146 -145
- package/src/primitives/AbsoluteBox.tsx +46 -0
- package/src/primitives/Badge.tsx +12 -12
- package/src/primitives/Button.tsx +44 -14
- package/src/primitives/Checkbox.tsx +11 -5
- package/src/primitives/EmptyState.tsx +27 -26
- package/src/primitives/Field.tsx +38 -38
- package/src/primitives/IconButton.tsx +5 -2
- package/src/primitives/List.tsx +111 -0
- package/src/primitives/NumberInput.tsx +38 -0
- package/src/primitives/ScrollArea.tsx +42 -0
- package/src/primitives/Select.tsx +1 -12
- package/src/primitives/StatusBar.tsx +46 -0
- package/src/primitives/TextArea.tsx +36 -0
- package/src/primitives/TextInput.tsx +11 -2
- package/src/primitives/WorkbenchChrome.tsx +424 -0
- package/src/primitives/WorkbenchEditor.tsx +394 -0
- package/src/primitives/index.ts +105 -0
- package/src/primitives/select/Select.tsx +266 -0
- package/src/primitives/select/index.ts +2 -0
- package/src/primitives/select/options.ts +38 -0
- package/src/primitives/select/overlay.ts +63 -0
- package/src/primitives/select/select.app.css +9 -0
- package/src/primitives/select/select.css +140 -0
- package/src/primitives/select/types.ts +18 -0
- package/src/primitives/styles.css +955 -0
- package/src/scrollbars.css +69 -0
- package/src/styles.css +7738 -1762
- package/src/utils/codicon.ts +17 -0
- package/src/widget-asset/WidgetAssetEditor.tsx +140 -0
- package/src/widget-asset/WidgetAssetMetadataForm.tsx +82 -0
- package/src/widget-asset/WidgetAssetModeControls.tsx +42 -0
- package/src/widget-asset/WidgetAssetSourceEditor.tsx +50 -0
- package/src/widget-asset/WidgetAssetWorkbench.tsx +112 -0
- package/src/widget-asset/index.ts +17 -0
- package/src/widget-asset/widget-asset-document.ts +37 -0
- package/src/widget-asset/widget-asset-mode.ts +3 -0
- package/src/widget-studio/builtin-widget-asset-catalog.ts +12 -0
- package/src/widget-studio/create-widget-studio-workspace-editor.tsx +79 -0
- package/src/widget-studio/index.ts +7 -0
- package/src/widget-studio/resolve-widget-studio-asset-catalog.ts +26 -0
- package/src/widget-tree/WidgetAssetPalette.tsx +77 -0
- package/src/widget-tree/WidgetInspectorPanel.tsx +266 -0
- package/src/widget-tree/WidgetSourceEditor.tsx +56 -0
- package/src/widget-tree/WidgetTreeLab.tsx +272 -0
- package/src/widget-tree/WidgetTreeModeControls.tsx +44 -0
- package/src/widget-tree/WidgetTreeSidePanel.tsx +49 -0
- package/src/widget-tree/WidgetTreeView.tsx +97 -0
- package/src/widget-tree/WidgetTreeWorkbench.tsx +111 -0
- package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +154 -0
- package/src/widget-tree/demo-registry.ts +32 -0
- package/src/widget-tree/demo-widget-assets.ts +4 -0
- package/src/widget-tree/index.ts +27 -0
- package/src/widget-tree/widget-tree-document.ts +20 -0
- package/src/widget-tree/widget-tree-layout.ts +31 -0
- package/src/widget-tree/widget-tree-mode.ts +7 -0
- package/src/widget-tree/widget-tree-play-helpers.ts +74 -0
- package/src/workbench/ActivityBar.tsx +52 -52
- package/src/workbench/ArtifactShell.tsx +397 -0
- package/src/workbench/CommandPalette.tsx +819 -0
- package/src/workbench/ConfirmationFlow.tsx +245 -0
- package/src/workbench/MarkdownPreview.tsx +151 -0
- package/src/workbench/ShortcutCommandBridge.tsx +368 -0
- package/src/workbench/SplitView.tsx +136 -136
- package/src/workbench/StatusBar.tsx +178 -123
- package/src/workbench/StructuredArtifactEditor.tsx +205 -0
- package/src/workbench/Timeline.tsx +302 -0
- package/src/workbench/WorkbenchCanvasShell.tsx +941 -0
- package/src/workbench/WorkbenchShell.tsx +74 -70
- package/src/workbench/WorkbenchStandaloneShell.tsx +318 -291
- package/src/workbench/auth/WorkbenchAuthGate.tsx +61 -0
- package/src/workbench/auth/WorkbenchLoginView.tsx +635 -0
- package/src/workbench/auth/index.ts +19 -0
- package/src/workbench/chat/ChatComposer.tsx +165 -148
- package/src/workbench/chat/ChatMessageItem.tsx +10 -1
- package/src/workbench/chat/index.ts +9 -0
- package/src/workbench/chat/slashCommand.ts +28 -0
- package/src/workbench/chat/useChatRuntimeState.ts +76 -0
- package/src/workbench/chat/useSlashCommandSuggest.ts +101 -0
- package/src/workbench/commands.ts +520 -490
- package/src/workbench/index.ts +443 -98
- package/src/workbench/keyboard.ts +53 -0
- package/src/workbench/schema/index.ts +48 -0
- package/src/workbench/schema/workbenchDocument.ts +31 -0
- package/src/workbench/schema/workbenchDocumentActions.ts +6 -0
- package/src/workbench/schema/workbenchDocumentAdapter.ts +9 -0
- package/src/workbench/schema/workbenchDocumentPatch.ts +13 -0
- package/src/workbench/schema/workbenchDocumentRenderer.tsx +702 -0
- package/src/workbench/settings/NavigationPanel.tsx +47 -0
- package/src/workbench/settings/SchemaForm.tsx +420 -0
- package/src/workbench/settings/SectionedPanel.tsx +415 -0
- package/src/workbench/settings/StructuredDataForm.tsx +539 -0
- package/src/workbench/settings/StructuredDataSchemaPanel.tsx +559 -0
- package/src/workbench/settings/StructuredDataSchemaPanelEmbed.tsx +21 -0
- package/src/workbench/settings/StructuredDataSchemaPanelFrame.tsx +76 -0
- package/src/workbench/settings/StructuredDataTableView.tsx +127 -0
- package/src/workbench/settings/WorkbenchPanelRegion.tsx +46 -0
- package/src/workbench/settings/WorkbenchSettingsModal.tsx +21 -12
- package/src/workbench/settings/WorkbenchSettingsNav.tsx +38 -22
- package/src/workbench/settings/index.ts +135 -0
- package/src/workbench/settings/panel-region.css +21 -0
- package/src/workbench/settings/sectionedPanelScrollSpy.ts +143 -0
- package/src/workbench/settings/structured-data-schema-panel.css +330 -0
- package/src/workbench/settings/structured-data-table-view.css +85 -0
- package/src/workbench/settings/structuredDataSchema.ts +886 -0
- package/src/workbench/shell.ts +15 -0
- package/src/workbench/shellState.ts +203 -203
- package/src/workbench/standalone.ts +101 -94
- package/src/workbench/status.ts +129 -0
- package/src/workbench/theme.ts +44 -0
- package/src/workbench/workspace/MultiProviderExplorer.tsx +625 -0
- package/src/workbench/workspace/WorkspaceDraftsContext.tsx +111 -0
- package/src/workbench/workspace/WorkspaceEditor.tsx +47 -12
- package/src/workbench/workspace/WorkspaceEditorPanel.tsx +334 -353
- package/src/workbench/workspace/WorkspaceExplorer.tsx +532 -524
- package/src/workbench/workspace/WorkspaceFileIcon.tsx +32 -2
- package/src/workbench/workspace/WorkspaceSearchPanel.tsx +113 -113
- package/src/workbench/workspace/WorkspaceSearchResults.tsx +47 -47
- package/src/workbench/workspace/index.ts +156 -98
- package/src/workbench/workspace/mimeType.ts +45 -0
- package/src/workbench/workspace/path.ts +10 -10
- package/src/workbench/workspace/search.ts +6 -6
- package/src/workbench/workspace/tree.ts +1 -1
- package/src/workbench/workspace/types.ts +10 -10
- package/src/workbench/workspace/useVirtualWorkspace.ts +111 -98
- package/src/workbench/workspace/workspaceJsonDiagnostics.ts +83 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { useMemo, useState, type ReactNode } from 'react';
|
|
2
|
+
import type { WidgetRegistryContract } from '@workbench-kit/contracts';
|
|
3
|
+
import { parseJsonWidgetData } from '@workbench-kit/jdw';
|
|
4
|
+
|
|
5
|
+
import { Panel, PanelBody, PanelHeader } from '../layout/Panel';
|
|
6
|
+
import { EmptyState } from '../primitives/EmptyState';
|
|
7
|
+
import { Button } from '../primitives/Button';
|
|
8
|
+
import { IconButton } from '../primitives/IconButton';
|
|
9
|
+
import { Toolbar } from '../primitives/Toolbar';
|
|
10
|
+
import { JsonCodeEditorPane, JsonConfigValidationBanner } from '../jdw/JsonCodeEditorPane.js';
|
|
11
|
+
import { JdwPreview } from '../jdw/JdwPreview.js';
|
|
12
|
+
import { SplitView } from '../workbench/SplitView';
|
|
13
|
+
import { WorkbenchStructuredDataSchemaPanel } from '../workbench/settings/StructuredDataSchemaPanel';
|
|
14
|
+
import { type WorkbenchStructuredDataSchemaDocument } from '../workbench/settings/StructuredDataForm';
|
|
15
|
+
import { type WorkspaceEditorTheme } from '../workbench/workspace/WorkspaceEditor';
|
|
16
|
+
import { type WorkspaceFile } from '../workbench/workspace/types';
|
|
17
|
+
import { createJsonConfigEditorState } from './json-config-editor-state.js';
|
|
18
|
+
|
|
19
|
+
export type JsonConfigPreviewKind = 'auto' | 'none' | 'schema' | 'widget';
|
|
20
|
+
export type JsonConfigWorkbenchMode = 'code' | 'form' | 'preview';
|
|
21
|
+
type JsonConfigWorkbenchModeInput = JsonConfigWorkbenchMode | 'split';
|
|
22
|
+
|
|
23
|
+
export interface JsonConfigWorkbenchProps {
|
|
24
|
+
activePattern?: string | undefined;
|
|
25
|
+
baselineValue?: string | undefined;
|
|
26
|
+
defaultMode?: JsonConfigWorkbenchModeInput | undefined;
|
|
27
|
+
emptyPreviewLabel?: ReactNode | undefined;
|
|
28
|
+
headerActions?: ReactNode | undefined;
|
|
29
|
+
mode?: JsonConfigWorkbenchModeInput | undefined;
|
|
30
|
+
onChange: (value: string) => void;
|
|
31
|
+
onApply?: (() => void) | undefined;
|
|
32
|
+
onDiscard?: (() => void) | undefined;
|
|
33
|
+
onModeChange?: ((mode: JsonConfigWorkbenchMode) => void) | undefined;
|
|
34
|
+
onSave?: (() => void) | undefined;
|
|
35
|
+
path?: string | undefined;
|
|
36
|
+
preferredTableColumns?: readonly string[] | undefined;
|
|
37
|
+
previewKind?: JsonConfigPreviewKind | undefined;
|
|
38
|
+
readOnly?: boolean | undefined;
|
|
39
|
+
schema?: WorkbenchStructuredDataSchemaDocument | null | undefined;
|
|
40
|
+
sectionValueAliases?: Record<string, readonly (string | number)[][]> | undefined;
|
|
41
|
+
theme?: WorkspaceEditorTheme | undefined;
|
|
42
|
+
title?: ReactNode | undefined;
|
|
43
|
+
titleFallback?: string | undefined;
|
|
44
|
+
value: string;
|
|
45
|
+
widgetRegistry?: WidgetRegistryContract<unknown> | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const defaultDeserialize = (content: string) => {
|
|
49
|
+
if (!content.trim()) return null;
|
|
50
|
+
return JSON.parse(content);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const defaultSerialize = (data: unknown) => JSON.stringify(data, null, 2);
|
|
54
|
+
|
|
55
|
+
export function resolveJsonConfigPreviewKind(
|
|
56
|
+
previewKind: JsonConfigPreviewKind,
|
|
57
|
+
_schema: WorkbenchStructuredDataSchemaDocument | null | undefined,
|
|
58
|
+
json: string,
|
|
59
|
+
): 'none' | 'widget' {
|
|
60
|
+
if (previewKind === 'none') return 'none';
|
|
61
|
+
if (previewKind === 'schema') return 'none';
|
|
62
|
+
if (previewKind === 'widget') return 'widget';
|
|
63
|
+
|
|
64
|
+
const parsed = parseJsonWidgetData(json);
|
|
65
|
+
if (parsed.value !== null) {
|
|
66
|
+
return 'widget';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 'none';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function JsonConfigWorkbench({
|
|
73
|
+
activePattern,
|
|
74
|
+
baselineValue,
|
|
75
|
+
defaultMode = 'form',
|
|
76
|
+
emptyPreviewLabel = 'No preview available for this document.',
|
|
77
|
+
headerActions,
|
|
78
|
+
mode,
|
|
79
|
+
onChange,
|
|
80
|
+
onApply,
|
|
81
|
+
onDiscard,
|
|
82
|
+
onModeChange,
|
|
83
|
+
onSave,
|
|
84
|
+
path = 'config.json',
|
|
85
|
+
preferredTableColumns,
|
|
86
|
+
previewKind = 'auto',
|
|
87
|
+
readOnly = false,
|
|
88
|
+
schema,
|
|
89
|
+
sectionValueAliases,
|
|
90
|
+
theme = 'dark',
|
|
91
|
+
title = 'Configuration',
|
|
92
|
+
titleFallback,
|
|
93
|
+
value,
|
|
94
|
+
widgetRegistry,
|
|
95
|
+
}: JsonConfigWorkbenchProps) {
|
|
96
|
+
const formAvailable = schema !== null && schema !== undefined;
|
|
97
|
+
const [uncontrolledMode, setUncontrolledMode] =
|
|
98
|
+
useState<JsonConfigWorkbenchModeInput>(defaultMode);
|
|
99
|
+
const resolvedMode = normalizeJsonConfigWorkbenchMode(mode ?? uncontrolledMode, formAvailable);
|
|
100
|
+
|
|
101
|
+
const setMode = (nextMode: JsonConfigWorkbenchMode) => {
|
|
102
|
+
if (mode === undefined) {
|
|
103
|
+
setUncontrolledMode(nextMode);
|
|
104
|
+
}
|
|
105
|
+
onModeChange?.(nextMode);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const dirty = baselineValue !== undefined && value !== baselineValue;
|
|
109
|
+
const editorState = useMemo(
|
|
110
|
+
() =>
|
|
111
|
+
createJsonConfigEditorState({
|
|
112
|
+
baselineValue: baselineValue ?? value,
|
|
113
|
+
currentValue: value,
|
|
114
|
+
}),
|
|
115
|
+
[baselineValue, value],
|
|
116
|
+
);
|
|
117
|
+
const resolvedPreviewKind = resolveJsonConfigPreviewKind(previewKind, schema, value);
|
|
118
|
+
|
|
119
|
+
const structuredData = useMemo(() => {
|
|
120
|
+
try {
|
|
121
|
+
return defaultDeserialize(value);
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}, [value]);
|
|
126
|
+
|
|
127
|
+
const editorFile = useMemo<WorkspaceFile>(
|
|
128
|
+
() => ({
|
|
129
|
+
content: value,
|
|
130
|
+
mimeType: 'application/json',
|
|
131
|
+
path,
|
|
132
|
+
}),
|
|
133
|
+
[path, value],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const handleDataChange = (nextData: unknown) => {
|
|
137
|
+
try {
|
|
138
|
+
onChange(defaultSerialize(nextData));
|
|
139
|
+
} catch {
|
|
140
|
+
// ignore serialization failures during active input
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const codePane = (
|
|
145
|
+
<JsonCodeEditorPane
|
|
146
|
+
file={editorFile}
|
|
147
|
+
readOnly={readOnly}
|
|
148
|
+
showProblemsPanel
|
|
149
|
+
theme={theme}
|
|
150
|
+
value={value}
|
|
151
|
+
onChange={onChange}
|
|
152
|
+
onSave={onSave}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const formPane = formAvailable ? (
|
|
157
|
+
<div className="ui-json-config-workbench__form">
|
|
158
|
+
<WorkbenchStructuredDataSchemaPanel
|
|
159
|
+
activePattern={activePattern}
|
|
160
|
+
ariaLabel="Configuration form"
|
|
161
|
+
data={structuredData}
|
|
162
|
+
fill
|
|
163
|
+
preferredTableColumns={preferredTableColumns}
|
|
164
|
+
readOnly={readOnly}
|
|
165
|
+
schema={schema}
|
|
166
|
+
sectionValueAliases={sectionValueAliases}
|
|
167
|
+
titleFallback={titleFallback ?? path}
|
|
168
|
+
onDataChange={handleDataChange}
|
|
169
|
+
/>
|
|
170
|
+
</div>
|
|
171
|
+
) : (
|
|
172
|
+
<EmptyState compact icon="codicon-settings">
|
|
173
|
+
No form is available for this document.
|
|
174
|
+
</EmptyState>
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const previewPane = (() => {
|
|
178
|
+
if (resolvedPreviewKind === 'widget') {
|
|
179
|
+
return (
|
|
180
|
+
<div className="ui-json-config-workbench__preview">
|
|
181
|
+
<JdwPreview json={value} registry={widgetRegistry} />
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<EmptyState compact icon="codicon-open-preview">
|
|
188
|
+
{emptyPreviewLabel}
|
|
189
|
+
</EmptyState>
|
|
190
|
+
);
|
|
191
|
+
})();
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<Panel className="ui-json-config-workbench" data-theme={theme} data-mode={resolvedMode}>
|
|
195
|
+
<PanelHeader
|
|
196
|
+
actions={
|
|
197
|
+
<Toolbar>
|
|
198
|
+
{editorState.canApply && onApply ? (
|
|
199
|
+
<Button variant="primary" onClick={onApply}>
|
|
200
|
+
Apply
|
|
201
|
+
</Button>
|
|
202
|
+
) : null}
|
|
203
|
+
{dirty && !readOnly ? (
|
|
204
|
+
<>
|
|
205
|
+
{onDiscard ? <Button onClick={onDiscard}>Discard</Button> : null}
|
|
206
|
+
{onSave ? (
|
|
207
|
+
<Button variant="primary" onClick={onSave}>
|
|
208
|
+
Save
|
|
209
|
+
</Button>
|
|
210
|
+
) : null}
|
|
211
|
+
</>
|
|
212
|
+
) : null}
|
|
213
|
+
{headerActions}
|
|
214
|
+
<JsonConfigModeControls
|
|
215
|
+
formAvailable={formAvailable}
|
|
216
|
+
mode={resolvedMode}
|
|
217
|
+
onModeChange={setMode}
|
|
218
|
+
/>
|
|
219
|
+
</Toolbar>
|
|
220
|
+
}
|
|
221
|
+
>
|
|
222
|
+
<span className="ui-json-config-workbench__title">
|
|
223
|
+
{title}
|
|
224
|
+
{dirty ? (
|
|
225
|
+
<span className="ui-json-config-workbench__dirty-indicator" title="Unsaved changes">
|
|
226
|
+
●
|
|
227
|
+
</span>
|
|
228
|
+
) : null}
|
|
229
|
+
</span>
|
|
230
|
+
</PanelHeader>
|
|
231
|
+
<PanelBody className="ui-json-config-workbench__body">
|
|
232
|
+
{!editorState.validationOk || editorState.canApply ? (
|
|
233
|
+
<JsonConfigValidationBanner
|
|
234
|
+
canApply={editorState.canApply}
|
|
235
|
+
firstError={editorState.firstError}
|
|
236
|
+
validationOk={editorState.validationOk}
|
|
237
|
+
/>
|
|
238
|
+
) : null}
|
|
239
|
+
{resolvedMode === 'preview' ? (
|
|
240
|
+
<section className="ui-json-config-workbench__pane" aria-label="Preview">
|
|
241
|
+
{previewPane}
|
|
242
|
+
</section>
|
|
243
|
+
) : (
|
|
244
|
+
<SplitView
|
|
245
|
+
className="ui-json-config-workbench__split"
|
|
246
|
+
defaultPrimarySizePercent={50}
|
|
247
|
+
minPrimarySizePercent={20}
|
|
248
|
+
primary={
|
|
249
|
+
<section
|
|
250
|
+
className="ui-json-config-workbench__pane"
|
|
251
|
+
aria-label={resolvedMode === 'form' ? 'Form' : 'Code JSON'}
|
|
252
|
+
>
|
|
253
|
+
{resolvedMode === 'form' ? formPane : codePane}
|
|
254
|
+
</section>
|
|
255
|
+
}
|
|
256
|
+
secondary={
|
|
257
|
+
<section className="ui-json-config-workbench__pane" aria-label="Preview">
|
|
258
|
+
{previewPane}
|
|
259
|
+
</section>
|
|
260
|
+
}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
</PanelBody>
|
|
264
|
+
</Panel>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function normalizeJsonConfigWorkbenchMode(
|
|
269
|
+
mode: JsonConfigWorkbenchModeInput,
|
|
270
|
+
formAvailable: boolean,
|
|
271
|
+
): JsonConfigWorkbenchMode {
|
|
272
|
+
const normalized = mode === 'split' ? 'code' : mode;
|
|
273
|
+
if (normalized === 'form' && !formAvailable) {
|
|
274
|
+
return 'code';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return normalized;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function JsonConfigModeControls({
|
|
281
|
+
formAvailable,
|
|
282
|
+
mode,
|
|
283
|
+
onModeChange,
|
|
284
|
+
}: {
|
|
285
|
+
formAvailable: boolean;
|
|
286
|
+
mode: JsonConfigWorkbenchMode;
|
|
287
|
+
onModeChange: (mode: JsonConfigWorkbenchMode) => void;
|
|
288
|
+
}) {
|
|
289
|
+
return (
|
|
290
|
+
<div className="ui-json-config-workbench__modes">
|
|
291
|
+
<IconButton
|
|
292
|
+
aria-pressed={mode === 'code'}
|
|
293
|
+
className="ui-json-config-workbench__mode"
|
|
294
|
+
icon="codicon-code"
|
|
295
|
+
label="Code (JSON)"
|
|
296
|
+
onClick={() => onModeChange('code')}
|
|
297
|
+
/>
|
|
298
|
+
{formAvailable ? (
|
|
299
|
+
<IconButton
|
|
300
|
+
aria-pressed={mode === 'form'}
|
|
301
|
+
className="ui-json-config-workbench__mode"
|
|
302
|
+
icon="codicon-settings"
|
|
303
|
+
label="Form"
|
|
304
|
+
onClick={() => onModeChange('form')}
|
|
305
|
+
/>
|
|
306
|
+
) : null}
|
|
307
|
+
<IconButton
|
|
308
|
+
aria-pressed={mode === 'preview'}
|
|
309
|
+
className="ui-json-config-workbench__mode"
|
|
310
|
+
icon="codicon-open-preview"
|
|
311
|
+
label="Preview"
|
|
312
|
+
onClick={() => onModeChange('preview')}
|
|
313
|
+
/>
|
|
314
|
+
</div>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type JsonConfigValidationState = 'invalid' | 'unchanged' | 'valid-changed';
|
|
2
|
+
|
|
3
|
+
export interface JsonConfigEditorState {
|
|
4
|
+
canApply: boolean;
|
|
5
|
+
firstError: string | null;
|
|
6
|
+
textDirty: boolean;
|
|
7
|
+
validationOk: boolean;
|
|
8
|
+
validationState: JsonConfigValidationState;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createJsonConfigEditorState(input: {
|
|
12
|
+
baselineValue: string;
|
|
13
|
+
currentValue: string;
|
|
14
|
+
}): JsonConfigEditorState {
|
|
15
|
+
const textDirty = input.currentValue !== input.baselineValue;
|
|
16
|
+
let validationOk = true;
|
|
17
|
+
let firstError: string | null = null;
|
|
18
|
+
|
|
19
|
+
if (input.currentValue.trim().length > 0) {
|
|
20
|
+
try {
|
|
21
|
+
JSON.parse(input.currentValue);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
validationOk = false;
|
|
24
|
+
firstError = error instanceof Error ? error.message : 'Invalid JSON';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const canApply = textDirty && validationOk;
|
|
29
|
+
const validationState: JsonConfigValidationState = !validationOk
|
|
30
|
+
? 'invalid'
|
|
31
|
+
: textDirty
|
|
32
|
+
? 'valid-changed'
|
|
33
|
+
: 'unchanged';
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
canApply,
|
|
37
|
+
firstError,
|
|
38
|
+
textDirty,
|
|
39
|
+
validationOk,
|
|
40
|
+
validationState,
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/layout/Panel.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentPropsWithRef, ReactNode } from 'react';
|
|
2
|
+
import { ScrollArea } from '../primitives/ScrollArea';
|
|
2
3
|
import { cx } from '../utils/cx';
|
|
3
4
|
|
|
4
5
|
export type PanelProps = ComponentPropsWithRef<'div'>;
|
|
@@ -23,5 +24,149 @@ export function PanelHeader({ actions, children, className, ...props }: PanelHea
|
|
|
23
24
|
export type PanelBodyProps = ComponentPropsWithRef<'div'>;
|
|
24
25
|
|
|
25
26
|
export function PanelBody({ className, ...props }: PanelBodyProps) {
|
|
26
|
-
return
|
|
27
|
+
return (
|
|
28
|
+
<ScrollArea
|
|
29
|
+
className={cx('panel-body', 'ui-panel-body', className)}
|
|
30
|
+
orientation="both"
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PanelFooterProps = ComponentPropsWithRef<'div'>;
|
|
37
|
+
|
|
38
|
+
export function PanelFooter({ className, ...props }: PanelFooterProps) {
|
|
39
|
+
return <div className={cx('ui-panel-footer', className)} {...props} />;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type FilterBarProps = ComponentPropsWithRef<'div'>;
|
|
43
|
+
|
|
44
|
+
export function FilterBar({ className, ...props }: FilterBarProps) {
|
|
45
|
+
return <div className={cx('ui-filter-bar', className)} {...props} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type FilterBarRowProps = ComponentPropsWithRef<'div'>;
|
|
49
|
+
|
|
50
|
+
export function FilterBarRow({ className, ...props }: FilterBarRowProps) {
|
|
51
|
+
return <div className={cx('ui-filter-bar__row', className)} {...props} />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface FilterChipProps extends Omit<ComponentPropsWithRef<'button'>, 'children'> {
|
|
55
|
+
count?: number | undefined;
|
|
56
|
+
label: ReactNode;
|
|
57
|
+
onDismiss?: (() => void) | undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function FilterChip({
|
|
61
|
+
className,
|
|
62
|
+
count,
|
|
63
|
+
label,
|
|
64
|
+
onDismiss,
|
|
65
|
+
type = 'button',
|
|
66
|
+
...props
|
|
67
|
+
}: FilterChipProps) {
|
|
68
|
+
return (
|
|
69
|
+
<button className={cx('ui-filter-chip', className)} type={type} {...props}>
|
|
70
|
+
<span className="ui-filter-chip__label">{label}</span>
|
|
71
|
+
{count !== undefined ? <span className="ui-filter-chip__count">{count}</span> : null}
|
|
72
|
+
{onDismiss ? (
|
|
73
|
+
<span
|
|
74
|
+
aria-hidden={true}
|
|
75
|
+
className="ui-filter-chip__dismiss"
|
|
76
|
+
onClick={(event) => {
|
|
77
|
+
event.stopPropagation();
|
|
78
|
+
onDismiss();
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
<i className="codicon codicon-close" />
|
|
82
|
+
</span>
|
|
83
|
+
) : null}
|
|
84
|
+
</button>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface FilterBarActiveChipsProps extends ComponentPropsWithRef<'div'> {
|
|
89
|
+
clearAllLabel?: string | undefined;
|
|
90
|
+
onClearAll?: (() => void) | undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function FilterBarActiveChips({
|
|
94
|
+
children,
|
|
95
|
+
className,
|
|
96
|
+
clearAllLabel = 'Clear all',
|
|
97
|
+
onClearAll,
|
|
98
|
+
...props
|
|
99
|
+
}: FilterBarActiveChipsProps) {
|
|
100
|
+
return (
|
|
101
|
+
<FilterBarRow className={cx('ui-filter-bar__active-chips', className)} {...props}>
|
|
102
|
+
<div className="ui-filter-bar__chip-list" role="list">
|
|
103
|
+
{children}
|
|
104
|
+
</div>
|
|
105
|
+
{onClearAll ? (
|
|
106
|
+
<button className="ui-filter-bar__clear-all" type="button" onClick={onClearAll}>
|
|
107
|
+
{clearAllLabel}
|
|
108
|
+
</button>
|
|
109
|
+
) : null}
|
|
110
|
+
</FilterBarRow>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface HelpTextProps extends ComponentPropsWithRef<'div'> {
|
|
115
|
+
tone?: 'error' | 'normal';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function HelpText({ className, tone = 'normal', ...props }: HelpTextProps) {
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
className={cx('ui-help-text', tone === 'error' && 'ui-help-text--error', className)}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type PreviewPaneProps = ComponentPropsWithRef<'div'>;
|
|
128
|
+
|
|
129
|
+
export function PreviewPane({ className, ...props }: PreviewPaneProps) {
|
|
130
|
+
return <div className={cx('ui-preview-pane', className)} {...props} />;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type PreviewPaneContentProps = ComponentPropsWithRef<'div'>;
|
|
134
|
+
|
|
135
|
+
export function PreviewPaneContent({ className, ...props }: PreviewPaneContentProps) {
|
|
136
|
+
return <div className={cx('ui-preview-pane__content', className)} {...props} />;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type PreviewPaneViewportProps = ComponentPropsWithRef<'div'>;
|
|
140
|
+
|
|
141
|
+
export function PreviewPaneViewport({ className, ...props }: PreviewPaneViewportProps) {
|
|
142
|
+
return <div className={cx('ui-preview-pane__viewport', className)} {...props} />;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export type PreviewPaneDetailsProps = ComponentPropsWithRef<'div'>;
|
|
146
|
+
|
|
147
|
+
export function PreviewPaneDetails({ className, ...props }: PreviewPaneDetailsProps) {
|
|
148
|
+
return <div className={cx('ui-preview-pane__details', className)} {...props} />;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type PreviewPaneTitleProps = ComponentPropsWithRef<'div'>;
|
|
152
|
+
|
|
153
|
+
export function PreviewPaneTitle({ className, ...props }: PreviewPaneTitleProps) {
|
|
154
|
+
return <div className={cx('ui-preview-pane__title', className)} {...props} />;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface PreviewPaneTextProps extends ComponentPropsWithRef<'div'> {
|
|
158
|
+
tone?: 'error' | 'normal';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function PreviewPaneText({ className, tone = 'normal', ...props }: PreviewPaneTextProps) {
|
|
162
|
+
return (
|
|
163
|
+
<div
|
|
164
|
+
className={cx(
|
|
165
|
+
'ui-preview-pane__text',
|
|
166
|
+
tone === 'error' && 'ui-preview-pane__text--error',
|
|
167
|
+
className,
|
|
168
|
+
)}
|
|
169
|
+
{...props}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
27
172
|
}
|