001/* 002 * #%L 003 * GwtMaterial 004 * %% 005 * Copyright (C) 2015 - 2017 GwtMaterialDesign 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package gwt.material.design.client.ui; 021 022import com.google.gwt.core.client.Scheduler; 023import com.google.gwt.dom.client.Document; 024import com.google.gwt.event.dom.client.ClickEvent; 025import com.google.gwt.event.shared.HandlerRegistration; 026import com.google.gwt.user.client.Event; 027import gwt.material.design.client.base.MaterialWidget; 028import gwt.material.design.client.constants.CssName; 029import gwt.material.design.client.constants.IconPosition; 030import gwt.material.design.client.constants.IconType; 031import gwt.material.design.client.constants.WavesType; 032import gwt.material.design.client.events.PageSelectionEvent; 033import gwt.material.design.client.ui.html.ListItem; 034 035import static gwt.material.design.client.events.PageSelectionEvent.PageSelectionHandler; 036import static gwt.material.design.client.events.PageSelectionEvent.TYPE; 037 038//@formatter:off 039 040/** 041 * Material Pager with page event 042 * <h3>UiBinder Usage:</h3> 043 * <pre> 044 * {@code <m:MaterialPager ui:field='pager' />} 045 * </pre> 046 * 047 * @author Guaido79 048 */ 049public class MaterialPager extends MaterialWidget { 050 051 private int total; 052 private int pageSize = 10; 053 private int currentPage = 1; 054 private int maxPageLinksShown = 10; 055 private int calcTotalPages; 056 private int calcShowingPageFrom; 057 private int calcShowingPageTo; 058 private boolean enableIndicator; 059 private boolean calcInitialized; 060 private String indicatorTemplate = "Page {page} of {total}"; 061 private PagerListItem linkLeft; 062 private PagerListItem linkRight; 063 private MaterialChip indicator; 064 065 public MaterialPager() { 066 super(Document.get().createULElement(), CssName.PAGINATION); 067 setWaves(WavesType.DEFAULT); 068 removeStyleName(CssName.WAVES_EFFECT); 069 } 070 071 public MaterialPager(int total, int pageSize) { 072 this(); 073 this.total = total; 074 this.pageSize = pageSize; 075 } 076 077 public MaterialPager(int total, int pageSize, int currentPage) { 078 this(total, pageSize); 079 this.currentPage = currentPage; 080 } 081 082 @Override 083 protected void onLoad() { 084 super.onLoad(); 085 086 load(); 087 } 088 089 protected void load() { 090 if (!calcInitialized) { 091 calcTotalPages = total / pageSize + (((double) total % (double) pageSize) > 0 ? 1 : 0); 092 093 add(getOrCreateLiElementLeft()); 094 moveNextPagesRange(); 095 add(getOrCreateLiElementRight()); 096 if (enableIndicator) { 097 add(createLiElementIndicator()); 098 } 099 onPageSelection(1); 100 101 calcInitialized = true; 102 } 103 } 104 105 protected void moveNextPagesRange() { 106 calcShowingPageFrom = currentPage; 107 calcShowingPageTo = Math.min(currentPage + maxPageLinksShown - 1, calcTotalPages); 108 createPageNumberLinks(); 109 } 110 111 protected void movePreviousPagesRange() { 112 calcShowingPageFrom = currentPage - maxPageLinksShown + 1; 113 calcShowingPageTo = currentPage; 114 createPageNumberLinks(); 115 } 116 117 protected void createPageNumberLinks() { 118 for (int i = 0; i < getWidgetCount(); i++) { 119 final PagerListItem widget = (PagerListItem) getWidget(i); 120 if (!widget.isFixed()) { 121 Scheduler.get().scheduleDeferred(widget::removeFromParent); 122 } 123 } 124 int insertionIndex = 1; 125 for (int i = calcShowingPageFrom; i <= calcShowingPageTo; i++) { 126 final PagerListItem liElementForPage = createLiElementForPage(i); 127 128 Scheduler.get().scheduleDeferred(new InsertElementAtPositionCommand(insertionIndex++) { 129 @Override 130 public void execute() { 131 insert(liElementForPage, insertionIndex); 132 } 133 }); 134 } 135 } 136 137 protected PagerListItem createLiElementForPage(final int page) { 138 final PagerListItem pageLiElement = new PagerListItem(); 139 pageLiElement.setFixed(false); 140 pageLiElement.add(createLinkPage(page)); 141 142 registerHandler(addPageSelectionHandler(event -> pageLiElement.setActive(event.getPageTo() == page))); 143 registerHandler(pageLiElement.addHandler(event -> { 144 onPageSelection(page); 145 event.preventDefault(); 146 event.stopPropagation(); 147 }, ClickEvent.getType())); 148 149 return pageLiElement; 150 } 151 152 protected PagerListItem getOrCreateLiElementLeft() { 153 linkLeft = new PagerListItem(); 154 linkLeft.setFixed(true); 155 registerHandler(linkLeft.addHandler(event -> { 156 if (linkLeft.isEnabled()) { 157 onPageSelection(currentPage - 1); 158 } 159 event.preventDefault(); 160 event.stopPropagation(); 161 }, ClickEvent.getType())); 162 linkLeft.add(createLinkLeft()); 163 registerHandler(addPageSelectionHandler(event -> MaterialPager.this.linkLeft.setEnabled(event.getPageTo() > 1))); 164 return linkLeft; 165 } 166 167 protected PagerListItem getOrCreateLiElementRight() { 168 linkRight = new PagerListItem(); 169 linkRight.setFixed(true); 170 registerHandler(linkRight.addHandler(event -> { 171 if (linkRight.isEnabled()) { 172 onPageSelection(currentPage + 1); 173 } 174 event.stopPropagation(); 175 event.preventDefault(); 176 }, ClickEvent.getType())); 177 linkRight.add(createLinkRight()); 178 registerHandler(addPageSelectionHandler(event -> MaterialPager.this.linkRight.setEnabled(event.getPageTo() < calcTotalPages))); 179 return linkRight; 180 } 181 182 protected PagerListItem createLiElementIndicator() { 183 PagerListItem indicatorLi = new PagerListItem(false); 184 indicatorLi.setFixed(true); 185 indicatorLi.add(getOrCreateIndicator()); 186 return indicatorLi; 187 } 188 189 protected MaterialChip getOrCreateIndicator() { 190 indicator = new MaterialChip(); 191 indicator.getElement().getStyle().setBackgroundColor("inherit"); 192 registerHandler(addPageSelectionHandler(event -> indicator.setText(indicatorTemplate 193 .replaceAll("\\{page\\}", String.valueOf(event.getPageTo())) 194 .replaceAll("\\{total\\}", String.valueOf(event.getTotalPage())) 195 ))); 196 return indicator; 197 } 198 199 protected MaterialLink createLinkPage(int page) { 200 return new MaterialLink(String.valueOf(page)); 201 } 202 203 protected MaterialLink createLinkLeft() { 204 final MaterialLink linkLeft = new MaterialLink(IconType.CHEVRON_LEFT); 205 linkLeft.setIconPosition(IconPosition.NONE); 206 return linkLeft; 207 } 208 209 protected MaterialLink createLinkRight() { 210 final MaterialLink linkRight = new MaterialLink(IconType.CHEVRON_RIGHT); 211 linkRight.setIconPosition(IconPosition.NONE); 212 return linkRight; 213 } 214 215 protected void onPageSelection(int page) { 216 currentPage = page; 217 218 if (currentPage > calcShowingPageTo) { 219 moveNextPagesRange(); 220 } 221 if (currentPage < calcShowingPageFrom) { 222 movePreviousPagesRange(); 223 } 224 225 PageSelectionEvent event = new PageSelectionEvent(); 226 event.setPageFrom(currentPage); 227 event.setPageTo(page); 228 event.setTotalPage(calcTotalPages); 229 230 fireEvent(event); 231 } 232 233 public HandlerRegistration addPageSelectionHandler(PageSelectionHandler handler) { 234 return addHandler(handler, TYPE); 235 } 236 237 public boolean isEnableIndicator() { 238 return enableIndicator; 239 } 240 241 public void setEnableIndicator(boolean enableIndicator) { 242 this.enableIndicator = enableIndicator; 243 } 244 245 public int getTotal() { 246 return total; 247 } 248 249 public void setTotal(final int total) { 250 boolean needToClear = total != this.total; 251 this.total = total; 252 currentPage = 1; 253 if (calcInitialized && needToClear) { 254 clear(); 255 load(); 256 } 257 } 258 259 public int getPageSize() { 260 return pageSize; 261 } 262 263 public void setPageSize(int pageSize) { 264 this.pageSize = pageSize; 265 } 266 267 public int getCurrentPage() { 268 return currentPage; 269 } 270 271 public void setCurrentPage(int currentPage) { 272 this.currentPage = currentPage; 273 } 274 275 public int getMaxPageLinksShown() { 276 return maxPageLinksShown; 277 } 278 279 public void setMaxPageLinksShown(int maxPageLinksShown) { 280 this.maxPageLinksShown = maxPageLinksShown; 281 } 282 283 public String getIndicatorTemplate() { 284 return indicatorTemplate; 285 } 286 287 /** 288 * Set the paging indicator label with a custom template 289 * <ul> 290 * <li><strong>{page}</strong> is the current page</li> 291 * <li><strong>{total}</strong> is the total page</li> 292 * </ul> 293 * Example 294 * <pre> 295 * {@code 296 * Page {page} of {total} 297 * }</pre> 298 */ 299 public void setIndicatorTemplate(String indicatorTemplate) { 300 this.indicatorTemplate = indicatorTemplate; 301 } 302 303 static abstract class InsertElementAtPositionCommand implements Scheduler.ScheduledCommand { 304 protected int insertionIndex; 305 306 InsertElementAtPositionCommand(int insertionIndex) { 307 this.insertionIndex = insertionIndex; 308 } 309 } 310 311 static class PagerListItem extends ListItem { 312 private boolean fixed; 313 private boolean enabled; 314 315 public PagerListItem() { 316 this(true); 317 } 318 319 public PagerListItem(boolean clickable) { 320 if (clickable) { 321 addStyleName(CssName.WAVES_EFFECT); 322 sinkEvents(Event.ONCLICK | Event.TOUCHEVENTS); 323 } 324 } 325 326 public boolean isFixed() { 327 return fixed; 328 } 329 330 public void setFixed(boolean fixed) { 331 this.fixed = fixed; 332 } 333 334 public void setActive(boolean active) { 335 if (active) { 336 addStyleName(CssName.ACTIVE); 337 } else { 338 removeStyleName(CssName.ACTIVE); 339 } 340 } 341 342 @Override 343 public boolean isEnabled() { 344 return enabled; 345 } 346 347 @Override 348 public void setEnabled(boolean enabled) { 349 this.enabled = enabled; 350 if (!enabled) { 351 addStyleName(CssName.DISABLED); 352 } else { 353 removeStyleName(CssName.DISABLED); 354 } 355 } 356 } 357 358 public MaterialChip getIndicator() { 359 return indicator; 360 } 361}