Skip to content

Commit 750de74

Browse files
committed
feat: implement row actions for EasyGrid
1 parent 587aade commit 750de74

77 files changed

Lines changed: 4600 additions & 46 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

FEATURE_ROW_ACTIONS.md

Lines changed: 177 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,221 @@
1-
# Feature: Row Actions (Roadmap)
2-
3-
> **Note:** This feature is planned for a future iteration and is not yet implemented.
4-
51
# Feature: Row Actions
62

7-
Row actions are buttons or menu items displayed in a dedicated actions column, created and managed by `EasyGrid` on the wrapped grid.
3+
Row actions are buttons or menu items presented by `EasyGrid` on the wrapped grid — as inline buttons, an overflow menu, or the grid's right-click context menu.
84

95
## API
106

117
### `EasyGrid<T>` methods
128

9+
#### Adding row actions
10+
11+
All `addRowAction` overloads register a new action and return an `EasyRowAction<T>` that can be further configured via its fluent API (visibility, enablement, tooltip, confirmation). How the action is presented depends on the current `RowActionsStyle`.
12+
13+
```java
14+
EasyRowAction<T> addRowAction(String label, SerializableConsumer<T> handler);
15+
```
16+
Adds a text-only action button. When the user clicks it, `handler` is invoked with the corresponding row item.
17+
18+
---
19+
20+
```java
21+
EasyRowAction<T> addRowAction(String label, Icon icon, SerializableConsumer<T> handler);
22+
EasyRowAction<T> addRowAction(String label, IconFactory iconFactory, SerializableConsumer<T> handler);
23+
```
24+
Adds an action button with both a label and a static icon rendered alongside it. `VaadinIcon` implements `IconFactory` and can be passed directly.
25+
26+
---
27+
28+
```java
29+
EasyRowAction<T> addRowAction(Icon icon, SerializableConsumer<T> handler);
30+
EasyRowAction<T> addRowAction(IconFactory iconFactory, SerializableConsumer<T> handler);
31+
```
32+
Adds an icon-only action button with no visible label. Useful when space is limited and the icon alone conveys the action.
33+
34+
---
35+
1336
```java
14-
// Add an action button (label + icon)
15-
EasyRowAction<T> addRowAction(String label, VaadinIcon icon, SerializableConsumer<T> handler);
37+
<ICON extends AbstractIcon<ICON>>
38+
EasyRowAction<T> addRowAction(ValueProvider<T, ICON> iconProvider, SerializableConsumer<T> handler);
39+
40+
<ICON extends AbstractIcon<ICON>>
41+
EasyRowAction<T> addRowAction(String label, ValueProvider<T, ICON> iconProvider, SerializableConsumer<T> handler);
42+
```
43+
Adds an action button whose icon is resolved per row by calling `iconProvider` with the row item, so different rows may show a different icon for the same action. The optional `label` is shown alongside the dynamic icon.
1644

17-
// Add an action button with a theme variant
18-
EasyRowAction<T> addRowAction(String label, VaadinIcon icon, ButtonVariant variant, SerializableConsumer<T> handler);
45+
---
1946

20-
// Render all actions as a context menu (overflow menu) instead of inline buttons
21-
void setRowActionsAsMenu(boolean asMenu);
47+
#### Rendering mode
2248

