001package gwt.material.design.addins.client.stepper; 002 003/* 004 * #%L 005 * GwtMaterial 006 * %% 007 * Copyright (C) 2015 GwtMaterialDesign 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import com.google.gwt.core.client.GWT; 024import com.google.gwt.dom.client.Document; 025import com.google.gwt.event.logical.shared.SelectionEvent; 026import com.google.gwt.event.logical.shared.SelectionHandler; 027import com.google.gwt.event.shared.HandlerRegistration; 028import com.google.gwt.user.client.ui.Widget; 029import com.google.gwt.view.client.SelectionChangeEvent; 030import com.google.gwt.view.client.SelectionChangeEvent.Handler; 031import com.google.gwt.view.client.SelectionChangeEvent.HasSelectionChangedHandlers; 032import gwt.material.design.addins.client.MaterialAddins; 033import gwt.material.design.client.MaterialDesignBase; 034import gwt.material.design.client.base.HasAxis; 035import gwt.material.design.client.base.HasError; 036import gwt.material.design.client.base.MaterialWidget; 037import gwt.material.design.client.base.mixin.CssNameMixin; 038import gwt.material.design.client.constants.Axis; 039import gwt.material.design.client.ui.MaterialLoader; 040import gwt.material.design.client.ui.animate.MaterialAnimator; 041import gwt.material.design.client.ui.animate.Transition; 042import gwt.material.design.client.ui.html.Div; 043import gwt.material.design.client.ui.html.Span; 044 045//@formatter:off 046 047/** 048 * Steppers convey progress through numbered steps. They may also be used for navigation. 049 * 050 * <h3>XML Namespace Declaration</h3> 051 * <pre> 052 * {@code 053 * xmlns:ma='urn:import:gwt.material.design.addins.client' 054 * } 055 * </pre> 056 * 057 * <h3>UiBinder Usage:</h3> 058 * <pre> 059 * {@code 060 * <ma:stepper.MaterialStepper ui:field="stepper"> 061 * <ma:stepper.MaterialStep step="1" title="Name of Step 1"> 062 * <m:MaterialPanel width="100%" height="300px" backgroundColor="grey lighten-2"/> 063 * <m:MaterialButton ui:field="btnContinue1" text="Continue to Step 2" grid="l4" marginTop="12" backgroundColor="blue" textColor="white" waves="DEFAULT"/> 064 * <m:MaterialButton ui:field="btnPrev1" text="Cancel" grid="l4" marginTop="12" type="FLAT" waves="DEFAULT"/> 065 * </ma:stepper.MaterialStep> 066 * <!-- Other Step components here --> 067 * </ma:stepper.MaterialStepper> 068 * } 069 * </pre> 070 * 071 * @author kevzlou7979 072 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#steppers">Material Steppers</a> 073 */ 074// @formatter:on 075public class MaterialStepper extends MaterialWidget implements HasAxis, HasError, SelectionHandler<MaterialStep>, HasSelectionChangedHandlers { 076 077 static { 078 if(MaterialAddins.isDebug()) { 079 MaterialDesignBase.injectCss(MaterialStepperDebugClientBundle.INSTANCE.stepperDebugCss()); 080 } else { 081 MaterialDesignBase.injectCss(MaterialStepperClientBundle.INSTANCE.stepperCss()); 082 } 083 } 084 085 private int currentStepIndex = 0; 086 private Div divFeedback = new Div(); 087 private Span feedback = new Span(); 088 private boolean stepSkippingAllowed = true; 089 090 private final CssNameMixin<MaterialStepper, Axis> axisMixin = new CssNameMixin<>(this); 091 092 public MaterialStepper (){ 093 super(Document.get().createDivElement(), "stepper"); 094 divFeedback.setStyleName("feedback"); 095 divFeedback.add(feedback); 096 } 097 098 @Override 099 protected void onLoad() { 100 super.onLoad(); 101 if(getChildren().size() != 0) { 102 goToStep(currentStepIndex + 1); 103 } 104 } 105 106 /** 107 * Specific method to add {@link MaterialStep}s to the stepper. 108 */ 109 public void add(MaterialStep step) { 110 this.add((Widget)step); 111 step.setAxis(getAxis()); 112 step.addSelectionHandler(this); 113 } 114 115 /** 116 * Go to next Step , used by linear stepper. 117 */ 118 public void nextStep() { 119 if(currentStepIndex >= getWidgetCount() - 1){ 120 GWT.log("You have reach the maximum step."); 121 }else{ 122 Widget w = getWidget(currentStepIndex); 123 if(w instanceof MaterialStep){ 124 MaterialStep step = (MaterialStep) w; 125 step.setActive(false); 126 step.setSuccess(step.getDescription()); 127 128 // next step 129 int nextStepIndex = getWidgetIndex(step) + 1; 130 if (nextStepIndex >= 0){ 131 for(int i = nextStepIndex; i < getWidgetCount(); i++){ 132 w = getWidget(i); 133 if (!(w instanceof MaterialStep)){ 134 continue; 135 } 136 MaterialStep nextStep = (MaterialStep) w; 137 if (nextStep.isEnabled() && nextStep.isVisible()){ 138 nextStep.setActive(true); 139 setCurrentStepIndex(i); 140 break; 141 } 142 } 143 } 144 } 145 } 146 } 147 148 /** 149 * Go to previous step , used by linear stepper. 150 */ 151 public void prevStep() { 152 if(currentStepIndex > 0) { 153 Widget w = getWidget(currentStepIndex); 154 if (w instanceof MaterialStep) { 155 MaterialStep step = (MaterialStep) w; 156 step.setActive(false); 157 158 // next step 159 int prevStepIndex = getWidgetIndex(step) - 1; 160 if (prevStepIndex >= 0){ 161 for(int i = prevStepIndex; i >= 0; i--){ 162 w = getWidget(i); 163 if (!(w instanceof MaterialStep)){ 164 continue; 165 } 166 MaterialStep prevStep = (MaterialStep) w; 167 if (prevStep.isEnabled() && prevStep.isVisible()){ 168 prevStep.setActive(true); 169 setCurrentStepIndex(i); 170 break; 171 } 172 } 173 } 174 } 175 }else{ 176 GWT.log("You have reach the minimum step."); 177 } 178 } 179 180 /** 181 * Go to specific step manually by setting which step index you want to go. 182 */ 183 public void goToStep(int step){ 184 for(int i = 0; i < getWidgetCount(); i++){ 185 Widget w = getWidget(i); 186 if(w instanceof MaterialStep){ 187 ((MaterialStep) w).setActive(false); 188 } 189 } 190 191 Widget w = getWidget(step - 1); 192 if(w instanceof MaterialStep){ 193 ((MaterialStep) w).setActive(true); 194 } 195 setCurrentStepIndex(step - 1); 196 } 197 198 /** 199 * Go to the specfic {@link MaterialStep}. 200 */ 201 public void goToStep(MaterialStep step){ 202 for(int i = 0; i < getWidgetCount(); i++){ 203 Widget w = getWidget(i); 204 if(w instanceof MaterialStep){ 205 MaterialStep materialStep = (MaterialStep) w; 206 boolean active = materialStep.equals(step); 207 materialStep.setActive(active); 208 if (active){ 209 setCurrentStepIndex(i); 210 } 211 } 212 } 213 } 214 215 /** 216 * Go to the step with the specified step id. 217 * 218 * @see MaterialStep#getStep() 219 */ 220 public void goToStepId(int id){ 221 for(int i = 0; i < getWidgetCount(); i++){ 222 Widget w = getWidget(i); 223 if(w instanceof MaterialStep){ 224 MaterialStep materialStep = (MaterialStep) w; 225 boolean active = materialStep.getStep() == id; 226 materialStep.setActive(active); 227 if (active){ 228 setCurrentStepIndex(i); 229 } 230 } 231 } 232 } 233 234 /** 235 * Reset the Stepper to initial step (first step). 236 */ 237 public void reset() { 238 goToStep(1); 239 clearErrorOrSuccess(); 240 } 241 242 /** 243 * Called internally when the index is changed. Fires a {@link SelectionChangeEvent} 244 * when the current index changes. 245 */ 246 protected void setCurrentStepIndex(int currentStepIndex) { 247 if (this.currentStepIndex != currentStepIndex){ 248 this.currentStepIndex = currentStepIndex; 249 SelectionChangeEvent.fire(this); 250 } 251 } 252 253 public int getCurrentStepIndex() { 254 return currentStepIndex; 255 } 256 257 @Override 258 public void setAxis(Axis axis) { 259 axisMixin.setCssName(axis); 260 for(int i = 0; i < getWidgetCount(); i++){ 261 Widget w = getWidget(i); 262 if(w instanceof MaterialStep){ 263 ((MaterialStep) w).setAxis(axis); 264 } 265 } 266 } 267 268 @Override 269 public Axis getAxis() { 270 return axisMixin.getCssName(); 271 } 272 273 /** 274 * Gets the current step component. 275 */ 276 public MaterialStep getCurrentStep() { 277 if(currentStepIndex > getWidgetCount() - 1 || currentStepIndex < 0){ 278 return null; 279 } 280 Widget w = getWidget(currentStepIndex); 281 if (w instanceof MaterialStep){ 282 return (MaterialStep) w; 283 } 284 return null; 285 } 286 287 @Override 288 public void setError(String error) { 289 getCurrentStep().setError(error); 290 } 291 292 @Override 293 public void setSuccess(String success) { 294 getCurrentStep().setSuccess(success); 295 } 296 297 @Override 298 public void setHelperText(String helperText) { 299 getCurrentStep().setDescription(helperText); 300 } 301 302 @Override 303 public void clearErrorOrSuccess() { 304 for(int i = 0; i < getWidgetCount(); i++){ 305 Widget w = getWidget(i); 306 if(w instanceof MaterialStep){ 307 ((MaterialStep) w).clearErrorOrSuccess(); 308 } 309 } 310 } 311 312 /** 313 * Get feedback message. 314 */ 315 public String getFeedback() { 316 return feedback.getElement().getInnerHTML(); 317 } 318 319 /** 320 * Show feedback message and circular loader on body container 321 */ 322 public void showFeedback(String feedbackText) { 323 feedback.setText(feedbackText); 324 MaterialAnimator.animate(Transition.FADEINUP, feedback, 500); 325 MaterialLoader.showLoading(true, getCurrentStep().getDivBody()); 326 add(divFeedback); 327 } 328 329 /** 330 * Hide feedback message and circular loader on body container. 331 */ 332 public void hideFeedback() { 333 divFeedback.removeFromParent(); 334 } 335 336 /** 337 * Sets whether the user is allowed to skip steps by clicking on the step title. 338 * The default is <code>true</code>. 339 */ 340 public void setStepSkippingAllowed(boolean stepSkippingAllowed) { 341 this.stepSkippingAllowed = stepSkippingAllowed; 342 } 343 344 /** 345 * Returns whether the user is allowed to skip steps by clicking on the step title. 346 * The default is <code>true</code>. 347 */ 348 public boolean isStepSkippingAllowed() { 349 return stepSkippingAllowed; 350 } 351 352 /** 353 * Called when a step title is clicked. 354 */ 355 @Override 356 public void onSelection(SelectionEvent<MaterialStep> event) { 357 if (stepSkippingAllowed){ 358 goToStep(event.getSelectedItem()); 359 } 360 } 361 362 @Override 363 public HandlerRegistration addSelectionChangeHandler(final Handler handler) { 364 return addHandler(new Handler() { 365 @Override 366 public void onSelectionChange(SelectionChangeEvent event) { 367 if(isEnabled()){ 368 handler.onSelectionChange(event); 369 } 370 } 371 }, SelectionChangeEvent.getType()); 372 } 373}