Skip to content

Commit 4182d10

Browse files
flangpaodb
authored andcommitted
feat: add radio button selection column to grid
Close #113
1 parent 0ae3ea1 commit 4182d10

File tree

9 files changed

+616
-0
lines changed

9 files changed

+616
-0
lines changed

src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,4 +555,12 @@ public static <T> CheckboxColumn<T> addCheckboxColumn(Grid<T> grid,
555555
public static <T> void toggleSelectAllCheckbox(Grid<T> grid, boolean visible) {
556556
getHelper(grid).lazySelectAllGridHelper.toggleSelectAllCheckbox(visible);
557557
}
558+
559+
private final GridRadioSelectionColumnHelper<T> radioButtonSelectionColumnHelper =
560+
new GridRadioSelectionColumnHelper<>(this);
561+
562+
public static <T> GridRadioSelectionColumn showRadioSelectionColumn(Grid<T> grid) {
563+
return getHelper(grid).radioButtonSelectionColumnHelper.showRadioSelectionColumn();
564+
}
565+
558566
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*-
2+
* #%L
3+
* Grid Helpers Add-on
4+
* %%
5+
* Copyright (C) 2022 - 2024 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
21+
package com.flowingcode.vaadin.addons.gridhelpers;
22+
23+
import com.vaadin.flow.component.Component;
24+
import com.vaadin.flow.component.HasStyle;
25+
import com.vaadin.flow.component.Synchronize;
26+
import com.vaadin.flow.component.Tag;
27+
import com.vaadin.flow.component.dependency.JsModule;
28+
import com.vaadin.flow.component.dependency.NpmPackage;
29+
30+
/**
31+
* Server side implementation for the flow specific grid radio selection column.
32+
*/
33+
@Tag("grid-flow-radio-selection-column")
34+
@NpmPackage(value = "@vaadin/polymer-legacy-adapter", version = "24.3.2")
35+
@JsModule("@vaadin/polymer-legacy-adapter/style-modules.js")
36+
@JsModule("./fcGridHelper/grid-flow-radio-selection-column.js")
37+
public class GridRadioSelectionColumn extends Component implements HasStyle {
38+
39+
/**
40+
* Sets this column's frozen state.
41+
*
42+
* @param frozen whether to freeze or unfreeze this column
43+
*/
44+
public void setFrozen(boolean frozen) {
45+
getElement().setProperty("frozen", frozen);
46+
}
47+
48+
/**
49+
* Gets the this column's frozen state.
50+
*
51+
* @return whether this column is frozen
52+
*/
53+
@Synchronize("frozen-changed")
54+
public boolean isFrozen() {
55+
return getElement().getProperty("frozen", false);
56+
}
57+
58+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*-
2+
* #%L
3+
* Grid Helpers Add-on
4+
* %%
5+
* Copyright (C) 2022 - 2024 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
21+
package com.flowingcode.vaadin.addons.gridhelpers;
22+
23+
import com.vaadin.flow.component.grid.Grid;
24+
import com.vaadin.flow.component.grid.Grid.SelectionMode;
25+
import java.io.Serializable;
26+
import lombok.RequiredArgsConstructor;
27+
28+
@SuppressWarnings("serial")
29+
@RequiredArgsConstructor
30+
class GridRadioSelectionColumnHelper<T> implements Serializable {
31+
32+
private final GridHelper<T> helper;
33+
34+
private GridRadioSelectionColumn selectionColumn;
35+
36+
public GridRadioSelectionColumn showRadioSelectionColumn() {
37+
remove();
38+
39+
Grid<T> grid = helper.getGrid();
40+
grid.setSelectionMode(SelectionMode.SINGLE);
41+
42+
selectionColumn = new GridRadioSelectionColumn();
43+
selectionColumn.setClassName("fc-grid-radio-selection-column");
44+
45+
if (grid.getElement().getNode().isAttached()) {
46+
grid.getElement().insertChild(0, selectionColumn.getElement());
47+
} else {
48+
grid.getElement().getNode().runWhenAttached(ui -> {
49+
grid.getElement().insertChild(0, selectionColumn.getElement());
50+
});
51+
}
52+
53+
return selectionColumn;
54+
}
55+
56+
private void remove() {
57+
if (selectionColumn != null && selectionColumn.getElement().getNode().isAttached()) {
58+
helper.getGrid().getElement().removeChild(selectionColumn.getElement());
59+
}
60+
}
61+
62+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*-
2+
* #%L
3+
* Grid Helpers Add-on
4+
* %%
5+
* Copyright (C) 2022 - 2024 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
21+
import '@vaadin/grid/vaadin-grid-column.js';
22+
import { GridColumn } from '@vaadin/grid/src/vaadin-grid-column.js';
23+
import { GridRadioSelectionColumnBaseMixin } from './grid-radio-selection-column-base-mixin.js';
24+
25+
class GridFlowRadioSelectionColumn extends GridRadioSelectionColumnBaseMixin(GridColumn) {
26+
27+
static get is() {
28+
return 'grid-flow-radio-selection-column';
29+
}
30+
31+
static get properties() {
32+
return {
33+
/**
34+
* Override property to enable auto-width
35+
*/
36+
autoWidth: {
37+
type: Boolean,
38+
value: true
39+
},
40+
41+
/**
42+
* Override property to set custom width
43+
*/
44+
width: {
45+
type: String,
46+
value: '56px'
47+
},
48+
49+
/**
50+
* The previous state of activeItem. When activeItem turns to `null`,
51+
* previousActiveItem will have an Object with just unselected activeItem
52+
* @private
53+
*/
54+
__previousActiveItem: Object,
55+
};
56+
}
57+
58+
59+
constructor() {
60+
super();
61+
this.__boundOnActiveItemChanged = this.__onActiveItemChanged.bind(this);
62+
}
63+
64+
/** @protected */
65+
disconnectedCallback() {
66+
this._grid.removeEventListener('active-item-changed', this.__boundOnActiveItemChanged);
67+
super.disconnectedCallback();
68+
}
69+
70+
/** @protected */
71+
connectedCallback() {
72+
super.connectedCallback();
73+
if (this._grid) {
74+
this._grid.addEventListener('active-item-changed', this.__boundOnActiveItemChanged);
75+
// this._grid.__deselectDisallowed = true;
76+
}
77+
}
78+
79+
/**
80+
* Override a method from `GridRadioSelectionColumnBaseMixin` to handle the user
81+
* selecting an item.
82+
*
83+
* @param {Object} item the item to select
84+
* @protected
85+
* @override
86+
*/
87+
_selectItem(item) {
88+
this._grid.$connector.doSelection([item], true);
89+
}
90+
91+
/**
92+
* Override a method from `GridRadioSelectionColumnBaseMixin` to handle the user
93+
* deselecting an item.
94+
*
95+
* @param {Object} item the item to deselect
96+
* @protected
97+
* @override
98+
*/
99+
_deselectItem(item) {
100+
this._grid.$connector.doDeselection([item], true);
101+
}
102+
103+
104+
/** @private */
105+
__onActiveItemChanged(e) {
106+
const activeItem = e.detail.value;
107+
if (activeItem) {
108+
if(this.__previousActiveItem !== activeItem) {
109+
this._deselectItem(this.__previousActiveItem);
110+
}
111+
this._selectItem(activeItem);
112+
this.__previousActiveItem = activeItem;
113+
} else {
114+
this.__previousActiveItem = undefined;
115+
}
116+
}
117+
118+
/**
119+
* Override a method from `GridRadioSelectionColumnBaseMixin` to handle the user clicked on an item.
120+
*
121+
* @param {Object} item the clicked item
122+
* @protected
123+
* @override
124+
*/
125+
_onItemClicked(item) {
126+
super._onItemClicked(item);
127+
this.__previousActiveItem = item;
128+
}
129+
130+
}
131+
132+
customElements.define(GridFlowRadioSelectionColumn.is, GridFlowRadioSelectionColumn);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*-
2+
* #%L
3+
* Grid Helpers Add-on
4+
* %%
5+
* Copyright (C) 2022 - 2024 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
21+
import { addListener } from '@vaadin/component-base/src/gestures.js';
22+
23+
/**
24+
* A mixin that provides basic functionality for the
25+
* `<grid-radio-selection-column>`. This includes properties, cell rendering,
26+
* and overridable methods for handling changes to the selection state.
27+
*
28+
* **NOTE**: This mixin is re-used by the Flow component, and as such must not
29+
* implement any selection state updates for the column element or the grid.
30+
* Web component-specific selection state updates must be implemented in the
31+
* `<grid-radio-selection-column>` itself, by overriding the protected methods
32+
* provided by this mixin.
33+
*
34+
* @polymerMixin
35+
*/
36+
export const GridRadioSelectionColumnBaseMixin = (superClass) =>
37+
class GriRadioSelectionColumnBaseMixin extends superClass {
38+
static get properties() {
39+
return {
40+
/**
41+
* Width of the cells for this column.
42+
*/
43+
width: {
44+
type: String,
45+
value: '58px',
46+
},
47+
48+
/**
49+
* Flex grow ratio for the cell widths. When set to 0, cell width is fixed.
50+
* @attr {number} flex-grow
51+
* @type {number}
52+
*/
53+
flexGrow: {
54+
type: Number,
55+
value: 0,
56+
},
57+
58+
/**
59+
* When true, the active gets automatically selected.
60+
* @attr {boolean} auto-select
61+
* @type {boolean}
62+
*/
63+
autoSelect: {
64+
type: Boolean,
65+
value: false,
66+
},
67+
68+
};
69+
}
70+
71+
static get observers() {
72+
return [
73+
'_onHeaderRendererOrBindingChanged(path)',
74+
];
75+
}
76+
77+
/**
78+
* Renders the Select Row radio button to the body cell.
79+
*
80+
* @override
81+
*/
82+
_defaultRenderer(root, _column, { item, selected }) {
83+
let radioButton = root.firstElementChild;
84+
if (!radioButton) {
85+
radioButton = document.createElement('vaadin-radio-button');
86+
radioButton.setAttribute('aria-label', 'Select Row');
87+
root.appendChild(radioButton);
88+
// Add listener after appending, so we can skip the initial change event
89+
radioButton.addEventListener('checked-changed', this.__onSelectRowCheckedChanged.bind(this));
90+
}
91+
92+
radioButton.__item = item;
93+
radioButton.__rendererChecked = selected;
94+
radioButton.checked = selected;
95+
}
96+
97+
/**
98+
* Selects the row when the Select Row radio button is clicked.
99+
* The listener handles only user-fired events.
100+
*
101+
* @private
102+
*/
103+
__onSelectRowCheckedChanged(e) {
104+
// Skip if the state is changed by the renderer.
105+
if (e.target.checked === e.target.__rendererChecked) {
106+
return;
107+
}
108+
109+
this._onItemClicked(e.target.__item);
110+
}
111+
112+
_onItemClicked(item){
113+
this._selectItem(item);
114+
}
115+
116+
};

0 commit comments

Comments
 (0)