23-
// Access the underlying Grid.Column for header, width, freezing, etc.
49+
```java
50+
void setRowActionsStyle(RowActionsStyle style);
51+
```
52+
Controls how row actions are presented. `RowActionsStyle` has three values:
53+
54+
- `INLINE_BUTTONS` (the default) — each action is rendered as a separate inline button in the actions column.
55+
- `DROPDOWN` — all actions are presented as items in an overflow menu, opened by a single trigger button per row that is hosted in the actions column.
56+
- `CONTEXT_MENU` — all actions are presented as items in the grid's right-click context menu; no actions column is created.
57+
58+
---
59+
60+
#### Accessing the actions column
61+
62+
```java
2463
Grid.Column<T> getActionsColumn();
2564
```
65+
Returns the `Grid.Column<T>` backing the actions column, allowing the caller to configure its header text, width, freeze position, or any other `Grid.Column` property. For the column-based styles the column is created automatically when the actions are first rendered.
66+
67+
Returns a non-`null` column for the `INLINE_BUTTONS` and `DROPDOWN` styles (the latter hosts its overflow menu trigger button in the column). For the `CONTEXT_MENU` style the actions are not hosted in a column, so this method returns `null`.
68+
69+
---
70+
71+
#### Default theme variants
72+
73+
```java
74+
void setDefaultRowActionVariants(ButtonVariant... variants);
75+
```
76+
Sets the Vaadin `ButtonVariant`s applied by default to every action button created *after* this call; actions added earlier are unaffected. The built-in default is `LUMO_TERTIARY_INLINE`. Pass no arguments (or `null`) to clear the defaults. Individual actions can still add their own variants via `EasyRowAction.addThemeVariants(...)`.
77+
78+
---
79+
80+
#### Custom renderer
81+
82+
```java
83+
void setRowActionsRenderer(RowActionsRenderer<T> renderer);
84+
```
85+
Replaces the strategy that turns the registered actions into UI. The built-in renderers (selected via `setRowActionsStyle`) cover the common cases; supply a custom `RowActionsRenderer<T>` to present actions another way. The previous renderer is cleaned up and a rebuild is scheduled.
86+
87+
---
88+
89+
#### Refreshing after configuration changes
90+
91+
```java
92+
void refreshRowActions();
93+
```
94+
Schedules a rebuild of the row actions on the next server response. The fluent `EasyRowAction` methods (`visibleWhen`, `enabledWhen`, `tooltip`, `withConfirmation`) already trigger this automatically, so an explicit call is only needed after changing an action's attribute or property, which are not applied automatically.
95+
96+
---
2697

2798
### `EasyRowAction<T>`
2899

100+
All mutator methods return `this` to support method chaining.
101+
102+
#### Visibility
103+
104+
```java
105+
EasyRowAction<T> visibleWhen(SerializablePredicate<T> predicate);
106+
```
107+
Makes the action conditionally visible on a per-row basis. The predicate is evaluated against each row's item when the row is rendered; the action button is hidden for rows where the predicate returns `false`.
108+
109+
---
110+
111+
#### Enablement
112+
113+
```java
114+
EasyRowAction<T> enabledWhen(SerializablePredicate<T> predicate);
115+
```
116+
Makes the action conditionally enabled on a per-row basis. The action button is shown in a disabled state (visible but not clickable) for rows where the predicate returns `false`.
117+
118+
---
119+
120+
#### Tooltip
121+
122+
```java
123+
EasyRowAction<T> tooltip(String tooltip);
124+
```
125+
Sets a fixed tooltip displayed when the user hovers over the action button.
126+
127+
```java
128+
EasyRowAction<T> tooltip(ValueProvider<T, String> tooltipProvider);
129+
```
130+
Sets a per-row tooltip resolved by calling `tooltipProvider` with the row item, so different rows may show different tooltip text for the same action.
131+
132+
---
133+
134+
#### Confirmation
135+
29136
```java
30-
public class EasyRowAction<T> {
31-
// Conditional visibility
32-
EasyRowAction<T> withVisibleWhen(SerializablePredicate<T> predicate);
137+
EasyRowAction<T> withConfirmation(String message);
138+
EasyRowAction<T> withConfirmation(String title, String message);
139+
```
140+
Intercepts button clicks and presents a confirmation dialog before invoking the action handler. The handler is only called if the user confirms. `message` is the confirmation prompt shown to the user; the optional `title` sets the dialog heading.
33141

34-
// Conditional enablement
35-
EasyRowAction<T> withEnabledWhen(SerializablePredicate<T> predicate);
142+
---
36143

37-
// Tooltip
38-
EasyRowAction<T> withTooltip(String tooltip);
39-
EasyRowAction<T> withTooltip(SerializableFunction<T, String> tooltipProvider);
144+
#### Styling and theme variants
40145

