Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make terminal draggable #4340

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
18 changes: 9 additions & 9 deletions packages/ai-native/src/browser/layout/layout.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,20 @@
border: none;
}

.header {
height: 36px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
background-color: var(--editorGroupHeader-tabsBackground);
}

.right_slot_container_wrap {
height: 100%;
display: flex;
flex-direction: column;

.header {
height: 36px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
background-color: var(--editorGroupHeader-tabsBackground);
}

.container {
flex: 1;
}
Expand Down
18 changes: 10 additions & 8 deletions packages/ai-native/src/browser/layout/tabbar.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,18 @@ export const AIRightTabRenderer = ({
return (
<ContainerView
{...props}
customTitleBar={
<div className={styles.header}>
<span className={styles.title}>{options && options.title}</span>
<div className={styles.side}>
<EnhancePopover id={'ai_right_panel_header_close'} title={localize('editor.title.context.close')}>
<EnhanceIcon icon='close' onClick={handleClose} />
</EnhancePopover>
</div>
</div>
}
renderContainerWrap={({ children }) => (
<div className={styles.right_slot_container_wrap}>
<div className={styles.header}>
<span className={styles.title}>{options && options.title}</span>
<div className={styles.side}>
<EnhancePopover id={'ai_right_panel_header_close'} title={localize('editor.title.context.close')}>
<EnhanceIcon icon='close' onClick={handleClose} />
</EnhancePopover>
</div>
</div>
<div className={styles.container}>{children}</div>
</div>
)}
Expand Down
1 change: 1 addition & 0 deletions packages/core-browser/src/layout/layout.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export interface ExtViewContainerOptions {
// viewContainer 最小高度,默认 120
miniSize?: number;
alignment?: Layout.alignment;
draggable?: boolean;
}
export const ComponentRegistry = Symbol('ComponentRegistry');

Expand Down
25 changes: 23 additions & 2 deletions packages/main-layout/src/browser/accordion/titlebar.view.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import React from 'react';

import { useDesignStyles } from '@opensumi/ide-core-browser';
import { useDesignStyles, useInjectable } from '@opensumi/ide-core-browser';

import { TabbarService, TabbarServiceFactory } from '../tabbar/tabbar.service';

import styles from './styles.module.less';

export const TitleBar: React.FC<{
title: string;
menubar?: React.ReactNode;
height?: number;
draggable?: boolean;
side: string;
containerId: string;
Marckon marked this conversation as resolved.
Show resolved Hide resolved
}> = React.memo((props) => {
const styles_titlebar = useDesignStyles(styles.titlebar, 'titlebar');
const tabbarService: TabbarService = useInjectable(TabbarServiceFactory)(props.side);

return (
<div className={styles_titlebar} style={{ height: props.height }}>
<h1>{props.title}</h1>
{!props.draggable && <h1>{props.title}</h1>}
{!!props.draggable && (
<h1
draggable
style={{ cursor: 'pointer' }}
onDragStart={(e) => {
Marckon marked this conversation as resolved.
Show resolved Hide resolved
tabbarService.handleDragStart(e, props.containerId);
}}
onDragEnd={(e) => {
tabbarService.handleDragEnd(e);
}}
Marckon marked this conversation as resolved.
Show resolved Hide resolved
>
{props.title}
</h1>
)}
{props.menubar || null}
</div>
);
Expand Down
12 changes: 10 additions & 2 deletions packages/main-layout/src/browser/default-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* istanbul ignore file */
import { LayoutConfig, SlotLocation } from '@opensumi/ide-core-browser';

import { DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER } from '../common';

export const defaultConfig: LayoutConfig = {
[SlotLocation.top]: {
modules: ['@opensumi/ide-menu-bar'],
Expand All @@ -19,13 +21,19 @@ export const defaultConfig: LayoutConfig = {
],
},
[SlotLocation.right]: {
modules: [],
modules: [DROP_RIGHT_CONTAINER],
},
[SlotLocation.main]: {
modules: ['@opensumi/ide-editor'],
},
[SlotLocation.bottom]: {
modules: ['@opensumi/ide-terminal-next', '@opensumi/ide-output', 'debug-console', '@opensumi/ide-markers'],
modules: [
DROP_BOTTOM_CONTAINER,
'@opensumi/ide-terminal-next',
'@opensumi/ide-output',
'debug-console',
'@opensumi/ide-markers',
],
},
[SlotLocation.statusBar]: {
modules: ['@opensumi/ide-status-bar'],
Expand Down
34 changes: 34 additions & 0 deletions packages/main-layout/src/browser/drop-area/drop-area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';

import { useInjectable } from '@opensumi/ide-core-browser';
import { IMainLayoutService } from '@opensumi/ide-main-layout';

import styles from './styles.module.less';

interface IDropAreaProps {
location: string;
}

const DropArea: React.FC<IDropAreaProps> = (props) => {
const { location } = props;
const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);

return (
<div
className={styles.drop_area}
onDrop={(e) => {
const containerId = e.dataTransfer?.getData('containerId');
layoutService.moveContainerTo(containerId, location);
}}
Marckon marked this conversation as resolved.
Show resolved Hide resolved
onDragOver={(e) => {
e.preventDefault();
}}
>
drop here
</div>
);
};

export const RightDropArea = () => <DropArea location='right' />;

export const BottomDropArea = () => <DropArea location='bottom' />;
7 changes: 7 additions & 0 deletions packages/main-layout/src/browser/drop-area/styles.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.drop_area {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
98 changes: 90 additions & 8 deletions packages/main-layout/src/browser/layout.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import { Deferred, getDebugLogger, isUndefined } from '@opensumi/ide-core-common
import { ThemeChangedEvent } from '@opensumi/ide-theme';

import {
DROP_BOTTOM_CONTAINER,
DROP_RIGHT_CONTAINER,
IMainLayoutService,
MainLayoutContribution,
SUPPORT_ACCORDION_LOCATION,
Expand Down Expand Up @@ -224,6 +226,70 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
}
};

findTabbarServiceByContainerId(containerId: string): TabbarService | undefined {
let tabbarService: undefined | TabbarService;
for (const value of this.tabbarServices.values()) {
if (value.containersMap.has(containerId)) {
tabbarService = value;
break;
}
}

return tabbarService;
}

moveContainerTo(containerId: string, to: string): void {
const fromTabbar = this.findTabbarServiceByContainerId(containerId);

if (!fromTabbar) {
this.logger.error(`cannot find container: ${containerId}`);
return;
}
const container = fromTabbar.getContainer(containerId);
if (!container) {
this.logger.error(`cannot find container: ${containerId}`);
return;
}

const toTabbar = this.getTabbarService(to);

fromTabbar.removeContainer(containerId);

if (!fromTabbar.visibleContainers.length || fromTabbar.currentContainerId.get() === containerId) {
this.toggleSlot(fromTabbar.location, false);
}
toTabbar.dynamicAddContainer(containerId, container);
const newHandler = this.injector.get(TabBarHandler, [containerId, this.getTabbarService(toTabbar.location)]);
this.handleMap.set(containerId, newHandler!);
}
Marckon marked this conversation as resolved.
Show resolved Hide resolved

showDropAreaForContainer(containerId: string): void {
const tabbarService = this.findTabbarServiceByContainerId(containerId);
const bottomService = this.tabbarServices.get('bottom');
const rightService = this.tabbarServices.get('right');
if (!tabbarService) {
this.logger.error(`cannot find container: ${containerId}`);
return;
}
if (tabbarService?.location === 'right') {
bottomService?.updateCurrentContainerId('drop-bottom');
}
if (tabbarService?.location === 'bottom') {
rightService?.updateCurrentContainerId('drop-right');
}
}

Marckon marked this conversation as resolved.
Show resolved Hide resolved
hideDropArea(): void {
const bottomService = this.tabbarServices.get('bottom');
const rightService = this.tabbarServices.get('right');
if (bottomService?.currentContainerId.get() === DROP_BOTTOM_CONTAINER) {
bottomService.updateCurrentContainerId(bottomService.previousContainerId || '');
}
if (rightService?.currentContainerId.get() === DROP_RIGHT_CONTAINER) {
rightService.updateCurrentContainerId(rightService.previousContainerId || '');
}
}

isVisible(location: string) {
const tabbarService = this.getTabbarService(location);
return !!tabbarService.currentContainerId.get();
Expand All @@ -245,25 +311,41 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
return;
}
if (show === true) {
tabbarService.updateCurrentContainerId(
tabbarService.currentContainerId.get() ||
tabbarService.previousContainerId ||
tabbarService.containersMap.keys().next().value!,
);
// 不允许通过该api展示drop面板
tabbarService.updateCurrentContainerId(this.findNonDropContainerId(tabbarService));
} else if (show === false) {
tabbarService.updateCurrentContainerId('');
} else {
tabbarService.updateCurrentContainerId(
tabbarService.currentContainerId.get()
? ''
: tabbarService.previousContainerId || tabbarService.containersMap.keys().next().value!,
tabbarService.currentContainerId.get() ? '' : this.findNonDropContainerId(tabbarService),
);
}
if (tabbarService.currentContainerId.get() && size) {
tabbarService.resizeHandle?.setSize(size);
}
}

private findNonDropContainerId(tabbarService: TabbarService): string {
const currentContainerId = tabbarService.currentContainerId.get();
if (currentContainerId && ![DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER].includes(currentContainerId as string)) {
return currentContainerId;
}
if (
tabbarService.previousContainerId &&
![DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER].includes(tabbarService.previousContainerId as string)
) {
return tabbarService.previousContainerId;
}

for (const key of tabbarService.containersMap.keys()) {
if (![DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER].includes(key as string)) {
return key;
}
}

return '';
Marckon marked this conversation as resolved.
Show resolved Hide resolved
}

getTabbarService(location: string) {
const service = this.tabbarServices.get(location) || this.injector.get(TabbarService, [location]);
if (!this.tabbarServices.get(location)) {
Expand Down
26 changes: 24 additions & 2 deletions packages/main-layout/src/browser/main-layout.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ import {
import { ContributionProvider, Domain, IEventBus, WithEventBus, localize } from '@opensumi/ide-core-common';
import { Command, CommandContribution, CommandRegistry, CommandService } from '@opensumi/ide-core-common/lib/command';

import { IMainLayoutService } from '../common';
import { DROP_BOTTOM_CONTAINER, DROP_RIGHT_CONTAINER, IMainLayoutService } from '../common';

import { BottomDropArea, RightDropArea } from './drop-area/drop-area';
import { ViewQuickOpenHandler } from './quick-open-view';
import { BottomTabRenderer, LeftTabRenderer, RightTabRenderer } from './tabbar/renderer.view';

Expand Down Expand Up @@ -117,14 +118,22 @@ export const RETRACT_BOTTOM_PANEL: Command = {
iconClass: getIcon('shrink'),
};

@Domain(CommandContribution, ClientAppContribution, SlotRendererContribution, MenuContribution, QuickOpenContribution)
@Domain(
CommandContribution,
ClientAppContribution,
SlotRendererContribution,
MenuContribution,
QuickOpenContribution,
ComponentContribution,
)
export class MainLayoutModuleContribution
extends WithEventBus
implements
CommandContribution,
ClientAppContribution,
SlotRendererContribution,
MenuContribution,
ComponentContribution,
QuickOpenContribution
{
@Autowired(IMainLayoutService)
Expand Down Expand Up @@ -186,6 +195,19 @@ export class MainLayoutModuleContribution
}
}

registerComponent(registry: ComponentRegistry): void {
registry.register(DROP_RIGHT_CONTAINER, [], {
component: RightDropArea,
hideTab: true,
containerId: DROP_RIGHT_CONTAINER,
});
registry.register(DROP_BOTTOM_CONTAINER, [], {
component: BottomDropArea,
hideTab: true,
containerId: DROP_BOTTOM_CONTAINER,
});
}

async onStart() {
this.registerSideToggleKey();
}
Expand Down
3 changes: 3 additions & 0 deletions packages/main-layout/src/browser/tabbar/bar.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ export const TabbarViewBase: React.FC<ITabbarViewProps> = (props) => {
}
tabbarService.handleDrop(e, containerId);
}}
onDragEnd={(e) => {
tabbarService.handleDragEnd(e);
}}
key={containerId}
id={containerId}
onContextMenu={(e) => tabbarService.handleContextMenu(e, containerId)}
Expand Down
Loading