@opensumi/ide-main-layout 3.7.1 → 3.7.2-next-1739859371.0

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 (46) hide show
  1. package/lib/browser/accordion/titlebar.view.d.ts +3 -0
  2. package/lib/browser/accordion/titlebar.view.d.ts.map +1 -1
  3. package/lib/browser/accordion/titlebar.view.js +12 -1
  4. package/lib/browser/accordion/titlebar.view.js.map +1 -1
  5. package/lib/browser/default-config.d.ts.map +1 -1
  6. package/lib/browser/default-config.js +9 -2
  7. package/lib/browser/default-config.js.map +1 -1
  8. package/lib/browser/drop-area/drop-area.d.ts +4 -0
  9. package/lib/browser/drop-area/drop-area.d.ts.map +1 -0
  10. package/lib/browser/drop-area/drop-area.js +25 -0
  11. package/lib/browser/drop-area/drop-area.js.map +1 -0
  12. package/lib/browser/drop-area/styles.module.less +7 -0
  13. package/lib/browser/layout.service.d.ts +6 -0
  14. package/lib/browser/layout.service.d.ts.map +1 -1
  15. package/lib/browser/layout.service.js +94 -12
  16. package/lib/browser/layout.service.js.map +1 -1
  17. package/lib/browser/main-layout.contribution.d.ts +2 -1
  18. package/lib/browser/main-layout.contribution.d.ts.map +1 -1
  19. package/lib/browser/main-layout.contribution.js +14 -1
  20. package/lib/browser/main-layout.contribution.js.map +1 -1
  21. package/lib/browser/tabbar/bar.view.d.ts.map +1 -1
  22. package/lib/browser/tabbar/bar.view.js +8 -3
  23. package/lib/browser/tabbar/bar.view.js.map +1 -1
  24. package/lib/browser/tabbar/panel.view.d.ts +1 -0
  25. package/lib/browser/tabbar/panel.view.d.ts.map +1 -1
  26. package/lib/browser/tabbar/panel.view.js +4 -3
  27. package/lib/browser/tabbar/panel.view.js.map +1 -1
  28. package/lib/browser/tabbar/tabbar.service.d.ts +3 -0
  29. package/lib/browser/tabbar/tabbar.service.d.ts.map +1 -1
  30. package/lib/browser/tabbar/tabbar.service.js +15 -0
  31. package/lib/browser/tabbar/tabbar.service.js.map +1 -1
  32. package/lib/common/main-layout.definition.d.ts +6 -0
  33. package/lib/common/main-layout.definition.d.ts.map +1 -1
  34. package/lib/common/main-layout.definition.js +3 -1
  35. package/lib/common/main-layout.definition.js.map +1 -1
  36. package/package.json +8 -8
  37. package/src/browser/accordion/titlebar.view.tsx +30 -2
  38. package/src/browser/default-config.ts +10 -2
  39. package/src/browser/drop-area/drop-area.tsx +40 -0
  40. package/src/browser/drop-area/styles.module.less +7 -0
  41. package/src/browser/layout.service.ts +107 -14
  42. package/src/browser/main-layout.contribution.ts +24 -2
  43. package/src/browser/tabbar/bar.view.tsx +9 -2
  44. package/src/browser/tabbar/panel.view.tsx +7 -2
  45. package/src/browser/tabbar/tabbar.service.ts +18 -0
  46. package/src/common/main-layout.definition.ts +7 -0
@@ -32,6 +32,8 @@ import { Deferred, getDebugLogger, isUndefined } from '@opensumi/ide-core-common
32
32
  import { ThemeChangedEvent } from '@opensumi/ide-theme';
33
33
 
