-
Notifications
You must be signed in to change notification settings - Fork 201
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
237 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import 'Frontend/demo/init'; // hidden-source-line | ||
import '@vaadin/grid'; | ||
import '@vaadin/grid/vaadin-grid-selection-column.js'; | ||
import { html, LitElement } from 'lit'; | ||
import { customElement, query, state } from 'lit/decorators.js'; | ||
import { getPeople } from 'Frontend/demo/domain/DataService'; | ||
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person'; | ||
import { applyTheme } from 'Frontend/generated/theme'; | ||
import { Grid } from '@vaadin/grid'; | ||
|
||
// tag::snippet[] | ||
@customElement('grid-range-selection') | ||
export class Example extends LitElement { | ||
protected override createRenderRoot() { | ||
const root = super.createRenderRoot(); | ||
// Apply custom theme (only supported if your app uses one) | ||
applyTheme(root); | ||
return root; | ||
} | ||
|
||
@query('vaadin-grid') | ||
private grid!: Grid<Person>; | ||
|
||
@state() | ||
private items: Person[] = []; | ||
|
||
@state() | ||
private startItem!: Person; | ||
|
||
protected override async firstUpdated() { | ||
const { people } = await getPeople(); | ||
this.items = people; | ||
} | ||
|
||
onItemToggle(event: GridItemToggleEvent<Person>) { | ||
const { item, selected, shiftKey } = event.detail; | ||
|
||
// If the anchor point isn't set, set it to the current item | ||
this.startItem ??= item; | ||
|
||
if (shiftKey) { | ||
// Calculcate the range of items between the anchor point and | ||
// the current item | ||
const startIndex = this.items.indexOf(this.startItem); | ||
const endIndex = this.items.indexOf(item); | ||
const rangeItems = this.items.slice( | ||
Math.min(startIndex, endIndex), | ||
Math.max(startIndex, endIndex) + 1 | ||
); | ||
|
||
// Update the selection state of the items within the range | ||
// based on the state of the current item | ||
const selectedItems = new Set(this.grid.selectedItems); | ||
rangeItems.forEach((rangeItem) => { | ||
if (selected) { | ||
selectedItems.add(rangeItem); | ||
} else { | ||
selectedItems.delete(rangeItem); | ||
} | ||
}); | ||
this.grid.selectedItems = [...selectedItems]; | ||
} | ||
|
||
// Update the anchor point to the current item | ||
this.startItem = item; | ||
} | ||
|
||
protected override render() { | ||
return html` | ||
<vaadin-grid .items="${this.items}" @item-toggle="${this.onItemToggle}"> | ||
<vaadin-grid-selection-column></vaadin-grid-selection-column> | ||
<vaadin-grid-column path="firstName"></vaadin-grid-column> | ||
<vaadin-grid-column path="lastName"></vaadin-grid-column> | ||
<vaadin-grid-column path="email"></vaadin-grid-column> | ||
</vaadin-grid> | ||
`; | ||
} | ||
} | ||
// end::snippet[] |
68 changes: 68 additions & 0 deletions
68
frontend/demo/component/grid/react/grid-range-selection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { reactExample } from 'Frontend/demo/react-example'; // hidden-source-line | ||
import React, { useEffect, useRef } from 'react'; | ||
import { useSignals } from '@preact/signals-react/runtime'; // hidden-source-line | ||
import { useSignal } from '@vaadin/hilla-react-signals'; | ||
import { Grid } from '@vaadin/react-components/Grid.js'; | ||
import { GridColumn } from '@vaadin/react-components/GridColumn.js'; | ||
import { GridSelectionColumn } from '@vaadin/react-components/GridSelectionColumn.js'; | ||
import { getPeople } from 'Frontend/demo/domain/DataService'; | ||
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person'; | ||
|
||
function Example() { | ||
useSignals(); // hidden-source-line | ||
const items = useSignal<Person[]>([]); | ||
const selectedItems = useSignal<Person[]>([]); | ||
const startItem = useRef<Person>(); | ||
|
||
useEffect(() => { | ||
getPeople().then(({ people }) => { | ||
items.value = people; | ||
}); | ||
}, []); | ||
|
||
const handleItemToggle = (event: CustomEvent) => { | ||
const { item, selected, shiftKey } = event.detail; | ||
|
||
// If the anchor point isn't set, set it to the current item | ||
startItem.current ??= item; | ||
|
||
if (shiftKey) { | ||
// Calculcate the range of items between the anchor point and | ||
// the current item | ||
const startIndex = items.value.indexOf(startItem.current!); | ||
const endIndex = items.value.indexOf(item); | ||
const rangeItems = items.value.slice( | ||
Math.min(startIndex, endIndex), | ||
Math.max(startIndex, endIndex) + 1 | ||
); | ||
|
||
// Update the selection state of the items within the range | ||
// based on the state of the current item | ||
const selectedItemsCopy = new Set(selectedItems.value); | ||
rangeItems.forEach((rangeItem) => { | ||
if (selected) { | ||
selectedItemsCopy.add(rangeItem); | ||
} else { | ||
selectedItemsCopy.delete(rangeItem); | ||
} | ||
}); | ||
selectedItems.value = [...selectedItemsCopy]; | ||
} | ||
|
||
// Update the anchor point to the current item | ||
startItem.current = item; | ||
}; | ||
|
||
return ( | ||
// tag::snippet[] | ||
<Grid items={items.value} onItemToggle={handleItemToggle}> | ||
<GridSelectionColumn /> | ||
<GridColumn path="firstName" /> | ||
<GridColumn path="lastName" /> | ||
<GridColumn path="email" /> | ||
</Grid> | ||
// end::snippet[] | ||
); | ||
} | ||
|
||
export default reactExample(Example); // hidden-source-line |
68 changes: 68 additions & 0 deletions
68
src/main/java/com/vaadin/demo/component/grid/GridRangeSelection.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.vaadin.demo.component.grid; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import com.vaadin.demo.domain.Person; | ||
import com.vaadin.flow.component.grid.Grid; | ||
import com.vaadin.flow.component.grid.GridMultiSelectionModel; | ||
import com.vaadin.flow.component.grid.dataview.GridListDataView; | ||
import com.vaadin.flow.component.html.Div; | ||
import com.vaadin.flow.router.Route; | ||
import com.vaadin.demo.DemoExporter; // hidden-source-line | ||
import com.vaadin.demo.domain.DataService; | ||
|
||
@Route("grid-range-selection") | ||
public class GridRangeSelection extends Div { | ||
|
||
private Person startItem; | ||
|
||
public GridRangeSelection() { | ||
Grid<Person> grid = new Grid<>(Person.class, false); | ||
grid.addColumn(Person::getFirstName).setHeader("First name"); | ||
grid.addColumn(Person::getLastName).setHeader("Last name"); | ||
grid.addColumn(Person::getEmail).setHeader("Email"); | ||
|
||
List<Person> people = DataService.getPeople(); | ||
grid.setItems(people); | ||
|
||
// tag::snippet[] | ||
grid.setSelectionMode(Grid.SelectionMode.MULTI); | ||
((GridMultiSelectionModel<Person>) grid.getSelectionModel()) | ||
.addClientItemToggleListener(event -> { | ||
Person item = event.getItem(); | ||
|
||
startItem = startItem != null ? startItem : item; | ||
if (event.isShiftKey()) { | ||
Set<Person> range = fetchItemsRange(grid, startItem, | ||
item); | ||
if (event.isSelected()) { | ||
grid.asMultiSelect().select(range); | ||
} else { | ||
grid.asMultiSelect().deselect(range); | ||
} | ||
} | ||
startItem = item; | ||
}); | ||
// end::snippet[] | ||
|
||
add(grid); | ||
} | ||
|
||
// tag::snippet2[] | ||
private <T> Set<T> fetchItemsRange(Grid<T> grid, T startItem, T endItem) { | ||
GridListDataView<T> dataView = grid.getListDataView(); | ||
int startIndex = dataView.getItemIndex(startItem).get(); | ||
int endIndex = dataView.getItemIndex(endItem).get(); | ||
|
||
return dataView.getItems().skip(Math.min(startIndex, endIndex)) | ||
.limit(Math.abs(startIndex - endIndex) + 1) | ||
.collect(Collectors.toSet()); | ||
} | ||
// end::snippet2[] | ||
|
||
public static class Exporter // hidden-source-line | ||
extends DemoExporter<GridRangeSelection> { // hidden-source-line | ||
} // hidden-source-line | ||
} |