Skip to content

Commit cb13c60

Browse files
bminglesdsmmcken
andauthored
feat: ListView + Picker - Item icon support (#1959)
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>
1 parent fb63be1 commit cb13c60

33 files changed

Lines changed: 1368 additions & 564 deletions

packages/code-studio/src/styleguide/ListViews.tsx

Lines changed: 184 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1-
import React, { useCallback, useState } from 'react';
1+
import React, { ChangeEvent, ReactNode, useCallback, useState } from 'react';
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3+
import type { StyleProps } from '@react-types/shared';
24
import {
35
Grid,
46
Icon,
57
Item,
68
ListView,
9+
ListViewNormalized,
710
ItemKey,
811
Text,
12+
Flex,
13+
Checkbox,
14+
ListViewProps,
15+
RadioGroup,
16+
RadioItem,
17+
useSpectrumThemeProvider,
918
} from '@deephaven/components';
1019
import { vsAccount, vsPerson } from '@deephaven/icons';
11-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
20+
import { LIST_VIEW_ROW_HEIGHTS } from '@deephaven/utils';
1221
import { generateNormalizedItems, sampleSectionIdAndClasses } from './utils';
1322

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

1726
function AccountIllustration(): JSX.Element {
1827
return (
@@ -25,11 +34,63 @@ function AccountIllustration(): JSX.Element {
2534
);
2635
}
2736

37+
interface LabeledProps extends StyleProps {
38+
label: string;
39+
direction?: 'row' | 'column';
40+
children: ReactNode;
41+
}
42+
43+
const LABELED_FLEX_CONTAINER_HEIGHTS = {
44+
gap: 10,
45+
label: {
46+
medium: 21,
47+
large: 25.5,
48+
},
49+
};
50+
51+
function LabeledFlexContainer({
52+
label,
53+
direction = 'column',
54+
children,
55+
...styleProps
56+
}: LabeledProps) {
57+
return (
58+
<Flex
59+
// eslint-disable-next-line react/jsx-props-no-spreading
60+
{...styleProps}
61+
direction={direction}
62+
gap={LABELED_FLEX_CONTAINER_HEIGHTS.gap}
63+
>
64+
<Text>{label}</Text>
65+
{children}
66+
</Flex>
67+
);
68+
}
69+
2870
export function ListViews(): JSX.Element {
71+
const { scale } = useSpectrumThemeProvider();
2972
const [selectedKeys, setSelectedKeys] = useState<'all' | Iterable<ItemKey>>(
3073
[]
3174
);
3275

76+
const [density, setDensity] = useState<ListViewProps['density']>('compact');
77+
78+
// Calculate the height of the single child example
79+
const singleChildExampleHeight =
80+
LABELED_FLEX_CONTAINER_HEIGHTS.label[scale] +
81+
LABELED_FLEX_CONTAINER_HEIGHTS.gap +
82+
2 + // listview border
83+
LIST_VIEW_ROW_HEIGHTS[density ?? 'compact'][scale];
84+
85+
const onDensityChange = useCallback(
86+
(event: ChangeEvent<HTMLInputElement>) => {
87+
setDensity(event.currentTarget.value as ListViewProps['density']);
88+
},
89+
[]
90+
);
91+
92+
const [showIcons, setShowIcons] = useState(true);
93+
3394
const onChange = useCallback((keys: 'all' | Iterable<ItemKey>): void => {
3495
setSelectedKeys(keys);
3596
}, []);
@@ -39,82 +100,128 @@ export function ListViews(): JSX.Element {
39100
<div {...sampleSectionIdAndClasses('list-views')}>
40101
<h2 className="ui-title">List View</h2>
41102

42-
<Grid columnGap={14} height="size-6000">
43-
<Text>Single Child</Text>
44-
<ListView
45-
density="compact"
46-
gridRow="2"
47-
aria-label="Single Child"
48-
selectionMode="multiple"
49-
>
50-
<Item>Aaa</Item>
51-
</ListView>
52-
53-
<label>Icons</label>
54-
<ListView
55-
gridRow="2"
56-
aria-label="Icon"
57-
density="compact"
58-
selectionMode="multiple"
59-
>
60-
<Item textValue="Item with icon A">
61-
<AccountIllustration />
62-
<Text>Item with icon A</Text>
63-
</Item>
64-
<Item textValue="Item with icon B">
65-
<AccountIllustration />
66-
<Text>Item with icon B</Text>
67-
</Item>
68-
<Item textValue="Item with icon C">
69-
<AccountIllustration />
70-
<Text>Item with icon C</Text>
71-
</Item>
72-
<Item textValue="Item with icon D">
73-
<AccountIllustration />
74-
<Text>Item with icon D with overflowing content</Text>
75-
</Item>
76-
</ListView>
77-
78-
<label>Mixed Children Types</label>
79-
<ListView
80-
gridRow="2"
81-
aria-label="Mixed Children Types"
82-
density="compact"
83-
maxWidth="size-2400"
84-
selectionMode="multiple"
85-
defaultSelectedKeys={[999, 444]}
103+
<Grid
104+
gap={14}
105+
height="size-6000"
106+
columns="1fr 1fr 1fr"
107+
rows={`auto minmax(${singleChildExampleHeight}px, auto) 1fr auto 1fr`}
108+
>
109+
<LabeledFlexContainer
110+
direction="row"
111+
label="Density"
112+
gridColumn="span 3"
86113
>
87-
{/* eslint-disable react/jsx-curly-brace-presence */}
88-
{'String 1'}
89-
{'String 2'}
90-
{'String 3'}
91-
{''}
92-
{'Some really long text that should get truncated'}
93-
{/* eslint-enable react/jsx-curly-brace-presence */}
94-
{444}
95-
{999}
96-
{true}
97-
{false}
98-
<Item>Item Aaa</Item>
99-
<Item>Item Bbb</Item>
100-
<Item textValue="Complex Ccc">
101-
<Icon slot="image">
102-
<FontAwesomeIcon icon={vsPerson} />
103-
</Icon>
104-
<Text>Complex Ccc with text that should be truncated</Text>
105-
</Item>
106-
</ListView>
107-
108-
<label>Controlled</label>
109-
<ListView
110-
gridRow="2"
111-
aria-label="Controlled"
112-
selectionMode="multiple"
113-
selectedKeys={selectedKeys}
114-
onChange={onChange}
114+
<RadioGroup value={density} onChange={onDensityChange}>
115+
<RadioItem value="compact">Compact</RadioItem>
116+
<RadioItem value="regular">Regular</RadioItem>
117+
<RadioItem value="spacious">Spacious</RadioItem>
118+
</RadioGroup>
119+
</LabeledFlexContainer>
120+
121+
<LabeledFlexContainer
122+
label="Single Child"
123+
gridColumn="span 3"
124+
height="100%"
115125
>
116-
{itemsSimple}
117-
</ListView>
126+
<ListView
127+
density={density}
128+
aria-label="Single Child"
129+
selectionMode="multiple"
130+
>
131+
<Item textValue="Aaa">Aaa</Item>
132+
</ListView>
133+
</LabeledFlexContainer>
134+
135+
<LabeledFlexContainer label="Icons" gridColumn="span 2">
136+
<ListView
137+
aria-label="Icon"
138+
density={density}
139+
selectionMode="multiple"
140+
>
141+
<Item textValue="Item with icon A">
142+
<AccountIllustration />
143+
<Text>Item with icon A</Text>
144+
</Item>
145+
<Item textValue="Item with icon B">
146+
<AccountIllustration />
147+
<Text>Item with icon B</Text>
148+
</Item>
149+
<Item textValue="Item with icon C">
150+
<AccountIllustration />
151+
<Text>Item with icon C</Text>
152+
</Item>
153+
<Item textValue="Item with icon D">
154+
<AccountIllustration />
155+
<Text>Item with icon D with overflowing content</Text>
156+
</Item>
157+
<Item textValue="Item with icon E">
158+
<AccountIllustration />
159+
<Text>Item with icon E</Text>
160+
</Item>
161+
</ListView>
162+
</LabeledFlexContainer>
163+
164+
<LabeledFlexContainer label="Mixed Children Types">
165+
<ListView
166+
aria-label="Mixed Children Types"
167+
density={density}
168+
maxWidth="size-2400"
169+
selectionMode="multiple"
170+
defaultSelectedKeys={['999', 444]}
171+
>
172+
{/* eslint-disable react/jsx-curly-brace-presence */}
173+
{'String 1'}
174+
{'String 2'}
175+
{'String 3'}
176+
{''}
177+
{'Some really long text that should get truncated'}
178+
{/* eslint-enable react/jsx-curly-brace-presence */}
179+
{444}
180+
{999}
181+
{true}
182+
{false}
183+
<Item textValue="Item Aaa">Item Aaa</Item>
184+
<Item textValue="Item Bbb">Item Bbb</Item>
185+
<Item textValue="Item with Description">
186+
<Text>Item with Description</Text>
187+
<Text slot="description">Description</Text>
188+
</Item>
189+
<Item textValue="Complex Ccc">
190+
<Icon slot="image">
191+
<FontAwesomeIcon icon={vsPerson} />
192+
</Icon>
193+
<Text>Complex Ccc with text that should be truncated</Text>
194+
</Item>
195+
<Item textValue="Complex Ccc with Description">
196+
<Icon slot="image">
197+
<FontAwesomeIcon icon={vsPerson} />
198+
</Icon>
199+
<Text>Complex Ccc with text that should be truncated</Text>
200+
<Text slot="description">Description</Text>
201+
</Item>
202+
</ListView>
203+
</LabeledFlexContainer>
204+
205+
<Flex gridColumn="span 3" gap={14}>
206+
<Checkbox
207+
checked={showIcons}
208+
onChange={e => setShowIcons(e.currentTarget.checked)}
209+
>
210+
Show Ions
211+
</Checkbox>
212+
</Flex>
213+
214+
<LabeledFlexContainer label="Controlled">
215+
<ListViewNormalized
216+
aria-label="Controlled"
217+
density={density}
218+
normalizedItems={itemsWithIcons}
219+
selectionMode="multiple"
220+
selectedKeys={selectedKeys}
221+
showItemIcons={showIcons}
222+
onChange={onChange}
223+
/>
224+
</LabeledFlexContainer>
118225
</Grid>
119226
</div>
120227
);

0 commit comments

Comments
 (0)