@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,274 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { forwardRef, useId, useState } from 'react';
3
+ import { Badge } from '../primitives/Badge';
4
+ import { cxCodicon } from '../utils/codicon';
5
+ import { cx } from '../utils/cx';
6
+ import {
7
+ getWorkbenchStatusLabel,
8
+ isWorkbenchStatusBusy,
9
+ isWorkbenchStatusDisabled,
10
+ type WorkbenchStatus,
11
+ } from '../workbench/status';
12
+ import {
13
+ SideBarList,
14
+ SideBarListItem,
15
+ type SideBarListItemProps,
16
+ type SideBarListProps,
17
+ } from './SideBarViewFrame';
18
+
19
+ export type WorkbenchActionStatus = WorkbenchStatus;
20
+
21
+ export interface WorkbenchActionItem {
22
+ danger?: boolean;
23
+ description?: string;
24
+ disabledReason?: string;
25
+ icon?: ReactNode;
26
+ id: string;
27
+ label: string;
28
+ metadata?: Record<string, unknown>;
29
+ shortcut?: string;
30
+ status?: WorkbenchActionStatus;
31
+ }
32
+
33
+ type WorkbenchSidebarHeadingLevel = 2 | 3 | 4 | 5 | 6;
34
+
35
+ export interface WorkbenchSidebarSectionProps extends Omit<
36
+ ComponentPropsWithRef<'section'>,
37
+ 'title'
38
+ > {
39
+ actions?: ReactNode;
40
+ badge?: ReactNode;
41
+ children?: ReactNode;
42
+ collapsible?: boolean;
43
+ collapsed?: boolean;
44
+ count?: number;
45
+ defaultCollapsed?: boolean;
46
+ headingLevel?: WorkbenchSidebarHeadingLevel;
47
+ onCollapsedChange?: (collapsed: boolean) => void;
48
+ title: ReactNode;
49
+ }
50
+
51
+ export function WorkbenchSidebarSection({
52
+ actions,
53
+ badge,
54
+ children,
55
+ className,
56
+ collapsible = true,
57
+ collapsed,
58
+ count,
59
+ defaultCollapsed = false,
60
+ headingLevel = 3,
61
+ id,
62
+ onCollapsedChange,
63
+ title,
64
+ ...props
65
+ }: WorkbenchSidebarSectionProps) {
66
+ const generatedId = useId();
67
+ const sectionId = id ?? generatedId;
68
+ const headingId = `${sectionId}-heading`;
69
+ const contentId = `${sectionId}-content`;
70
+ const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState(defaultCollapsed);
71
+ const resolvedCollapsed = collapsed ?? uncontrolledCollapsed;
72
+
73
+ const handleToggle = () => {
74
+ if (!collapsible) return;
75
+
76
+ const nextCollapsed = !resolvedCollapsed;
77
+ if (collapsed === undefined) {
78
+ setUncontrolledCollapsed(nextCollapsed);
79
+ }
80
+ onCollapsedChange?.(nextCollapsed);
81
+ };
82
+
83
+ const resolvedBadge =
84
+ badge ?? (count !== undefined ? <Badge variant="muted">{count}</Badge> : null);
85
+
86
+ return (
87
+ <section
88
+ id={id}
89
+ aria-labelledby={headingId}
90
+ className={cx('ui-workbench-sidebar-section', className)}
91
+ {...props}
92
+ >
93
+ <div className="ui-workbench-sidebar-section__header">
94
+ {collapsible ? (
95
+ <button
96
+ aria-controls={contentId}
97
+ aria-expanded={!resolvedCollapsed}
98
+ className="ui-workbench-sidebar-section__toggle"
99
+ type="button"
100
+ onClick={handleToggle}
101
+ >
102
+ <i
103
+ aria-hidden="true"
104
+ className={cxCodicon(
105
+ resolvedCollapsed ? 'chevron-right' : 'chevron-down',
106
+ 'ui-workbench-sidebar-section__chevron',
107
+ )}
108
+ />
109
+ <span
110
+ id={headingId}
111
+ aria-level={headingLevel}
112
+ className="ui-workbench-sidebar-section__title"
113
+ role="heading"
114
+ >
115
+ {title}
116
+ </span>
117
+ </button>
118
+ ) : (
119
+ <div className="ui-workbench-sidebar-section__static-title">
120
+ <span className="ui-workbench-sidebar-section__chevron" />
121
+ <span
122
+ id={headingId}
123
+ aria-level={headingLevel}
124
+ className="ui-workbench-sidebar-section__title"
125
+ role="heading"
126
+ >
127
+ {title}
128
+ </span>
129
+ </div>
130
+ )}
131
+ {resolvedBadge || actions ? (
132
+ <div className="ui-workbench-sidebar-section__meta">
133
+ {resolvedBadge}
134
+ {actions}
135
+ </div>
136
+ ) : null}
137
+ </div>
138
+ <div
139
+ id={contentId}
140
+ className="ui-workbench-sidebar-section__content"
141
+ hidden={resolvedCollapsed}
142
+ >
143
+ {children}
144
+ </div>
145
+ </section>
146
+ );
147
+ }
148
+
149
+ export interface WorkbenchActionListProps extends SideBarListProps {
150
+ empty?: boolean;
151
+ emptyLabel?: ReactNode;
152
+ }
153
+
154
+ export function WorkbenchActionList({
155
+ children,
156
+ className,
157
+ empty,
158
+ emptyLabel = 'No actions',
159
+ ...props
160
+ }: WorkbenchActionListProps) {
161
+ return (
162
+ <SideBarList className={cx('ui-workbench-action-list', className)} {...props}>
163
+ {empty ? (
164
+ <li className="ui-side-bar-list-entry">
165
+ <div className="ui-workbench-action-list__empty">{emptyLabel}</div>
166
+ </li>
167
+ ) : (
168
+ children
169
+ )}
170
+ </SideBarList>
171
+ );
172
+ }
173
+
174
+ export interface WorkbenchActionListItemProps extends Omit<
175
+ SideBarListItemProps,
176
+ 'children' | 'variant'
177
+ > {
178
+ danger?: boolean;
179
+ description?: ReactNode;
180
+ disabledReason?: ReactNode;
181
+ icon?: ReactNode;
182
+ label: ReactNode;
183
+ shortcut?: ReactNode;
184
+ showStatus?: boolean;
185
+ status?: WorkbenchActionStatus;
186
+ statusLabel?: string;
187
+ unavailable?: boolean;
188
+ }
189
+
190
+ export const WorkbenchActionListItem = forwardRef<HTMLButtonElement, WorkbenchActionListItemProps>(
191
+ function WorkbenchActionListItem(
192
+ {
193
+ className,
194
+ danger,
195
+ description,
196
+ disabled,
197
+ disabledReason,
198
+ icon,
199
+ label,
200
+ shortcut,
201
+ showStatus = true,
202
+ status = 'idle',
203
+ statusLabel,
204
+ title,
205
+ unavailable,
206
+ ...props
207
+ },
208
+ ref,
209
+ ) {
210
+ const generatedId = useId();
211
+ const resolvedStatus = unavailable ? 'unavailable' : status;
212
+ const resolvedDisabled = disabled || isWorkbenchStatusDisabled(resolvedStatus);
213
+ const reasonId = disabledReason ? `${generatedId}-disabled-reason` : undefined;
214
+ const resolvedStatusLabel = statusLabel ?? getWorkbenchStatusLabel(resolvedStatus);
215
+ const titleText =
216
+ title ??
217
+ (typeof disabledReason === 'string'
218
+ ? disabledReason
219
+ : unavailable
220
+ ? 'Unavailable'
221
+ : undefined);
222
+
223
+ return (
224
+ <SideBarListItem
225
+ ref={ref}
226
+ aria-busy={isWorkbenchStatusBusy(resolvedStatus) ? true : undefined}
227
+ aria-describedby={reasonId}
228
+ aria-disabled={resolvedDisabled ? true : undefined}
229
+ className={cx(
230
+ 'ui-workbench-action-list-item',
231
+ Boolean(description) && 'ui-workbench-action-list-item--with-description',
232
+ danger && 'ui-workbench-action-list-item--danger',
233
+ className,
234
+ )}
235
+ data-danger={danger ? 'true' : undefined}
236
+ data-show-status={showStatus ? 'true' : 'false'}
237
+ data-status={resolvedStatus}
238
+ data-unavailable={unavailable ? 'true' : undefined}
239
+ disabled={resolvedDisabled || isWorkbenchStatusDisabled(resolvedStatus)}
240
+ title={titleText}
241
+ {...props}
242
+ >
243
+ <span className="ui-workbench-action-list-item__icon" aria-hidden="true">
244
+ {icon}
245
+ </span>
246
+ <span className="ui-workbench-action-list-item__content">
247
+ <span className="ui-workbench-action-list-item__label">{label}</span>
248
+ {description ? (
249
+ <span className="ui-workbench-action-list-item__description">{description}</span>
250
+ ) : null}
251
+ {disabledReason ? (
252
+ <span id={reasonId} className="ui-visually-hidden">
253
+ {disabledReason}
254
+ </span>
255
+ ) : null}
256
+ </span>
257
+ <span className="ui-workbench-action-list-item__meta">
258
+ {shortcut ? (
259
+ <kbd className="ui-workbench-action-list-item__shortcut">{shortcut}</kbd>
260
+ ) : null}
261
+ </span>
262
+ {showStatus ? (
263
+ <span
264
+ aria-label={resolvedStatusLabel}
265
+ className="ui-workbench-action-list-item__status"
266
+ title={resolvedStatusLabel}
267
+ >
268
+ <span aria-hidden="true" className="ui-workbench-action-list-item__status-dot" />
269
+ </span>
270
+ ) : null}
271
+ </SideBarListItem>
272
+ );
273
+ },
274
+ );
@@ -0,0 +1,351 @@
1
+ import { forwardRef } from 'react';
2
+ import type { ComponentPropsWithRef, CSSProperties, ReactNode } from 'react';
3
+ import { IconButton } from '../primitives/IconButton';
4
+ import type { IconButtonProps } from '../primitives/IconButton';
5
+ import { cxCodicon } from '../utils/codicon';
6
+ import { cx } from '../utils/cx';
7
+ import { toLengthValue, workbenchTreeIndentOffset } from './layoutHelpers';
8
+
9
+ export interface WorkbenchTreeProps extends ComponentPropsWithRef<'div'> {
10
+ indentSize?: number | string;
11
+ rowHeight?: number | string;
12
+ }
13
+
14
+ export function WorkbenchTree({
15
+ className,
16
+ indentSize,
17
+ role = 'tree',
18
+ rowHeight,
19
+ style,
20
+ ...props
21
+ }: WorkbenchTreeProps) {
22
+ const treeStyle = {
23
+ ...(indentSize !== undefined
24
+ ? { '--ui-workbench-tree-indent-size': toLengthValue(indentSize) }
25
+ : null),
26
+ ...(rowHeight !== undefined
27
+ ? { '--ui-workbench-tree-row-height': toLengthValue(rowHeight) }
28
+ : null),
29
+ ...style,
30
+ } as CSSProperties;
31
+
32
+ return (
33
+ <div className={cx('ui-workbench-tree', className)} role={role} style={treeStyle} {...props} />
34
+ );
35
+ }
36
+
37
+ export type WorkbenchTreeInteraction = 'default' | 'draggable' | 'dragging';
38
+
39
+ export interface WorkbenchTreeItemProps extends ComponentPropsWithRef<'div'> {
40
+ actions?: ReactNode;
41
+ control?: ReactNode;
42
+ depth?: number;
43
+ icon?: ReactNode;
44
+ indentSize?: number;
45
+ interaction?: WorkbenchTreeInteraction;
46
+ label: ReactNode;
47
+ meta?: ReactNode;
48
+ selected?: boolean;
49
+ }
50
+
51
+ export const WorkbenchTreeItem = forwardRef<HTMLDivElement, WorkbenchTreeItemProps>(
52
+ function WorkbenchTreeItem(
53
+ {
54
+ actions,
55
+ children,
56
+ className,
57
+ control,
58
+ depth = 0,
59
+ icon,
60
+ indentSize = 14,
61
+ interaction = 'default',
62
+ label,
63
+ meta,
64
+ role = 'treeitem',
65
+ selected = false,
66
+ style,
67
+ tabIndex = 0,
68
+ ...props
69
+ },
70
+ ref,
71
+ ) {
72
+ const itemStyle = {
73
+ '--ui-workbench-tree-depth': depth,
74
+ '--ui-workbench-tree-indent-offset': workbenchTreeIndentOffset(depth, indentSize),
75
+ ...style,
76
+ } as CSSProperties;
77
+
78
+ return (
79
+ <div
80
+ ref={ref}
81
+ aria-selected={selected}
82
+ className={cx('ui-workbench-tree-item', className)}
83
+ data-interaction={interaction}
84
+ data-selected={selected ? 'true' : 'false'}
85
+ role={role}
86
+ style={itemStyle}
87
+ tabIndex={tabIndex}
88
+ {...props}
89
+ >
90
+ <span className="ui-workbench-tree-item__control">{control}</span>
91
+ <span className="ui-workbench-tree-item__icon" aria-hidden="true">
92
+ {icon}
93
+ </span>
94
+ <span className="ui-workbench-tree-item__label">{label}</span>
95
+ {meta ? <span className="ui-workbench-tree-item__meta">{meta}</span> : null}
96
+ {actions ? <span className="ui-workbench-tree-item__actions">{actions}</span> : null}
97
+ {children}
98
+ </div>
99
+ );
100
+ },
101
+ );
102
+
103
+ export interface WorkbenchTreeExpanderProps extends Omit<
104
+ ComponentPropsWithRef<'button'>,
105
+ 'children'
106
+ > {
107
+ expanded?: boolean;
108
+ label?: string;
109
+ visible?: boolean;
110
+ }
111
+
112
+ function handleNestedTreeControlEvent<TEvent extends { stopPropagation(): void }>(
113
+ event: TEvent,
114
+ handler: ((event: TEvent) => void) | undefined,
115
+ ) {
116
+ event.stopPropagation();
117
+ handler?.(event);
118
+ }
119
+
120
+ export function WorkbenchTreeExpander({
121
+ className,
122
+ disabled,
123
+ expanded = false,
124
+ label,
125
+ onClick,
126
+ onDoubleClick,
127
+ onPointerDown,
128
+ tabIndex,
129
+ type = 'button',
130
+ visible = true,
131
+ ...props
132
+ }: WorkbenchTreeExpanderProps) {
133
+ return (
134
+ <button
135
+ aria-expanded={visible ? expanded : undefined}
136
+ aria-hidden={visible ? undefined : true}
137
+ aria-label={visible ? label : undefined}
138
+ className={cx('ui-workbench-tree-expander', className)}
139
+ data-visible={visible ? 'true' : 'false'}
140
+ disabled={!visible || disabled}
141
+ onClick={(event) => handleNestedTreeControlEvent(event, onClick)}
142
+ onDoubleClick={(event) => handleNestedTreeControlEvent(event, onDoubleClick)}
143
+ onPointerDown={(event) => handleNestedTreeControlEvent(event, onPointerDown)}
144
+ tabIndex={visible ? tabIndex : -1}
145
+ type={type}
146
+ {...props}
147
+ >
148
+ <i aria-hidden="true" className={cxCodicon(expanded ? 'chevron-down' : 'chevron-right')} />
149
+ </button>
150
+ );
151
+ }
152
+
153
+ export interface WorkbenchTreeActionButtonProps extends IconButtonProps {
154
+ active?: boolean;
155
+ tone?: 'default' | 'muted' | 'warning';
156
+ visible?: boolean;
157
+ }
158
+
159
+ export function WorkbenchTreeActionButton({
160
+ active = false,
161
+ className,
162
+ compact = true,
163
+ onClick,
164
+ onDoubleClick,
165
+ onPointerDown,
166
+ tabIndex,
167
+ tone = 'default',
168
+ visible = false,
169
+ ...props
170
+ }: WorkbenchTreeActionButtonProps) {
171
+ return (
172
+ <IconButton
173
+ className={cx('ui-workbench-tree-action', className)}
174
+ compact={compact}
175
+ data-active={active ? 'true' : 'false'}
176
+ data-tone={tone}
177
+ data-visible={visible ? 'true' : 'false'}
178
+ onClick={(event) => handleNestedTreeControlEvent(event, onClick)}
179
+ onDoubleClick={(event) => handleNestedTreeControlEvent(event, onDoubleClick)}
180
+ onPointerDown={(event) => handleNestedTreeControlEvent(event, onPointerDown)}
181
+ tabIndex={visible ? tabIndex : -1}
182
+ {...props}
183
+ />
184
+ );
185
+ }
186
+
187
+ export interface WorkbenchTreeDropLineProps extends ComponentPropsWithRef<'div'> {
188
+ position: 'above' | 'below';
189
+ }
190
+
191
+ export function WorkbenchTreeDropLine({
192
+ className,
193
+ position,
194
+ ...props
195
+ }: WorkbenchTreeDropLineProps) {
196
+ return (
197
+ <div
198
+ aria-hidden="true"
199
+ className={cx('ui-workbench-tree-drop-line', className)}
200
+ data-position={position}
201
+ {...props}
202
+ />
203
+ );
204
+ }
205
+
206
+ export interface WorkbenchTreeDropZoneProps extends ComponentPropsWithRef<'div'> {
207
+ depth?: number;
208
+ empty?: boolean;
209
+ highlighted?: boolean;
210
+ indentSize?: number;
211
+ }
212
+
213
+ export const WorkbenchTreeDropZone = forwardRef<HTMLDivElement, WorkbenchTreeDropZoneProps>(
214
+ function WorkbenchTreeDropZone(
215
+ { className, depth = 0, empty = false, highlighted = false, indentSize = 14, style, ...props },
216
+ ref,
217
+ ) {
218
+ const zoneStyle = {
219
+ '--ui-workbench-tree-depth': depth,
220
+ '--ui-workbench-tree-indent-offset': workbenchTreeIndentOffset(depth, indentSize),
221
+ ...style,
222
+ } as CSSProperties;
223
+
224
+ return (
225
+ <div
226
+ ref={ref}
227
+ className={cx('ui-workbench-tree-drop-zone', className)}
228
+ data-empty={empty ? 'true' : 'false'}
229
+ data-highlighted={highlighted ? 'true' : 'false'}
230
+ style={zoneStyle}
231
+ {...props}
232
+ />
233
+ );
234
+ },
235
+ );
236
+
237
+ export interface WorkbenchTreeDragOverlayProps extends ComponentPropsWithRef<'div'> {
238
+ rowHeight?: number | string;
239
+ }
240
+
241
+ export function WorkbenchTreeDragOverlay({
242
+ className,
243
+ rowHeight,
244
+ style,
245
+ ...props
246
+ }: WorkbenchTreeDragOverlayProps) {
247
+ const overlayStyle = {
248
+ ...(rowHeight !== undefined
249
+ ? { '--ui-workbench-tree-row-height': toLengthValue(rowHeight) }
250
+ : null),
251
+ ...style,
252
+ } as CSSProperties;
253
+
254
+ return (
255
+ <div
256
+ className={cx('ui-workbench-tree-drag-overlay', className)}
257
+ style={overlayStyle}
258
+ {...props}
259
+ />
260
+ );
261
+ }
262
+
263
+ export interface WorkbenchDragPreviewProps extends ComponentPropsWithRef<'div'> {
264
+ maxWidth?: number | string;
265
+ offset?: number | string;
266
+ x: number | string;
267
+ y: number | string;
268
+ }
269
+
270
+ export function WorkbenchDragPreview({
271
+ children,
272
+ className,
273
+ maxWidth = 160,
274
+ offset = 8,
275
+ style,
276
+ x,
277
+ y,
278
+ ...props
279
+ }: WorkbenchDragPreviewProps) {
280
+ const previewStyle = {
281
+ '--ui-workbench-drag-preview-x': toLengthValue(x),
282
+ '--ui-workbench-drag-preview-y': toLengthValue(y),
283
+ '--ui-workbench-drag-preview-offset': toLengthValue(offset),
284
+ '--ui-workbench-drag-preview-max-width': toLengthValue(maxWidth),
285
+ ...style,
286
+ } as CSSProperties;
287
+
288
+ return (
289
+ <div className={cx('ui-workbench-drag-preview', className)} style={previewStyle} {...props}>
290
+ <span className="ui-workbench-drag-preview__label">{children}</span>
291
+ </div>
292
+ );
293
+ }
294
+
295
+ export type WorkbenchTemplateGlyphIcon =
296
+ | 'badge'
297
+ | 'color'
298
+ | 'flex'
299
+ | 'frame'
300
+ | 'grid'
301
+ | 'image'
302
+ | 'list'
303
+ | 'text';
304
+
305
+ export interface WorkbenchTemplateGlyphProps extends ComponentPropsWithRef<'span'> {
306
+ accent: string;
307
+ icon: WorkbenchTemplateGlyphIcon;
308
+ }
309
+
310
+ export function WorkbenchTemplateGlyph({
311
+ accent,
312
+ className,
313
+ icon,
314
+ style,
315
+ ...props
316
+ }: WorkbenchTemplateGlyphProps) {
317
+ const glyphStyle = {
318
+ '--ui-workbench-template-glyph-accent': accent,
319
+ ...style,
320
+ } as CSSProperties;
321
+
322
+ return (
323
+ <span
324
+ aria-hidden="true"
325
+ className={cx('ui-workbench-template-glyph', className)}
326
+ data-icon={icon}
327
+ style={glyphStyle}
328
+ {...props}
329
+ >
330
+ {icon === 'text' ? <span className="ui-workbench-template-glyph__text">T</span> : null}
331
+ {icon === 'list'
332
+ ? [0, 1, 2].map((index) => (
333
+ <span key={index} className="ui-workbench-template-glyph__list-line" />
334
+ ))
335
+ : null}
336
+ {icon === 'flex'
337
+ ? [0, 1, 2].map((index) => (
338
+ <span key={index} className="ui-workbench-template-glyph__flex-bar" />
339
+ ))
340
+ : null}
341
+ {icon === 'image' ? <span className="ui-workbench-template-glyph__image-mark" /> : null}
342
+ {icon === 'badge' ? <span className="ui-workbench-template-glyph__badge-mark" /> : null}
343
+ </span>
344
+ );
345
+ }
346
+
347
+ export type WorkbenchDividerProps = ComponentPropsWithRef<'div'>;
348
+
349
+ export function WorkbenchDivider({ className, ...props }: WorkbenchDividerProps) {
350
+ return <div className={cx('ui-workbench-divider', className)} {...props} />;
351
+ }
@@ -0,0 +1,25 @@
1
+ export function toLengthValue(value: number | string) {
2
+ return typeof value === 'number' ? `${value}px` : value;
3
+ }
4
+
5
+ export function toAngleValue(value: number | string) {
6
+ return typeof value === 'number' ? `${value}deg` : value;
7
+ }
8
+
9
+ export function toLineLengthValue(start: number | string, end: number | string) {
10
+ if (typeof start === 'number' && typeof end === 'number') {
11
+ return `${Math.max(1, end - start)}px`;
12
+ }
13
+ return `max(1px, calc(${toLengthValue(end)} - ${toLengthValue(start)}))`;
14
+ }
15
+
16
+ export const DEFAULT_WORKBENCH_TREE_BASE_INDENT = 8;
17
+ export const DEFAULT_WORKBENCH_TREE_INDENT_SIZE = 14;
18
+
19
+ export function workbenchTreeIndentOffset(
20
+ depth: number,
21
+ indentSize: number = DEFAULT_WORKBENCH_TREE_INDENT_SIZE,
22
+ baseIndent: number = DEFAULT_WORKBENCH_TREE_BASE_INDENT,
23
+ ) {
24
+ return `${baseIndent + Math.max(0, depth) * indentSize}px`;
25
+ }