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

Implement connections list screen #5153

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

.connections-list-container {
height: 100%;
position: relative;
left: 0.5px;
}

.connections-list-header {
display: flex;
align-items: center;
border-bottom: 1px solid var(--vscode-positronVariables-border);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.connections-list-header .vertical-splitter {
background-color: var(--vscode-positronVariables-border);
}

.positron-connections-list .action-bar-button.disabled {
cursor: not-allowed;
}

.positron-connections-list .action-bar-button.disabled .action-bar-button-text {
color: var(--vscode-positronSideActionBar-disabledForeground);
}

.connections-list-item {
margin-top: 5px;
display: flex;
align-items: center;
}

.connections-list-container .col-icon,
.connections-list-container .col-name,
.connections-list-container .col-language,
.connections-list-container .col-status,
.connections-list-container .col-action {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.connections-list-container .col-icon {
flex-shrink: 0;
height: 100%;
}

.connections-list-container .col-name {
padding-left: 5px;
flex-basis: 35%;
flex-grow: 1;
}

.connections-list-container .col-status {
padding-left: 5px;
flex-basis: 25%;
flex-shrink: 0;
}

.connections-list-item .col-status.disabled {
color: var(--vscode-positronSideActionBar-disabledForeground);
}

.connections-list-container .col-language {
padding-left: 5px;
flex-basis: 20%;
flex-shrink: 0;
}

.connections-list-container.col-action {
flex-shrink: 0;
height: 100%;
}

.connections-list-item .col-action {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}

.connections-list-item.selected {
color: var(--vscode-positronVariables-activeSelectionForeground);
background: var(--vscode-positronVariables-activeSelectionBackground);
outline: 1px;
outline-offset: -1px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

import React, { useState, useEffect } from 'react';
import { ActionBarButton } from 'vs/platform/positronActionBar/browser/components/actionBarButton';
import { ActionBarRegion } from 'vs/platform/positronActionBar/browser/components/actionBarRegion';
import { ActionBarSearch } from 'vs/platform/positronActionBar/browser/components/actionBarSearch';
import { PositronActionBar } from 'vs/platform/positronActionBar/browser/positronActionBar';
import { PositronActionBarContextProvider } from 'vs/platform/positronActionBar/browser/positronActionBarContext';
import { ViewsProps } from 'vs/workbench/contrib/positronConnections/browser/positronConnections';
import { PositronConnectionsServices, usePositronConnectionsContext } from 'vs/workbench/contrib/positronConnections/browser/positronConnectionsContext';
import { FixedSizeList as List } from 'react-window';
import 'vs/css!./listConnections';
import { positronClassNames } from 'vs/base/common/positronUtilities';
import { languageIdToName } from 'vs/workbench/contrib/positronConnections/browser/components/schemaNavigation';
import { IPositronConnectionInstance } from 'vs/workbench/services/positronConnections/browser/interfaces/positronConnectionsInstance';
import { DisposableStore } from 'vs/base/common/lifecycle';

export interface ListConnnectionsProps extends ViewsProps { }

export const ListConnections = (props: React.PropsWithChildren<ListConnnectionsProps>) => {

const context = usePositronConnectionsContext();
const { height, setActiveInstanceId } = props;

const [instances, setInstances] = useState<IPositronConnectionInstance[]>(context.connectionsService.getConnections);
useEffect(() => {
const disposableStore = new DisposableStore();
disposableStore.add(context.connectionsService.onDidChangeConnections((connections) => {
setInstances(connections);
}));
return () => disposableStore.dispose();
}, [context.connectionsService]);

const [selectedInstanceId, setSelectedInstanceId] = useState<string | undefined>(undefined);

const ItemEntry = (props: { index: number; style: any }) => {
const itemProps = instances[props.index];

return (
<div
style={props.style}
className={positronClassNames(
'connections-list-item',
{ 'selected': itemProps.id === selectedInstanceId }
)}
onMouseDown={() => setSelectedInstanceId(itemProps.id)}
>
<div className='col-icon' style={{ width: `${26}px` }}></div>
<div className='col-name'>{itemProps.name}</div>
<div className='col-language'>
{itemProps.language_id ? languageIdToName(itemProps.language_id) : ''}
</div>
<div
className={positronClassNames('col-status', { 'disabled': !itemProps.active })}
>
{itemProps.active ? 'Connected' : 'Disconected'}
</div>
<div
className='col-action' style={{ width: `${26}px` }}
onMouseDown={() => setActiveInstanceId(itemProps.id)}
>
<div
className={`codicon codicon-arrow-circle-right`}
>
</div>
</div>
</div>
);
};

return (
<div className='positron-connections-list'>
<ActionBar
{...context}
deleteConnectionHandler={
selectedInstanceId ?
() => {
context.connectionsService.removeConnection(selectedInstanceId);
} :
undefined
}
>
</ActionBar>
<div className='connections-list-container'>
<div className='connections-list-header' style={{ height: `${24}px` }}>
<div className='col-icon' style={{ width: `${26}px` }}></div>
<VerticalSplitter />
<div className='col-name'>Connection</div>
<VerticalSplitter />
<div className='col-language'>Language</div>
<VerticalSplitter />
<div className='col-status'>Status</div>
<VerticalSplitter />
<div className='col-action' style={{ width: `${26}px` }}></div>
</div>
<List
itemCount={instances.length}
itemSize={26}
height={height - ACTION_BAR_HEIGHT}
width={'calc(100% - 2px)'}
itemKey={index => instances[index].id}
>
{ItemEntry}
</List>
</div>
</div>
);
};

const VerticalSplitter = () => {
return (
<div className='vertical-splitter' style={{ width: '1px' }}>
<div className='sash' style={{ left: '-2px', width: '4px', cursor: 'auto' }}></div>
</div>
);
};


const ACTION_BAR_PADDING_LEFT = 8;
const ACTION_BAR_PADDING_RIGHT = 8;
const ACTION_BAR_HEIGHT = 32;

interface ActionBarProps extends PositronConnectionsServices {
deleteConnectionHandler?: () => void;
}

const ActionBar = (props: React.PropsWithChildren<ActionBarProps>) => {

return (
<div style={{ height: ACTION_BAR_HEIGHT }}>
<PositronActionBarContextProvider {...props}>
<PositronActionBar
size='small'
borderTop={true}
borderBottom={true}
paddingLeft={ACTION_BAR_PADDING_LEFT}
paddingRight={ACTION_BAR_PADDING_RIGHT}
>
<ActionBarRegion location='left'>
<ActionBarButton
align='left'
iconId='positron-new-connection'
tooltip={() => 'New Connection'}
text='New Connection'
disabled={true}
/>
</ActionBarRegion>
<ActionBarRegion location='right'>
<ActionBarButton
align='right'
iconId='close'
tooltip={() => 'Delete Connection'}
text='Delete Connection'
disabled={props.deleteConnectionHandler === undefined}
onPressed={props.deleteConnectionHandler}
/>
<div className='action-bar-disabled'>
<ActionBarSearch placeholder='filter'></ActionBarSearch>
</div>
</ActionBarRegion>
</PositronActionBar>
</PositronActionBarContextProvider>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

.connections-items-container {
height: 100%;
position: relative;
left: 0.5px;
}

.connections-item {
display: flex;
cursor: pointer;
align-items: center;
height: 26px;
}

.connections-details {
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: monospace;
display: flex;
align-items: center;
}

.connections-dtype {
color: var(--vscode-positronSideActionBar-disabledForeground);
font-weight: 300;
}

.connections-dtype::before {
content: ":";
color: var(--vscode-positronVariables-foreground);
margin-left: 3px;
margin-right: 3px;
}

.connections-language::before {
content: ":";
color: var(--vscode-positronVariables-foreground);
margin-left: 0px;
margin-right: 6px;
}

.connections-item .connections-icon {
flex-shrink: 0;
flex-grow: 0;
padding-left: 5px;
padding-right: 5px;
}

.connections-icon.disabled {
opacity: 0.7;
}

.connections-item .expand-collapse-area {
width: 26px;
display: flex;
align-items: center;
justify-content: center;
}

.connections-item.selected {
color: var(--vscode-positronVariables-activeSelectionForeground);
background: var(--vscode-positronVariables-activeSelectionBackground);
outline: 1px;
outline-offset: -1px;
}

.connection-disabled {
color: var(--vscode-positronSideActionBar-disabledForeground);
}

.connections-error {
margin-left: 5px;
}

.connections-error.codicon {
color: var(--vscode-debugConsole-errorForeground);
}

.connections-instance-details {
display: flex;
align-items: center;
border-bottom: 1px solid var(--vscode-positronVariables-border);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: calc(100% - 2px);
}

.connections-instance-details .connection-name {
padding-left: 10px;
flex-grow: 1;
flex-basis: 50%;
}

.connections-instance-details .connection-language {
flex-basis: 20%;
}

.connections-instance-details .connection-icon {
flex-grow: 0;
flex-shrink: 0;
width: 26px;
display: flex;
align-items: center;
justify-content: center;
}
Loading