You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VCheckBoxGroup.java 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui;
  17. import java.util.ArrayList;
  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.function.BiConsumer;
  22. import com.google.gwt.aria.client.Roles;
  23. import com.google.gwt.dom.client.Element;
  24. import com.google.gwt.event.dom.client.ClickEvent;
  25. import com.google.gwt.event.dom.client.ClickHandler;
  26. import com.google.gwt.user.client.ui.FocusWidget;
  27. import com.google.gwt.user.client.ui.HasEnabled;
  28. import com.google.gwt.user.client.ui.Widget;
  29. import com.vaadin.client.ApplicationConnection;
  30. import com.vaadin.client.BrowserInfo;
  31. import com.vaadin.client.WidgetUtil;
  32. import com.vaadin.client.widgets.FocusableFlowPanelComposite;
  33. import com.vaadin.shared.Registration;
  34. import com.vaadin.shared.ui.ListingJsonConstants;
  35. import elemental.json.JsonObject;
  36. /**
  37. * The client-side widget for the {@code CheckBoxGroup} component.
  38. *
  39. * @author Vaadin Ltd.
  40. * @since 8.0
  41. */
  42. public class VCheckBoxGroup extends FocusableFlowPanelComposite
  43. implements Field, ClickHandler, HasEnabled {
  44. public static final String CLASSNAME = "v-select-optiongroup";
  45. public static final String CLASSNAME_OPTION = "v-select-option";
  46. private final Map<VCheckBox, JsonObject> optionsToItems;
  47. /**
  48. * For internal use only. May be removed or replaced in the future.
  49. */
  50. public ApplicationConnection client;
  51. private boolean htmlContentAllowed = false;
  52. private boolean enabled;
  53. private boolean readonly;
  54. private List<BiConsumer<JsonObject, Boolean>> selectionChangeListeners;
  55. public VCheckBoxGroup() {
  56. getWidget().setStyleName(CLASSNAME);
  57. optionsToItems = new HashMap<>();
  58. selectionChangeListeners = new ArrayList<>();
  59. }
  60. /*
  61. * Build all the options
  62. */
  63. public void buildOptions(List<JsonObject> items) {
  64. Roles.getGroupRole().set(getElement());
  65. int i = 0;
  66. int widgetsToRemove = getWidget().getWidgetCount() - items.size();
  67. if (widgetsToRemove < 0) {
  68. widgetsToRemove = 0;
  69. }
  70. List<Widget> remove = new ArrayList<>(widgetsToRemove);
  71. for (Widget widget : getWidget()) {
  72. if (i < items.size()) {
  73. updateItem((VCheckBox) widget, items.get(i), false);
  74. i++;
  75. } else {
  76. remove.add(widget);
  77. }
  78. }
  79. remove.stream().forEach(this::remove);
  80. while (i < items.size()) {
  81. updateItem(new VCheckBox(), items.get(i), true);
  82. i++;
  83. }
  84. }
  85. /**
  86. * Returns the JsonObject used to populate the CheckBox widget that contains
  87. * given Element.
  88. *
  89. * @since 8.2
  90. * @param element
  91. * the element to search for
  92. * @return the related JsonObject; {@code null} if not found
  93. */
  94. public JsonObject getItem(Element element) {
  95. return optionsToItems.entrySet().stream()
  96. .filter(entry -> entry.getKey().getElement()
  97. .isOrHasChild(element))
  98. .map(entry -> entry.getValue()).findFirst().orElse(null);
  99. }
  100. private void remove(Widget widget) {
  101. getWidget().remove(widget);
  102. optionsToItems.remove(widget);
  103. }
  104. private void updateItem(VCheckBox widget, JsonObject item,
  105. boolean requireInitialization) {
  106. String itemHtml = item
  107. .getString(ListingJsonConstants.JSONKEY_ITEM_VALUE);
  108. if (!isHtmlContentAllowed()) {
  109. itemHtml = WidgetUtil.escapeHTML(itemHtml);
  110. }
  111. String iconUrl = item.getString(ListingJsonConstants.JSONKEY_ITEM_ICON);
  112. if (iconUrl != null && !iconUrl.isEmpty()) {
  113. Icon icon = client.getIcon(iconUrl);
  114. itemHtml = icon.getElement().getString() + itemHtml;
  115. }
  116. widget.setHTML(itemHtml);
  117. widget.setValue(
  118. item.getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED));
  119. setOptionEnabled(widget, item);
  120. if (requireInitialization) {
  121. widget.addStyleName(CLASSNAME_OPTION);
  122. widget.addClickHandler(this);
  123. getWidget().add(widget);
  124. }
  125. optionsToItems.put(widget, item);
  126. }
  127. @Override
  128. public void onClick(ClickEvent event) {
  129. if (event.getSource() instanceof VCheckBox) {
  130. VCheckBox source = (VCheckBox) event.getSource();
  131. if (!source.isEnabled()) {
  132. // Click events on the text are received even though the
  133. // checkbox is disabled
  134. return;
  135. }
  136. if (BrowserInfo.get().isWebkit() || BrowserInfo.get().isIE11()) {
  137. // Webkit does not focus non-text input elements on click
  138. // (#11854)
  139. source.setFocus(true);
  140. }
  141. Boolean selected = source.getValue();
  142. JsonObject item = optionsToItems.get(source);
  143. assert item != null;
  144. new ArrayList<>(selectionChangeListeners)
  145. .forEach(listener -> listener.accept(item, selected));
  146. }
  147. }
  148. public void setTabIndex(int tabIndex) {
  149. for (Widget anOptionsContainer : getWidget()) {
  150. FocusWidget widget = (FocusWidget) anOptionsContainer;
  151. widget.setTabIndex(tabIndex);
  152. }
  153. }
  154. /**
  155. * Updates the checkbox's enabled state according to the widget's enabled,
  156. * read only and the item's enabled.
  157. *
  158. * @param checkBox
  159. * the checkbox to update
  160. * @param item
  161. * the item for the checkbox
  162. */
  163. protected void setOptionEnabled(VCheckBox checkBox, JsonObject item) {
  164. boolean optionEnabled = !item
  165. .getBoolean(ListingJsonConstants.JSONKEY_ITEM_DISABLED);
  166. boolean enabled = optionEnabled && !isReadonly() && isEnabled();
  167. checkBox.setEnabled(enabled);
  168. }
  169. public boolean isHtmlContentAllowed() {
  170. return htmlContentAllowed;
  171. }
  172. public void setHtmlContentAllowed(boolean htmlContentAllowed) {
  173. this.htmlContentAllowed = htmlContentAllowed;
  174. }
  175. @Override
  176. public boolean isEnabled() {
  177. return enabled;
  178. }
  179. public boolean isReadonly() {
  180. return readonly;
  181. }
  182. public void setReadonly(boolean readonly) {
  183. if (this.readonly != readonly) {
  184. this.readonly = readonly;
  185. optionsToItems.forEach(this::setOptionEnabled);
  186. }
  187. }
  188. @Override
  189. public void setEnabled(boolean enabled) {
  190. if (this.enabled != enabled) {
  191. this.enabled = enabled;
  192. optionsToItems.forEach(this::setOptionEnabled);
  193. }
  194. }
  195. public Registration addSelectionChangeHandler(
  196. BiConsumer<JsonObject, Boolean> selectionChanged) {
  197. selectionChangeListeners.add(selectionChanged);
  198. return (Registration) () -> selectionChangeListeners
  199. .remove(selectionChanged);
  200. }
  201. }