41-
// Confirmation dialog before executing the action
42-
EasyRowAction<T> withConfirmation(String message);
43-
EasyRowAction<T> withConfirmation(String title, String message);
44-
}
146+
`EasyRowAction<T>` implements `HasStyle` and `HasThemeVariant<ButtonVariant>`, so the action's button can be styled and themed directly:
147+
148+
```java
149+
action.addClassName("danger");
150+
action.getStyle().set("font-weight", "bold");
151+
action.addThemeVariants(ButtonVariant.LUMO_ERROR);
45152
```
153+
These are forwarded onto the rendered button. Unlike the fluent mutators above, style and theme-variant changes made after the grid has already rendered are **not** applied automatically — call `easyGrid.refreshRowActions()` afterwards to make them visible.
154+
155+
---
156+
157+
#### Removal
158+
159+
```java
160+
void remove();
161+
```
162+
Removes this action. The row actions are re-rendered on the next server response, refreshing the grid's data view so the change becomes visible without an explicit data refresh. If the action has already been removed, this call is a no-op. After removal the `EasyRowAction` reference is considered dead and cannot be re-added; call `addRowAction` again to create a new action.
163+
164+
---
46165

47166
## Usage
48167

49168
```java
50-
// Inline action buttons
169+
// Label + VaadinIcon (VaadinIcon implements IconFactory)
51170
easyGrid.addRowAction("Edit", VaadinIcon.EDIT, person -> {
52171
editPerson(person);
53172
});
54173

55-
easyGrid.addRowAction("Delete", VaadinIcon.TRASH, ButtonVariant.LUMO_ERROR, person -> {
174+
easyGrid.addRowAction("Delete", VaadinIcon.TRASH, person -> {
56175
personService.delete(person);
57176
easyGrid.getDataProvider().refreshAll();
58177
}).withConfirmation("Are you sure you want to delete this person?");
59178

60-
// Actions as a context menu (overflow menu) instead of inline buttons
61-
easyGrid.setRowActionsAsMenu(true);
179+
// Label only
180+
easyGrid.addRowAction("Details", person -> showDetails(person));
181+
182+
// Per-row dynamic icon
183+
easyGrid.addRowAction(
184+
person -> person.isActive() ? VaadinIcon.CHECK.create() : VaadinIcon.CLOSE.create(),
185+
person -> toggleActive(person)
186+
);
187+
188+
// Actions as an overflow menu instead of inline buttons
189+
easyGrid.setRowActionsStyle(RowActionsStyle.DROPDOWN);
190+
191+
// ...or as the grid's right-click context menu (no actions column)
192+
easyGrid.setRowActionsStyle(RowActionsStyle.CONTEXT_MENU);
62193

63194
// Conditional visibility
64195
easyGrid.addRowAction("Activate", VaadinIcon.CHECK, person -> {
65196
personService.activate(person);
66-
}).withVisibleWhen(person -> !person.isActive());
197+
}).visibleWhen(person -> !person.isActive());
67198

68199
easyGrid.addRowAction("Deactivate", VaadinIcon.CLOSE, person -> {
69200
personService.deactivate(person);
70-
}).withVisibleWhen(person -> person.isActive());
201+
}).visibleWhen(person -> person.isActive());
202+
203+
// Removing an action
204+
EasyRowAction<Person> adminAction = easyGrid.addRowAction("Purge", VaadinIcon.TRASH, item -> purge(item));
205+
// later:
206+
adminAction.remove();
207+
208+
// Default theme variants applied to every action added afterwards
209+
easyGrid.setDefaultRowActionVariants(ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_TERTIARY);
210+
211+
// Style or theme an individual action's button
212+
easyGrid.addRowAction("Reset", person -> reset(person))
213+
.addClassName("warning");
71214

72-
// Configure the actions column via the underlying Grid.Column
215+
// Configure the actions column via the underlying Grid.Column.
216+
// Available for the INLINE_BUTTONS and DROPDOWN styles. With the CONTEXT_MENU
217+
// style there is no actions column and getActionsColumn() returns null, so the
218+
// chain below would throw a NullPointerException.
73219
easyGrid.getActionsColumn()
74220
.setHeader("Actions")
75221
.setWidth("150px")

pom.xml

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<url>https://www.flowingcode.com/en/open-source/</url>
1313

1414
<properties>
15-
<vaadin.version>24.10.6</vaadin.version>
15+
<vaadin.version>24.10.7</vaadin.version>
1616
<selenium.version>4.44.0</selenium.version>
1717
<maven.compiler.source>17</maven.compiler.source>
1818
<maven.compiler.target>17</maven.compiler.target>
@@ -70,6 +70,17 @@
7070
<artifactId>vaadin-core</artifactId>
7171
<optional>true</optional>
7272
</dependency>
73+
<dependency>
74+
<groupId>com.flowingcode.vaadin</groupId>
75+
<artifactId>json-migration-helper</artifactId>
76+
<version>0.9.4</version>
77+
</dependency>
78+
<dependency>
79+
<groupId>com.flowingcode.vaadin.test</groupId>
80+
<artifactId>testbench-rpc</artifactId>
81+
<version>1.5.0</version>
82+
<scope>test</scope>
83+
</dependency>
7384
<dependency>
7485
<groupId>org.projectlombok</groupId>
7586
<artifactId>lombok</artifactId>
@@ -603,19 +614,74 @@
603614
</build>
604615
</profile>
605616

617+
<profile>
618+
<id>dance</id>
619+
<build>
620+
<plugins>
621+
<plugin>
622+
<groupId>org.apache.maven.plugins</groupId>
623+
<artifactId>maven-clean-plugin</artifactId>
624+
<configuration>
625+
<filesets>
626+
<fileset>
627+
<directory>${project.basedir}</directory>
628+
<includes>
629+
<include>package.json</include>
630+
<include>package-lock.json</include>
631+
<include>tsconfig.json</include>
632+
<include>tsconfig.json.*</include>
633+
<include>types.d.ts</include>
634+
<include>types.d.ts.*</include>
635+
<include>vite.config.ts</include>
636+
<include>vite.generated.ts</include>
637+
<include>webpack.config.js</include>
638+
<include>webpack.generated.js</include>
639+
</includes>
640+
</fileset>
641+
<fileset>
642+
<directory>${project.basedir}/frontend</directory>
643+
<includes>
644+
<include>index.html</include>
645+
</includes>
646+
</fileset>
647+
<fileset>
648+
<directory>${project.basedir}/frontend/generated</directory>
649+
</fileset>
650+
<fileset>
651+
<directory>${project.basedir}/node_modules</directory>
652+
</fileset>
653+
<fileset>
654+
<directory>${project.basedir}/src/main/bundles</directory>
655+
</fileset>
656+
<fileset>
657+
<directory>${project.basedir}/src/main/dev-bundle</directory>
658+
</fileset>
659+
</filesets>
660+
</configuration>
661+
</plugin>
662+
</plugins>
663+
</build>
664+
</profile>
665+
606666
<profile>
607667
<id>v25</id>
608668
<properties>
609669
<maven.compiler.source>21</maven.compiler.source>
610670
<maven.compiler.target>21</maven.compiler.target>
611-
<vaadin.version>25.1.3</vaadin.version>
671+
<vaadin.version>25.1.7</vaadin.version>
612672
</properties>
613673
<dependencies>
614674
<dependency>
615675
<groupId>com.vaadin</groupId>
616676
<artifactId>vaadin-dev</artifactId>
617677
<optional>true</optional>
618678
</dependency>
679+
<dependency>
680+
<groupId>org.ow2.asm</groupId>
681+
<artifactId>asm</artifactId>
682+
<version>9.8</version>
683+
<optional>true</optional>
684+
</dependency>
619685
<dependency>
620686
<groupId>jakarta.servlet</groupId>
621687
<artifactId>jakarta.servlet-api</artifactId>

src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
*
4040
* @param <T> the bean type
4141
* @param <V> the property value type
42+
* @author Javier Godoy / Flowing Code
4243
*/
4344
@SuppressWarnings("serial")
4445
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)

src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyColumn.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
*
3939
* @param <T> the grid bean type
4040
* @param <V> the column value type
41+
* @author Javier Godoy / Flowing Code
4142
*/
4243
@SuppressWarnings("serial")
4344
public final class EasyColumn<T, V> implements IEasyGridColumn<T, V>, Serializable {

src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGrid.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* internally. This is the standard entry point for using the Easy Grid Add-on.
2929
*
3030
* @param <T> the grid bean type
31+
* @author Javier Godoy / Flowing Code
3132
*/
3233
@SuppressWarnings("serial")
3334
public class EasyGrid<T> extends EasyGridWrapper<T, Grid<T>> {

src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGridComposite.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
*
4444
* @param <T> the grid bean type
4545
* @param <GRID> the concrete {@code Grid} subtype being wrapped
46+
* @author Javier Godoy / Flowing Code
4647
*/
4748
@SuppressWarnings("serial")
4849
class EasyGridComposite<T, GRID extends Grid<T>> extends Composite<GRID>

0 commit comments

Comments
 (0)