Skip to content

feat: ListView actions#1968

Merged
bmingles merged 6 commits intodeephaven:mainfrom
bmingles:1913-list-view-actions
May 15, 2024
Merged

feat: ListView actions#1968
bmingles merged 6 commits intodeephaven:mainfrom
bmingles:1913-list-view-actions

Conversation

@bmingles
Copy link
Copy Markdown
Contributor

@bmingles bmingles commented Apr 26, 2024

  • Wrapper components for ActionGroup and ActionMenu to support primitive items
  • ListActionGroup and ListActionMenu components to support providing actions prop to ListView
  • ListView actions prop support

The branch in this PR can be used to see this in action. I also published an alpha this branch (0.77.1-alpha-listview-actions.4) to make types work in plugins

Example of standalone ui.action_group and ui.action_menu

from deephaven import ui


@ui.component
def action_group():
    action, on_action = ui.use_state("")
    selected_keys, set_selected_keys = ui.use_state(['Aaa'])

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

    return 'Action Group', (" - " if action else "") + action, ui.action_group(
        ui.item(
            ui.icon('vsAccount'),
            'Aaa',
            text_value="Aaa"
        ),
        'Bbb',
        'Ccc',
        selection_mode="multiple",
        selected_keys=selected_keys,
        on_action=on_action,
        on_change=set_selected_keys,
    ), selection_text,
    

ag = action_group()


@ui.component
def action_menu():
    action, on_action = ui.use_state("")

    return 'Action Menu', (" - " if action else "") + action, ui.action_menu(
        ui.item(
            ui.icon('vsAccount'),
            'Aaa',
            text_value="Aaa"
        ),
        'Bbb',
        'Ccc',
        on_action=on_action,
        align_self="start"
    )
    

am = action_menu()

Example showing actions in ui.list_view with different densities

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

initial_row_count=2000
icon_names = ['vsAccount']

columns = [
    "Id=new Integer(i)",
    "Display=new String(`Display `+i)",
    "Description=new String(`Description `+i)",
    "Icon=(String) icon_names[0]"
]
# Tables with initial row count of 200 that adds a row every second
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)


#### Component definitions ####

@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(data, density):
    value, set_value = ui.use_state([2, 4, 5])

    # Action Groups

    ag_action, set_ag_action = ui.use_state(['', ''])
    on_ag_action=ui.use_callback(lambda a,i : set_ag_action([a,str(i)]), [])

    lv_actions = labeled_lv(
        "Actions (text only)",
        data,
        density=density,
        max_height=5000,
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View",
        on_change=set_value,
        selected_keys=value,
        actions=ui.list_action_group(
            'Edit',
            'Delete',
            on_action=on_ag_action,
        ),
    )

    lv_actions_icon = labeled_lv(
        "Actions (icon only)",
        data,
        density=density,
        max_height=5000,
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View",
        on_change=set_value,
        selected_keys=value,
        actions=ui.list_action_group(
            ui.item(
                ui.icon('vsEdit'),
                ui.text('Edit'),
                key='Edit',
            ),
            ui.item(
                ui.icon('vsTrash'),
                ui.text('Delete'),
                key='Delete'
            ),
            max_width=80,
            button_label_behavior="collapse",
            overflow_mode="collapse",
            on_action=on_ag_action,
        ),
    )
    
    action_group_text = ui.text("Action: " + ag_action[0] + ", Item: " + ag_action[1])

    # Action Menus

    am_action, set_am_action = ui.use_state(['', ''])
    on_am_action=ui.use_callback(lambda a,i : set_am_action([a,str(i)]), [])

    lv_action_menu = labeled_lv(
        "Action Menu (text only)",
        data,
        density=density,
        max_height=5000,
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View",
        selected_keys=value,
        on_change=set_value,
        actions=ui.list_action_menu(
            'Edit',
            'Delete',
            on_action=on_am_action,
        ),
    )

    lv_action_menu2 = labeled_lv(
        "Action Menu (icons)",
        data,
        density=density,
        max_height=5000,
        key_column="Id",
        label_column="Display",
        icon_column="Icon",
        aria_label="List View",
        selected_keys=value,
        on_change=set_value,
        actions=ui.list_action_menu(
             ui.item(
                ui.icon('vsEdit'),
                ui.text('Edit'),
                key='Edit',
                text_value="Edit"
            ),
            ui.item(
                ui.icon('vsTrash'),
                ui.text('Delete'),
                key='Delete',
                text_value="Delete"
            ),
            on_action=on_am_action,
        ),
    )

    action_menu_text = ui.text("Action: " + am_action[0] + ", Item: " + am_action[1])

    return ui.flex(
        ui.flex(
            lv_actions,
            lv_actions_icon,
            direction="row",
        ),
        action_group_text,
        ui.flex(
            lv_action_menu,
            lv_action_menu2,
            direction="row"
        ),
        action_menu_text,
        direction="column",
    )

