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

feat: ListView actions #1968

Merged
merged 6 commits into from
May 15, 2024
Merged

Conversation

bmingles
Copy link
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
@@ -28,21 +28,21 @@
}

.dh-list-view-wrapper-density-compact {
svg[class*='react-spectrum-ListViewItem-thumbnail'] {
svg[class*='spectrum-Icon'] {
Copy link
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
Copy link
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
Copy link
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
Copy link
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
9 checks passed
@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