Skip to content

Commit

Permalink
UI/drop down list box work (#2713)
Browse files Browse the repository at this point in the history
  • Loading branch information
softwarenerd authored Apr 10, 2024
1 parent 5b253a2 commit 98b652e
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 130 deletions.
3 changes: 3 additions & 0 deletions src/vs/base/browser/ui/positronComponents/button/button.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
border: none;
cursor: pointer;
background-color: transparent;

/* Unset user agent stylesheet styles. */
font-family: unset !important;
text-align: unset !important;
}

.positron-button:focus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@

.drop-down-list-box
.title {
display: flex;
padding-left: 6px;
align-items: center;
grid-column: title / chevron;
color: var(--vscode-positronContextMenu-foreground);
}
Expand All @@ -33,10 +31,6 @@
grid-column: chevron / end;
}

.drop-down-list-box
.chevron.disabled {
}

.drop-down-list-box-items {
width: 100%;
margin: 4px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,35 @@ import { PositronModalPopup } from 'vs/workbench/browser/positronComponents/posi
import { PositronModalReactRenderer } from 'vs/workbench/browser/positronModalReactRenderer/positronModalReactRenderer';
import { DropDownListBoxSeparator } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBoxSeparator';

/**
* DropDownListBoxEntry type.
*/
export type DropDownListBoxEntry<T extends NonNullable<any>, V extends NonNullable<any>> = DropDownListBoxItem<T, V> | DropDownListBoxSeparator;

/**
* DropDownListBoxProps interface.
*/
interface DropDownListBoxProps {
interface DropDownListBoxProps<T extends NonNullable<any>, V extends NonNullable<any>> {
keybindingService: IKeybindingService;
layoutService: ILayoutService;
className?: string;
disabled?: boolean;
title: string;
entries: (DropDownListBoxItem | DropDownListBoxSeparator)[];
selectedIdentifier?: string;
onSelectionChanged: (identifier: string) => void;
entries: DropDownListBoxEntry<T, V>[];
createItem?: (dropDownListBoxItem: DropDownListBoxItem<T, V>) => JSX.Element;
selectedIdentifier?: T;
onSelectionChanged: (dropDownListBoxItem: DropDownListBoxItem<T, V>) => void;
}

/**
* Finds a drop down list box item by identifier.
* @param identifier The identifier of the drop down list box item to find.
* @param entries The set of drop down list box entries.
* @param identifier The identifier of the drop down list box item to find.
* @returns The drop down list box item, if it was found; otherwise, undefined.
*/
const findDropDownListBoxItem = (
identifier: string | undefined,
entries: (DropDownListBoxItem | DropDownListBoxSeparator)[]
const findDropDownListBoxItem = <T extends NonNullable<any>, V>(
entries: DropDownListBoxEntry<T, V>[],
identifier?: T | undefined
) => {
// Find the drop down list box item.
for (let i = 0; i < entries.length; i++) {
Expand All @@ -61,38 +67,46 @@ const findDropDownListBoxItem = (
* @param props The component properties.
* @returns The rendered component.
*/
export const DropDownListBox = (props: DropDownListBoxProps) => {
export const DropDownListBox = <T extends NonNullable<any>, V,>(props: DropDownListBoxProps<T, V>) => {
// Reference hooks.
const ref = useRef<HTMLButtonElement>(undefined!);

// State hooks.
const [selectedDropDownListBoxItem, setSelectedDropDownListBoxItem] =
useState<DropDownListBoxItem | undefined>(
findDropDownListBoxItem(props.selectedIdentifier, props.entries)
useState<DropDownListBoxItem<T, V> | undefined>(
findDropDownListBoxItem(props.entries, props.selectedIdentifier)
);
const [highlightedDropDownListBoxItem, setHighlightedDropDownListBoxItem] =
useState<DropDownListBoxItem | undefined>(undefined);
useState<DropDownListBoxItem<T, V> | undefined>(undefined);

// Updates the selected drop down list box item.
useEffect(() => {
setSelectedDropDownListBoxItem(findDropDownListBoxItem(
props.entries,
props.selectedIdentifier,
props.entries
));
}, [props.entries, props.selectedIdentifier]);

/**
* Gets the title to display.
* @returns The title to display.
*/
const titleToDisplay = () => {
if (highlightedDropDownListBoxItem) {
return highlightedDropDownListBoxItem.options.title;
} else if (selectedDropDownListBoxItem) {
return selectedDropDownListBoxItem.options.title;
const Title = () => {
if (!props.createItem) {
if (highlightedDropDownListBoxItem) {
return <span>{highlightedDropDownListBoxItem.options.title}</span>;
} else if (selectedDropDownListBoxItem) {
return <span>{selectedDropDownListBoxItem.options.title}</span>;
}
} else {
return props.title;
if (highlightedDropDownListBoxItem) {
return props.createItem(highlightedDropDownListBoxItem);
} else if (selectedDropDownListBoxItem) {
return props.createItem(selectedDropDownListBoxItem);
}
}

return <span>{props.title}</span>;
};

// Render.
Expand All @@ -115,23 +129,26 @@ export const DropDownListBox = (props: DropDownListBoxProps) => {

// Show the drop down list box modal popup.
renderer.render(
<DropDownListBoxModalPopup
<DropDownListBoxModalPopup<T, V>
renderer={renderer}
anchor={ref.current}
entries={props.entries}
createItem={props.createItem}
onItemHighlighted={dropDownListBoxItem =>
setHighlightedDropDownListBoxItem(dropDownListBoxItem)
}
onItemSelected={dropDownListBoxItem => {
setSelectedDropDownListBoxItem(dropDownListBoxItem);
props.onSelectionChanged(dropDownListBoxItem.options.identifier);
props.onSelectionChanged(dropDownListBoxItem);
}}
/>
);
}}
>
<div className='title'>{titleToDisplay()}</div>
<div className={positronClassNames('chevron', { 'disabled': props.disabled })} aria-hidden='true'>
<div className='title'>
<Title />
</div>
<div className='chevron' aria-hidden='true'>
<div className='codicon codicon-chevron-down' />
</div>
</Button>
Expand All @@ -141,20 +158,21 @@ export const DropDownListBox = (props: DropDownListBoxProps) => {
/**
* DropDownListBoxModalPopupProps interface.
*/
interface DropDownListBoxModalPopupProps {
interface DropDownListBoxModalPopupProps<T, V> {
renderer: PositronModalReactRenderer;
anchor: HTMLElement;
entries: (DropDownListBoxItem | DropDownListBoxSeparator)[];
onItemHighlighted: (dropdownListBoxItem: DropDownListBoxItem) => void;
onItemSelected: (dropdownListBoxItem: DropDownListBoxItem) => void;
entries: DropDownListBoxEntry<T, V>[];
createItem?: (dropDownListBoxItem: DropDownListBoxItem<T, V>) => JSX.Element;
onItemHighlighted: (dropdownListBoxItem: DropDownListBoxItem<T, V>) => void;
onItemSelected: (dropdownListBoxItem: DropDownListBoxItem<T, V>) => void;
}

/**
* DropDownListBoxModalPopup component.
* @param props The component properties.
* @returns The rendered component.
*/
const DropDownListBoxModalPopup = (props: DropDownListBoxModalPopupProps) => {
const DropDownListBoxModalPopup = <T, V,>(props: DropDownListBoxModalPopupProps<T, V>) => {
// Render.
return (
<PositronModalPopup
Expand All @@ -181,24 +199,30 @@ const DropDownListBoxModalPopup = (props: DropDownListBoxModalPopupProps) => {
props.onItemSelected(entry);
}}
>
<div
className={positronClassNames(
'title',
{ 'disabled': entry.options.disabled }
)}
>
{entry.options.title}
</div>
{entry.options.icon &&
<div
className={positronClassNames(
'icon',
'codicon',
`codicon-${entry.options.icon}`,
{ 'disabled': entry.options.disabled }
)}
title={entry.options.title}
/>
{props.createItem && props.createItem(entry)}
{!props.createItem && (
<>
<div
className={positronClassNames(
'title',
{ 'disabled': entry.options.disabled }
)}
>
{entry.options.title}
</div>
{entry.options.icon &&
<div
className={positronClassNames(
'icon',
'codicon',
`codicon-${entry.options.icon}`,
{ 'disabled': entry.options.disabled }
)}
title={entry.options.title}
/>
}
</>
)
}
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@
/**
* DropDownListBoxItemOptions interface.
*/
export interface DropDownListBoxItemOptions {
readonly identifier: string;
readonly title: string;
export interface DropDownListBoxItemOptions<T extends NonNullable<any>, V extends NonNullable<any>> {
readonly identifier: T;
readonly title?: string;
readonly icon?: string;
readonly disabled?: boolean;
value: V;
}

/**
* DropDownListBoxItem class.
*/
export class DropDownListBoxItem {
export class DropDownListBoxItem<T extends NonNullable<any>, V extends NonNullable<any>> {
/**
* Constructor.
* @param options A DropDownListBoxItemOptions that contains the down list box item options.
*/
constructor(readonly options: DropDownListBoxItemOptions) { }
constructor(readonly options: DropDownListBoxItemOptions<T, V>) { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import { useEffect, useRef, useState } from 'react'; // eslint-disable-line no-d
// Other dependencies.
import { localize } from 'vs/nls';
import { Button } from 'vs/base/browser/ui/positronComponents/button/button';
import { DropDownListBox } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBox';
import { DropDownListBoxItem } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBoxItem';
import { PositronModalPopup } from 'vs/workbench/browser/positronComponents/positronModalPopup/positronModalPopup';
import { ColumnSchema, ColumnDisplayType } from 'vs/workbench/services/languageRuntime/common/positronDataExplorerComm';
import { PositronModalReactRenderer } from 'vs/workbench/browser/positronModalReactRenderer/positronModalReactRenderer';
import { DropDownListBoxSeparator } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBoxSeparator';
import { DataExplorerClientInstance } from 'vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient';
import { ColumnSchema, ColumnDisplayType } from 'vs/workbench/services/languageRuntime/common/positronDataExplorerComm';
import { DropDownListBox, DropDownListBoxEntry } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBox';
import { RowFilterParameter } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/rowFilterParameter';
import { DropDownColumnSelector } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/dropDownColumnSelector';
import { RangeRowFilter, RowFilter, RowFilterCondition, RowFilterIsBetween, RowFilterIsEmpty, RowFilterIsEqualTo, RowFilterIsGreaterThan, RowFilterIsLessThan, RowFilterIsNotBetween, RowFilterIsNotEmpty, SingleValueRowFilter } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilter';
Expand Down Expand Up @@ -99,7 +99,7 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
const [selectedColumnSchema, setSelectedColumnSchema] = useState<ColumnSchema | undefined>(
props.editRowFilter?.columnSchema
);
const [selectedCondition, setSelectedCondition] = useState<string | undefined>(
const [selectedCondition, setSelectedCondition] = useState<RowFilterCondition | undefined>(
props.editRowFilter?.rowFilterCondition
);
const [firstRowFilterValue, setFirstRowFilterValue] = useState<string>(() => {
Expand Down Expand Up @@ -139,22 +139,24 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
}

// Build the condition entries.
const conditionEntries: (DropDownListBoxItem | DropDownListBoxSeparator)[] = [];
const conditionEntries: DropDownListBoxEntry<RowFilterCondition, void>[] = [];

// Every type allows is empty and is not empty conditions.
conditionEntries.push(new DropDownListBoxItem({
identifier: RowFilterCondition.CONDITION_IS_EMPTY,
title: localize(
'positron.addEditRowFilter.conditionIsEmpty',
"is empty"
)
),
value: RowFilterCondition.CONDITION_IS_EMPTY
}));
conditionEntries.push(new DropDownListBoxItem({
identifier: RowFilterCondition.CONDITION_IS_NOT_EMPTY,
title: localize(
'positron.addEditRowFilter.conditionIsNotEmpty',
"is not empty"
)
),
value: RowFilterCondition.CONDITION_IS_NOT_EMPTY
}));
conditionEntries.push(new DropDownListBoxSeparator());

Expand All @@ -169,14 +171,16 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
title: localize(
'positron.addEditRowFilter.conditionIsLessThan',
"is less than"
)
),
value: RowFilterCondition.CONDITION_IS_LESS_THAN
}));
conditionEntries.push(new DropDownListBoxItem({
identifier: RowFilterCondition.CONDITION_IS_GREATER_THAN,
title: localize(
'positron.addEditRowFilter.conditionIsGreaterThan',
"is greater than"
)
),
value: RowFilterCondition.CONDITION_IS_GREATER_THAN
}));
break;
}
Expand All @@ -194,7 +198,8 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
title: localize(
'positron.addEditRowFilter.conditionIsEqualTo',
"is equal to"
)
),
value: RowFilterCondition.CONDITION_IS_EQUAL_TO
}));
break;
}
Expand All @@ -211,14 +216,16 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
title: localize(
'positron.addEditRowFilter.conditionIsBetween',
"is between"
)
),
value: RowFilterCondition.CONDITION_IS_BETWEEN
}));
conditionEntries.push(new DropDownListBoxItem({
identifier: RowFilterCondition.CONDITION_IS_NOT_BETWEEN,
title: localize(
'positron.addEditRowFilter.conditionIsNotBetween',
"is not between"
)
),
value: RowFilterCondition.CONDITION_IS_NOT_BETWEEN
}));
break;
}
Expand Down Expand Up @@ -575,14 +582,15 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
))()}
entries={conditionEntries()}
selectedIdentifier={selectedCondition}
onSelectionChanged={identifier => {
onSelectionChanged={dropDownListBoxItem => {
// Set the selected condition.
setSelectedCondition(identifier);
setSelectedCondition(dropDownListBoxItem.options.identifier);

// Clear the filter values and error text.
clearFilterValuesAndErrorText();
}}
/>

{firstRowFilterParameterComponent}
{secondRowFilterParameterComponent}
{errorText && (
Expand Down
Loading

0 comments on commit 98b652e

Please sign in to comment.