Skip to content

Commit

Permalink
feat: ListView + Picker - Item icon support (#1959)
Browse files Browse the repository at this point in the history
This example shows both basic and table data sources.
```python
import deephaven.ui as ui


@ui.component
def ui_list_view():
    value, set_value = ui.use_state(["Text 2"])

    text = ui.text("Selection: " + ", ".join(map(str, value)), grid_column="span 2")

    # list_view with text children
    lv = ui.list_view(
        "Text 1",
        "Text 2",
        "Text 3",
        # min_height=1,
        aria_label="List View",
        on_change=set_value,
        selected_keys=value,
    )

    # list_view with item children
    lv2 = ui.list_view(
        ui.item("Item 1", key="Text 1"),
        ui.item("Item 2", key="Text 2"),
        ui.item("Item 3", key="Text 3"),
        aria_label="List View 2",
        on_change=set_value,
        selected_keys=value,
    )
    
    return ui.grid(
        text,
        lv,
        lv2,
        columns="repeat(2, 1fr)",
        rows="min-content",
        height="100%"
    )


lv = ui_list_view()

####################################
import deephaven.ui as ui
from deephaven import time_table
import datetime

icon_names = ['vsAccount']

# Ticking table with initial row count of 200 that adds a row every second
initial_row_count=2000
columns = [
    "Id=new Integer(i)",
    "Display=new String(`Display `+i)",
    "Description=new String(`Description `+i)",
    "Icon=(String) icon_names[0]"
]
# column_types_ticking = time_table("PT1S", start_time=datetime.datetime.now() - datetime.timedelta(seconds=initial_row_count)).update([
#     columns
# )
column_types = empty_table(initial_row_count).update(columns)

@ui.component
def labeled_lv(label, *args, **kwargs):
    return ui.flex(
        ui.text(label),
        ui.list_view(
            *args,
            **kwargs
        ),
        direction="column",
        flex=1,
        min_width=0,
    )

@ui.component
def ui_list_view_table():
    value, set_value = ui.use_state([2, 4, 5])

    lv = labeled_lv(
        "Compact",
        column_types,
        max_height=5000,
        density="compact",
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View",
        on_change=set_value,
        selected_keys=value,
    )

    lv2 = labeled_lv(
        "Regular",
        column_types,
        max_height=5000,
        density="regular",
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View 2",
        on_change=set_value,
        selected_keys=value,
    )

    lv3 = labeled_lv(
        "Spacious",
        column_types,
        max_height=5000,
        density="spacious",
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View 3",
        on_change=set_value,
        selected_keys=value,
    )

    text = ui.text("Selection: " + ", ".join(map(str, value)))

    return ui.flex(
        ui.flex(
            lv,
            lv2,
            lv3,
            direction="row",
            gap=10,
        ),
        text,
        direction="column",
    )

lv_table = ui_list_view_table()
```

resolves #1890

---------

Co-authored-by: Don McKenzie <donmckenzie@deephaven.io>
  • Loading branch information
bmingles and dsmmcken authored May 1, 2024
1 parent fb63be1 commit cb13c60
Show file tree
Hide file tree
Showing 33 changed files with 1,368 additions and 564 deletions.
261 changes: 184 additions & 77 deletions packages/code-studio/src/styleguide/ListViews.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import React, { useCallback, useState } from 'react';
import React, { ChangeEvent, ReactNode, useCallback, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import type { StyleProps } from '@react-types/shared';
import {
Grid,
Icon,
Item,
ListView,
ListViewNormalized,
ItemKey,
Text,
Flex,
Checkbox,
ListViewProps,
RadioGroup,
RadioItem,
useSpectrumThemeProvider,
} from '@deephaven/components';
import { vsAccount, vsPerson } from '@deephaven/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { LIST_VIEW_ROW_HEIGHTS } from '@deephaven/utils';
import { generateNormalizedItems, sampleSectionIdAndClasses } from './utils';

// Generate enough items to require scrolling
const itemsSimple = [...generateNormalizedItems(52)];
const itemsWithIcons = [...generateNormalizedItems(52, { icons: true })];

function AccountIllustration(): JSX.Element {
return (
Expand All @@ -25,11 +34,63 @@ function AccountIllustration(): JSX.Element {
);
}

interface LabeledProps extends StyleProps {
label: string;
direction?: 'row' | 'column';
children: ReactNode;
}

const LABELED_FLEX_CONTAINER_HEIGHTS = {
gap: 10,
label: {
medium: 21,
large: 25.5,
},
};

function LabeledFlexContainer({
label,
direction = 'column',
children,
...styleProps
}: LabeledProps) {
return (
<Flex
// eslint-disable-next-line react/jsx-props-no-spreading
{...styleProps}
direction={direction}
gap={LABELED_FLEX_CONTAINER_HEIGHTS.gap}
>
<Text>{label}</Text>
{children}
</Flex>
);
}

export function ListViews(): JSX.Element {
const { scale } = useSpectrumThemeProvider();
const [selectedKeys, setSelectedKeys] = useState<'all' | Iterable<ItemKey>>(
[]
);

const [density, setDensity] = useState<ListViewProps['density']>('compact');

// Calculate the height of the single child example
const singleChildExampleHeight =
LABELED_FLEX_CONTAINER_HEIGHTS.label[scale] +
LABELED_FLEX_CONTAINER_HEIGHTS.gap +
2 + // listview border
LIST_VIEW_ROW_HEIGHTS[density ?? 'compact'][scale];

const onDensityChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
setDensity(event.currentTarget.value as ListViewProps['density']);
},
[]
);

const [showIcons, setShowIcons] = useState(true);

const onChange = useCallback((keys: 'all' | Iterable<ItemKey>): void => {
setSelectedKeys(keys);
}, []);
Expand All @@ -39,82 +100,128 @@ export function ListViews(): JSX.Element {
<div {...sampleSectionIdAndClasses('list-views')}>
<h2 className="ui-title">List View</h2>

<Grid columnGap={14} height="size-6000">
<Text>Single Child</Text>
<ListView
density="compact"
gridRow="2"
aria-label="Single Child"
selectionMode="multiple"
>
<Item>Aaa</Item>
</ListView>

<label>Icons</label>
<ListView
gridRow="2"
aria-label="Icon"
density="compact"
selectionMode="multiple"
>
<Item textValue="Item with icon A">
<AccountIllustration />
<Text>Item with icon A</Text>
</Item>
<Item textValue="Item with icon B">
<AccountIllustration />
<Text>Item with icon B</Text>
</Item>
<Item textValue="Item with icon C">
<AccountIllustration />
<Text>Item with icon C</Text>
</Item>
<Item textValue="Item with icon D">
<AccountIllustration />
<Text>Item with icon D with overflowing content</Text>
</Item>
</ListView>

<label>Mixed Children Types</label>
<ListView
gridRow="2"
aria-label="Mixed Children Types"
density="compact"
maxWidth="size-2400"
selectionMode="multiple"
defaultSelectedKeys={[999, 444]}
<Grid
gap={14}
height="size-6000"
columns="1fr 1fr 1fr"
rows={`auto minmax(${singleChildExampleHeight}px, auto) 1fr auto 1fr`}
>
<LabeledFlexContainer
direction="row"
label="Density"
gridColumn="span 3"
>
{/* eslint-disable react/jsx-curly-brace-presence */}
{'String 1'}
{'String 2'}
{'String 3'}
{''}
{'Some really long text that should get truncated'}
{/* eslint-enable react/jsx-curly-brace-presence */}
{444}
{999}
{true}
{false}
<Item>Item Aaa</Item>
<Item>Item Bbb</Item>
<Item textValue="Complex Ccc">
<Icon slot="image">
<FontAwesomeIcon icon={vsPerson} />
</Icon>
<Text>Complex Ccc with text that should be truncated</Text>
</Item>
</ListView>

<label>Controlled</label>
<ListView
gridRow="2"
aria-label="Controlled"
selectionMode="multiple"
selectedKeys={selectedKeys}
onChange={onChange}
<RadioGroup value={density} onChange={onDensityChange}>
<RadioItem value="compact">Compact</RadioItem>
<RadioItem value="regular">Regular</RadioItem>
<RadioItem value="spacious">Spacious</RadioItem>
</RadioGroup>
</LabeledFlexContainer>

<LabeledFlexContainer
label="Single Child"
gridColumn="span 3"
height="100%"
>
{itemsSimple}
</ListView>
<ListView
density={density}
aria-label="Single Child"
selectionMode="multiple"
>
<Item textValue="Aaa">Aaa</Item>
</ListView>
</LabeledFlexContainer>

<LabeledFlexContainer label="Icons" gridColumn="span 2">
<ListView
aria-label="Icon"
density={density}
selectionMode="multiple"
>
<Item textValue="Item with icon A">
<AccountIllustration />
<Text>Item with icon A</Text>
</Item>
<Item textValue="Item with icon B">
<AccountIllustration />
<Text>Item with icon B</Text>
</Item>
<Item textValue="Item with icon C">
<AccountIllustration />
<Text>Item with icon C</Text>
</Item>
<Item textValue="Item with icon D">
<AccountIllustration />
<Text>Item with icon D with overflowing content</Text>
</Item>
<Item textValue="Item with icon E">
<AccountIllustration />
<Text>Item with icon E</Text>
</Item>
</ListView>
</LabeledFlexContainer>

<LabeledFlexContainer label="Mixed Children Types">
<ListView
aria-label="Mixed Children Types"
density={density}
maxWidth="size-2400"
selectionMode="multiple"
defaultSelectedKeys={['999', 444]}
>
{/* eslint-disable react/jsx-curly-brace-presence */}
{'String 1'}
{'String 2'}
{'String 3'}
{''}
{'Some really long text that should get truncated'}
{/* eslint-enable react/jsx-curly-brace-presence */}
{444}
{999}
{true}
{false}
<Item textValue="Item Aaa">Item Aaa</Item>
<Item textValue="Item Bbb">Item Bbb</Item>
<Item textValue="Item with Description">
<Text>Item with Description</Text>
<Text slot="description">Description</Text>
</Item>
<Item textValue="Complex Ccc">
<Icon slot="image">
<FontAwesomeIcon icon={vsPerson} />
</Icon>
<Text>Complex Ccc with text that should be truncated</Text>
</Item>
<Item textValue="Complex Ccc with Description">
<Icon slot="image">
<FontAwesomeIcon icon={vsPerson} />
</Icon>
<Text>Complex Ccc with text that should be truncated</Text>
<Text slot="description">Description</Text>
</Item>
</ListView>
</LabeledFlexContainer>

<Flex gridColumn="span 3" gap={14}>
<Checkbox
checked={showIcons}
onChange={e => setShowIcons(e.currentTarget.checked)}
>
Show Ions
</Checkbox>
</Flex>

<LabeledFlexContainer label="Controlled">
<ListViewNormalized
aria-label="Controlled"
density={density}
normalizedItems={itemsWithIcons}
selectionMode="multiple"
selectedKeys={selectedKeys}
showItemIcons={showIcons}
onChange={onChange}
/>
</LabeledFlexContainer>
</Grid>
</div>
);
Expand Down
Loading

0 comments on commit cb13c60

Please sign in to comment.