@ui.component
def examples(data):
    density, set_density = ui.use_state(["COMPACT"])

    return 'Density', ui.action_group(
        'COMPACT', 
        'REGULAR', 
        'SPACIOUS',
        selected_keys=density,
        selection_mode="SINGLE",
        on_change=set_density
    ), ui_list_view_table(data, density[0])

lv_table = examples(column_types)
# lv_table = examples(column_types_ticking)

@bmingles bmingles force-pushed the 1913-list-view-actions branch 3 times, most recently from 5593bd0 to 709d10d Compare May 1, 2024 19:19
@bmingles bmingles linked an issue May 3, 2024 that may be closed by this pull request
@bmingles bmingles force-pushed the 1913-list-view-actions branch 2 times, most recently from dfca0c3 to a69bd62 Compare May 9, 2024 14:00

.dh-list-view-wrapper-density-compact {
svg[class*='react-spectrum-ListViewItem-thumbnail'] {
svg[class*='spectrum-Icon'] {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This includes action group icons so they don't cause item height to change

@bmingles bmingles changed the title feat: DRAFT: ListView actions feat: ListView actions May 9, 2024
@bmingles bmingles marked this pull request as ready for review May 9, 2024 20:19
@bmingles bmingles requested a review from mofojed May 9, 2024 20:19
@bmingles bmingles force-pushed the 1913-list-view-actions branch from 641842e to 2086850 Compare May 9, 2024 22:44
Copy link
Copy Markdown
Member

@mofojed mofojed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the ticking table example, it's adding/removing event listeners each time, and there seems to be some lag (using the lv_table = examples(column_types_ticking) line that's commented out in your example in the PR description). Even with a simple case where it's just a ticking table:

from deephaven import time_table, ui
tt = time_table("PT1s").update("X=i")
my_list_view = ui.list_view(tt)

It seems to be adding the listen again with each tick, which we shouldn't need to do. Should only need to add it once; something isn't being memoized correctly there.

Unsure if caused by this change or not, but please take a look at that before this merges.

@bmingles bmingles force-pushed the 1913-list-view-actions branch from 2086850 to 4af32c5 Compare May 10, 2024 15:18
@bmingles
Copy link
Copy Markdown
Contributor Author

@mofojed

It seems to be adding the listen again with each tick, which we shouldn't need to do. Should only need to add it once; something isn't being memoized correctly there.

Table subscriptions happen inside of useViewportData which is pre-existing. The re-subscription occurs due to callback passed to useTableListener changing whenever the vieportData changes.
https://github.com/deephaven/web-client-ui/blob/main/packages/jsapi-components/src/useViewportData.ts#L122

I've created #2003 to address the underlying issue.

@bmingles bmingles force-pushed the 1913-list-view-actions branch from 4af32c5 to 5ec88af Compare May 14, 2024 20:13
@bmingles
Copy link
Copy Markdown
Contributor Author

@mofojed the PR that fixed the subscriptions has been merged, and this PR is rebase on it, so should see that problem go away now.

@bmingles bmingles requested a review from mofojed May 15, 2024 14:13
@bmingles bmingles merged commit 8e325ec into deephaven:main May 15, 2024
@github-actions github-actions Bot locked and limited conversation to collaborators May 15, 2024
@bmingles bmingles deleted the 1913-list-view-actions branch May 15, 2024 14:57
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ListView - action support

2 participants