34
34
  import {
35
+ DROP_BOTTOM_CONTAINER,
36
+ DROP_RIGHT_CONTAINER,
35
37
  IMainLayoutService,
36
38
  MainLayoutContribution,
37
39
  SUPPORT_ACCORDION_LOCATION,
@@ -201,7 +203,7 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
201
203
  /**
202
204
  * ContainerId 存在三种值类型,对应的处理模式如下:
203
205
  * 1. undefined: 采用首个注册的容器作为当前 containerId
204
- * 2. string: 直接使用该 containerId 作为当前 containerId
206
+ * 2. string: 非 drop container 直接使用该 containerId 作为当前 containerId
205
207
  * 3. '': 直接清空当前 containerId,不展开相应的 viewContainer
206
208
  */
207
209
  if (isUndefined(currentId)) {
@@ -211,19 +213,93 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
211
213
  } else {
212
214
  service.updateCurrentContainerId(defaultContainer);
213
215
  }
214
- } else if (currentId) {
216
+ } else if (currentId && !this.isDropContainer(currentId)) {
215
217
  if (service.containersMap.has(currentId)) {
216
218
  service.updateCurrentContainerId(currentId);
217
219
  } else {
218
- service.updateCurrentContainerId(defaultContainer);
219
- // 等待后续新容器注册时,更新当前的 containerId
220
- service.updateNextContainerId(currentId);
220
+ // 如果在别的 tabbar 中存在该 containerId,则将其移动到当前 tabbar
221
+ if (this.findTabbarServiceByContainerId(currentId)) {
222
+ this.moveContainerTo(currentId, service.location);
223
+ service.updateCurrentContainerId(currentId);
224
+ } else {
225
+ service.updateCurrentContainerId(defaultContainer);
226
+ // 等待后续新容器注册时,更新当前的 containerId
227
+ service.updateNextContainerId(currentId);
228
+ }
221
229
  }
222
- } else if (currentId === '') {
230
+ } else if (currentId === '' || this.isDropContainer(currentId)) {
223
231
  service.updateCurrentContainerId('');
224
232
  }
225
233
  };
226
234
 
235
+ findTabbarServiceByContainerId(containerId: string): TabbarService | undefined {
236
+ let tabbarService: undefined | TabbarService;
237
+ for (const value of this.tabbarServices.values()) {
238
+ if (value.containersMap.has(containerId)) {
239
+ tabbarService = value;
240
+ break;
241
+ }
242
+ }
243
+
244
+ return tabbarService;
245
+ }
246
+
247
+ moveContainerTo(containerId: string, to: string): void {
248
+ const fromTabbar = this.findTabbarServiceByContainerId(containerId);
249
+
250
+ if (!fromTabbar) {
251
+ this.logger.error(`cannot find container: ${containerId}`);
252
+ return;
253
+ }
254
+ const container = fromTabbar.getContainer(containerId);
255
+ if (!container) {
256
+ this.logger.error(`cannot find container: ${containerId}`);
257
+ return;
258
+ }
259
+ if (!container.options?.draggable) {
260
+ this.logger.warn(`container: ${containerId} is not draggable`);
261
+ return;
262
+ }
263
+
264
+ const toTabbar = this.getTabbarService(to);
265
+
266
+ fromTabbar.removeContainer(containerId);
267
+
268
+ if (!fromTabbar.visibleContainers.length || fromTabbar.currentContainerId.get() === containerId) {
269
+ this.toggleSlot(fromTabbar.location, false);
270
+ }
271
+ toTabbar.dynamicAddContainer(containerId, container);
272
+ const newHandler = this.injector.get(TabBarHandler, [containerId, this.getTabbarService(toTabbar.location)]);
273
+ this.handleMap.set(containerId, newHandler!);
274
+ }
275
+
276
+ showDropAreaForContainer(containerId: string): void {
277
+ const tabbarService = this.findTabbarServiceByContainerId(containerId);
278
+ const bottomService = this.tabbarServices.get('bottom');
279
+ const rightService = this.tabbarServices.get('right');
280
+ if (!tabbarService) {
281
+ this.logger.error(`cannot find container: ${containerId}`);
282
+ return;
283
+ }
284
+ if (tabbarService?.location === 'right') {
285
+ bottomService?.updateCurrentContainerId(DROP_BOTTOM_CONTAINER);
286
+ }
287
+ if (tabbarService?.location === 'bottom') {
288
+ rightService?.updateCurrentContainerId(DROP_RIGHT_CONTAINER);
289
+ }
290
+ }
291
+
292
+ hideDropArea(): void {
293
+ const bottomService = this.tabbarServices.get('bottom');
294
+ const rightService = this.tabbarServices.get('right');
295
+ if (bottomService?.currentContainerId.get() === DROP_BOTTOM_CONTAINER) {
296
+ bottomService.updateCurrentContainerId(bottomService.previousContainerId || '');
297
+ }
298
+ if (rightService?.currentContainerId.get() === DROP_RIGHT_CONTAINER) {
299
+ rightService.updateCurrentContainerId(rightService.previousContainerId || '');
300
+ }
301
+ }
302
+
227
303
  isVisible(location: string) {
228
304
  const tabbarService = this.getTabbarService(location);
229
305
  return !!tabbarService.currentContainerId.get();
@@ -245,18 +321,13 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
245
321
  return;
246
322
  }
247
323
  if (show === true) {
248
- tabbarService.updateCurrentContainerId(
249
- tabbarService.currentContainerId.get() ||
250
- tabbarService.previousContainerId ||
251
- tabbarService.containersMap.keys().next().value!,
252
- );
324
+ // 不允许通过该api展示drop面板
325
+ tabbarService.updateCurrentContainerId(this.findNonDropContainerId(tabbarService));
253
326
  } else if (show === false) {
254
327
  tabbarService.updateCurrentContainerId('');
255
328
  } else {
256
329
  tabbarService.updateCurrentContainerId(
257
- tabbarService.currentContainerId.get()
258
- ? ''
259
- : tabbarService.previousContainerId || tabbarService.containersMap.keys().next().value!,
330
+ tabbarService.currentContainerId.get() ? '' : this.findNonDropContainerId(tabbarService),
260
331
  );
261
332
  }
262
333
  if (tabbarService.currentContainerId.get() && size) {
@@ -264,6 +335,28 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
264
335
  }
265
336
  }
266
337
 
338
+ private isDropContainer(containerId: string): boolean {
339
+ return [DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER].includes(containerId);
340
+ }
341
+
342
+ private findNonDropContainerId(tabbarService: TabbarService): string {
343
+ const currentContainerId = tabbarService.currentContainerId.get();
344
+ if (currentContainerId && !this.isDropContainer(currentContainerId)) {
345
+ return currentContainerId;
346
+ }
347
+ if (tabbarService.previousContainerId && !this.isDropContainer(tabbarService.previousContainerId)) {
348
+ return tabbarService.previousContainerId;
349
+ }
350
+
351
+ for (const key of tabbarService.containersMap.keys()) {
352
+ if (!this.isDropContainer(key)) {
353
+ return key;
354
+ }
355
+ }
356
+
357
+ return '';
358
+ }
359
+
267
360
  getTabbarService(location: string) {
268
361
  const service = this.tabbarServices.get(location) || this.injector.get(TabbarService, [location]);
269
362
  if (!this.tabbarServices.get(location)) {
@@ -41,8 +41,9 @@ import {
41
41
  import { ContributionProvider, Domain, IEventBus, WithEventBus, localize } from '@opensumi/ide-core-common';
42
42
  import { Command, CommandContribution, CommandRegistry, CommandService } from '@opensumi/ide-core-common/lib/command';
43
43
 
44
- import { IMainLayoutService } from '../common';
44
+ import { DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER, IMainLayoutService } from '../common';
45
45
 
46
+ import { BottomDropArea, RightDropArea } from './drop-area/drop-area';
46
47
  import { ViewQuickOpenHandler } from './quick-open-view';
47
48
  import { BottomTabRenderer, LeftTabRenderer, RightTabRenderer } from './tabbar/renderer.view';
48
49
 
@@ -117,7 +118,14 @@ export const RETRACT_BOTTOM_PANEL: Command = {
117
118
  iconClass: getIcon('shrink'),
118
119
  };
119
120
 
120
- @Domain(CommandContribution, ClientAppContribution, SlotRendererContribution, MenuContribution, QuickOpenContribution)
121
+ @Domain(
122
+ CommandContribution,
123
+ ClientAppContribution,
124
+ SlotRendererContribution,
125
+ MenuContribution,
126
+ QuickOpenContribution,
127
+ ComponentContribution,
128
+ )
121
129
  export class MainLayoutModuleContribution
122
130
  extends WithEventBus
123
131
  implements
@@ -125,6 +133,7 @@ export class MainLayoutModuleContribution
125
133
  ClientAppContribution,
126
134
  SlotRendererContribution,
127
135
  MenuContribution,
136
+ ComponentContribution,
128
137
  QuickOpenContribution
129
138
  {
130
139
  @Autowired(IMainLayoutService)
@@ -186,6 +195,19 @@ export class MainLayoutModuleContribution
186
195
  }
187
196
  }
188
197
 
198
+ registerComponent(registry: ComponentRegistry): void {
199
+ registry.register(DROP_RIGHT_CONTAINER, [], {
200
+ component: RightDropArea,
201
+ hideTab: true,
202
+ containerId: DROP_RIGHT_CONTAINER,
203
+ });
204
+ registry.register(DROP_BOTTOM_CONTAINER, [], {
205
+ component: BottomDropArea,
206
+ hideTab: true,
207
+ containerId: DROP_BOTTOM_CONTAINER,
208
+ });
209
+ }
210
+
189
211
  async onStart() {
190
212
  this.registerSideToggleKey();
191
213
  }
@@ -125,11 +125,15 @@ export const TabbarViewBase: React.FC<ITabbarViewProps> = (props) => {
125
125
  });
126
126
 
127
127
  const renderContainers = React.useCallback(
128
- (component: ComponentRegistryInfo, tabbarService: TabbarService, currentContainerId?: string) => {
128
+ (component: ComponentRegistryInfo, tabbarService: TabbarService, currentContainerId?: string, side?: string) => {
129
129
  const containerId = component.options?.containerId;
130
130
  if (!containerId) {
131
131
  return null;
132
132
  }
133
+ if (side && component.options?.hideLocationTab?.includes(side)) {
134
+ return null;
135
+ }
136
+
133
137
  tabbarService.updateTabInMoreKey(containerId, false);
134
138
  let ref: HTMLLIElement | null;
135
139
  return (
@@ -168,6 +172,9 @@ export const TabbarViewBase: React.FC<ITabbarViewProps> = (props) => {
168
172
  }
169
173
  tabbarService.handleDrop(e, containerId);
170
174
  }}
175
+ onDragEnd={(e) => {
176
+ tabbarService.handleDragEnd(e);
177
+ }}
171
178
  key={containerId}
172
179
  id={containerId}
173
180
  onContextMenu={(e) => tabbarService.handleContextMenu(e, containerId)}
@@ -186,7 +193,7 @@ export const TabbarViewBase: React.FC<ITabbarViewProps> = (props) => {
186
193
  return (
187
194
  <div className={cls([styles_tab_bar, className])}>
188
195
  <div className={styles_bar_content} style={{ flexDirection: Layout.getTabbarDirection(direction) }}>
189
- {visibleContainers.map((component) => renderContainers(component, tabbarService, currentContainerId))}
196
+ {visibleContainers.map((component) => renderContainers(component, tabbarService, currentContainerId, side))}
190
197
  {renderOtherVisibleContainers({ props, renderContainers })}
191
198
  {hideContainers.length ? (
192
199
  <li
@@ -100,12 +100,13 @@ export const ContainerView: React.FC<{
100
100
  renderContainerWrap?: React.FC<{
101
101
  children: React.ReactNode;
102
102
  }>;
103
+ customTitleBar?: React.ReactNode;
103
104
  className?: string;
104
- }> = ({ component, titleMenu, side, renderContainerWrap, className }) => {
105
+ }> = ({ component, titleMenu, side, renderContainerWrap, className, customTitleBar }) => {
105
106
  const ref = React.useRef<HTMLElement | null>();
106
107
  const containerRef = React.useRef<HTMLDivElement | null>(null);
107
108
  const appConfig = useInjectable<AppConfig>(AppConfig);
108
- const { title, titleComponent, component: CustomComponent, containerId } = component.options || {};
109
+ const { title, titleComponent, component: CustomComponent, containerId, draggable } = component.options || {};
109
110
  const injector: Injector = useInjectable(INJECTOR_TOKEN);
110
111
  const layoutViewSize = useInjectable<LayoutViewSizeConfig>(LayoutViewSizeConfig);
111
112
 
@@ -140,11 +141,15 @@ export const ContainerView: React.FC<{
140
141
 
141
142
  return (
142
143
  <div ref={containerRef} className={cls(styles.view_container, className)}>
144
+ {!!customTitleBar && customTitleBar}
143
145
  {!CustomComponent && (
144
146
  <div onContextMenu={handleContextMenu} className={styles.panel_titlebar}>
145
147
  {!title ? null : (
146
148
  <TitleBar
149
+ containerId={containerId}
150
+ side={side}
147
151
  title={title}
152
+ draggable={draggable}
148
153
  height={layoutViewSize.panelTitleBarHeight}
149
154
  menubar={<InlineActionBar menus={titleMenu} />}
150
155
  />
@@ -664,6 +664,7 @@ export class TabbarService extends WithEventBus {
664
664
  // drag & drop
665
665
  handleDragStart(e: React.DragEvent, containerId: string) {
666
666
  e.dataTransfer.setData('containerId', containerId);
667
+ this.layoutService.showDropAreaForContainer(containerId);
667
668
  }
668
669
 
669
670
  handleDrop(e: React.DragEvent, target: string) {
@@ -677,6 +678,10 @@ export class TabbarService extends WithEventBus {
677
678
  }
678
679
  }
679
680
 
681
+ handleDragEnd(e: React.DragEvent) {
682
+ this.layoutService.hideDropArea();
683
+ }
684
+
680
685
  restoreState() {
681
686
  this.storedState = this.layoutState.getState(LAYOUT_STATE.getTabbarSpace(this.location), {});
682
687
  for (const containerId of this.state.keys()) {
@@ -691,6 +696,19 @@ export class TabbarService extends WithEventBus {
691
696
  });
692
697
  }
693
698
 
699
+ removeContainer(containerId: string) {
700
+ const disposable = this.disposableMap.get(containerId);
701
+ disposable?.dispose();
702
+ this.updateCurrentContainerId('');
703
+ this.doChangeViewEmitter.fire();
704
+ }
705
+
706
+ dynamicAddContainer(containerId: string, options: ComponentRegistryInfo) {
707
+ this.registerContainer(containerId, options);
708
+ this.updateCurrentContainerId(containerId);
709
+ this.doChangeViewEmitter.fire();
710
+ }
711
+
694
712
  protected doInsertTab(containers: ComponentRegistryInfo[], sourceIndex: number, targetIndex: number) {
695
713
  const targetPriority = this.getContainerState(containers[targetIndex].options!.containerId).priority;
696
714
  const changePriority = (sourceIndex: number, targetIndex: number) => {
@@ -86,6 +86,10 @@ export interface IMainLayoutService {
86
86
  getExtraTopMenu(): IContextMenu;
87
87
  getExtraMenu(): IContextMenu;
88
88
  getAllAccordionService(): Map<string, AccordionService>;
89
+ moveContainerTo(containerId: string, to: string): void;
90
+ showDropAreaForContainer(containerId: string): void;
91
+ hideDropArea(): void;
92
+ findTabbarServiceByContainerId(containerId: string): TabbarService | undefined;
89
93
  }
90
94
 
91
95
  export const MainLayoutContribution = Symbol('MainLayoutContribution');
@@ -128,3 +132,6 @@ export class ViewCollapseChangedEvent extends BasicEvent<{
128
132
  }> {}
129
133
 
130
134
  export const SUPPORT_ACCORDION_LOCATION = new Set([SlotLocation.left, SlotLocation.right]);
135
+
136
+ export const DROP_BOTTOM_CONTAINER = 'drop-bottom';
137
+ export const DROP_RIGHT_CONTAINER = 'drop-right';