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.addins.client.swipeable; 021 022import com.google.gwt.dom.client.Document; 023import com.google.gwt.dom.client.Element; 024import com.google.gwt.event.shared.HandlerRegistration; 025import com.google.gwt.user.client.ui.UIObject; 026import com.google.gwt.user.client.ui.Widget; 027import gwt.material.design.addins.client.MaterialAddins; 028import gwt.material.design.addins.client.base.constants.AddinsCssName; 029import gwt.material.design.addins.client.gesture.velocity.js.JsTransitionOptions; 030import gwt.material.design.addins.client.gesture.velocity.js.JsVelocity; 031import gwt.material.design.addins.client.gesture.velocity.js.JsVelocityOptions; 032import gwt.material.design.addins.client.swipeable.base.HasSwipeableHandler; 033import gwt.material.design.addins.client.swipeable.events.*; 034import gwt.material.design.client.MaterialDesignBase; 035import gwt.material.design.client.base.JsLoader; 036import gwt.material.design.client.base.MaterialWidget; 037import gwt.material.design.client.constants.Color; 038import gwt.material.design.jquery.client.api.Functions; 039import gwt.material.design.jquery.client.api.JQueryElement; 040 041import static gwt.material.design.addins.client.gesture.hammer.js.JsHammer.$; 042 043/** 044 * A panel that allows any of its nested children to be swiped away. 045 * <p> 046 * <h3>XML Namespace Declaration</h3> 047 * <pre> 048 * {@code 049 * xmlns:ma='urn:import:gwt.material.design.addins.client' 050 * } 051 * </pre> 052 * <p> 053 * <h3>UiBinder Usage:</h3> 054 * <pre> 055 * { 056 * @code 057 * <ma:swipeable.MaterialSwipeablePanel ui:field="swipeablePanel" shadow="1" backgroundColor="white" padding="12"> 058 * <m:MaterialLabel text="You can swipe native components. This is a plain label" backgroundColor="yellow" padding="12" /> 059 * <m:MaterialCard> 060 * <m:MaterialCardContent> 061 * <m:MaterialLabel text="This is another Card Component that is swipeable." /> 062 * </m:MaterialCardContent> 063 * </m:MaterialCard> 064 * </ma:swipeable.MaterialSwipeablePanel> 065 * } 066 * </pre> 067 * 068 * @author kevzlou7979 069 * @author Ben Dol 070 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#swipeable">Material Swipeable</a> 071 */ 072//@formatter:on 073public class MaterialSwipeablePanel extends MaterialWidget implements JsLoader, HasSwipeableHandler<Widget> { 074 075 static { 076 if (MaterialAddins.isDebug()) { 077 MaterialDesignBase.injectDebugJs(MaterialSwipeableDebugClientBundle.INSTANCE.swipeableJsDebug()); 078 MaterialDesignBase.injectCss(MaterialSwipeableDebugClientBundle.INSTANCE.swipeableCssDebug()); 079 } else { 080 MaterialDesignBase.injectJs(MaterialSwipeableClientBundle.INSTANCE.swipeableJs()); 081 MaterialDesignBase.injectCss(MaterialSwipeableClientBundle.INSTANCE.swipeableCss()); 082 } 083 } 084 085 public MaterialSwipeablePanel() { 086 super(Document.get().createDivElement(), AddinsCssName.SWIPEABLE); 087 } 088 089 public MaterialSwipeablePanel(Color backgroundColor) { 090 this(); 091 setBackgroundColor(backgroundColor); 092 } 093 094 public MaterialSwipeablePanel(Color backgroundColor, Integer shadow) { 095 this(backgroundColor); 096 setShadow(shadow); 097 } 098 099 @Override 100 protected void onLoad() { 101 super.onLoad(); 102 103 load(); 104 } 105 106 @Override 107 public void load() { 108 for (Widget w : getChildren()) { 109 if (!w.getStyleName().contains(AddinsCssName.IGNORED)) { 110 load(w.getElement(), w); 111 } 112 } 113 } 114 115 protected void load(Element container, Widget target) { 116 JQueryElement parent = $(container); 117 parent.each((object, element) -> { 118 boolean swipeLeftToRight[] = {false}; 119 boolean swipeRightToLeft[] = {false}; 120 121 $(element).hammer().bind("pan", (result) -> { 122 int direction = result.gesture.direction; 123 int x = result.gesture.deltaX; 124 double velocityX = result.gesture.velocityX; 125 126 JsVelocity.$(container).velocity(buildVelocityOption(x), buildTransitionOption(50, false, "easeOutQuad", null)); 127 128 if (direction == 4 && (x > parent.innerWidth() || velocityX < -0.75)) { 129 swipeLeftToRight[0] = true; 130 } 131 132 if (direction == 2 && (x < (-1 * parent.innerWidth() / 2) || velocityX > 0.75)) { 133 swipeRightToLeft[0] = true; 134 } 135 136 }).bind("panend", e -> { 137 // Reset if collection is moved back into original position 138 if (Math.abs(e.gesture.deltaX) < ($(element).innerWidth() / 2)) { 139 swipeLeftToRight[0] = false; 140 swipeRightToLeft[0] = false; 141 } 142 143 // Sets final position once pan ended 144 if (swipeLeftToRight[0] || swipeRightToLeft[0]) { 145 double fullWidth = 0; 146 if (swipeLeftToRight[0]) { 147 fullWidth = parent.innerWidth(); 148 SwipeRightEvent.fire(MaterialSwipeablePanel.this, target); 149 } 150 151 if (swipeRightToLeft[0]) { 152 fullWidth = -1 * parent.innerWidth(); 153 SwipeLeftEvent.fire(MaterialSwipeablePanel.this, target); 154 } 155 156 Functions.Func completeCallback = () -> { 157 parent.css("border", "none"); 158 JsVelocity.$(parent).velocity(buildVelocityOption(0, 0), buildTransitionOption(200, false, "easeQuadOut", () -> parent.remove())); 159 }; 160 161 JsVelocity.$(parent).velocity(buildVelocityOption(fullWidth), buildTransitionOption(100, false, "easeOutQuad", completeCallback)); 162 } else { 163 JsVelocity.$(parent).velocity(buildVelocityOption(0), buildTransitionOption(100, false, "easeQuad", null)); 164 } 165 }); 166 }); 167 } 168 169 @Override 170 protected void onUnload() { 171 super.onUnload(); 172 173 unload(); 174 } 175 176 @Override 177 public void unload() { 178 for (Widget widget : getChildren()) { 179 JQueryElement element = $(widget.getElement()); 180 element.off("pan"); 181 element.off("panend"); 182 } 183 } 184 185 @Override 186 public void reload() { 187 unload(); 188 load(); 189 } 190 191 protected JsVelocityOptions buildVelocityOption(double translateX) { 192 JsVelocityOptions option = new JsVelocityOptions(); 193 option.translateX = translateX; 194 return option; 195 } 196 197 protected JsVelocityOptions buildVelocityOption(double height, double padding) { 198 JsVelocityOptions option = new JsVelocityOptions(); 199 option.height = 0; 200 option.padding = 0; 201 return option; 202 } 203 204 protected JsTransitionOptions buildTransitionOption(int duration, boolean queue, String easing, Functions.Func completeCallback) { 205 JsTransitionOptions option = new JsTransitionOptions(); 206 option.duration = duration; 207 option.easing = easing; 208 option.queue = queue; 209 if (completeCallback != null) { 210 option.complete = completeCallback; 211 } 212 return option; 213 } 214 215 /** 216 * Ignore any elements to be swipeable 217 */ 218 public void ignore(UIObject object, UIObject... objects) { 219 object.addStyleName(AddinsCssName.IGNORED); 220 221 if (objects != null) { 222 for (UIObject obj : objects) { 223 obj.addStyleName(AddinsCssName.IGNORED); 224 } 225 } 226 } 227 228 /** 229 * Remove Ignore property to any ignored elements 230 */ 231 public void removeIgnore(UIObject object, UIObject... objects) { 232 object.removeStyleName(AddinsCssName.IGNORED); 233 234 if (objects != null) { 235 for (UIObject obj : objects) { 236 obj.removeStyleName(AddinsCssName.IGNORED); 237 } 238 } 239 } 240 241 @Override 242 public HandlerRegistration addSwipeLeftHandler(final SwipeLeftEvent.SwipeLeftHandler<Widget> handler) { 243 return addHandler(new SwipeLeftEvent.SwipeLeftHandler<Widget>() { 244 @Override 245 public void onSwipeLeft(SwipeLeftEvent<Widget> event) { 246 if (isEnabled()) { 247 handler.onSwipeLeft(event); 248 } 249 } 250 }, SwipeLeftEvent.getType()); 251 } 252 253 @Override 254 public HandlerRegistration addSwipeRightHandler(final SwipeRightEvent.SwipeRightHandler<Widget> handler) { 255 return addHandler(new SwipeRightEvent.SwipeRightHandler<Widget>() { 256 @Override 257 public void onSwipeRight(SwipeRightEvent<Widget> event) { 258 if (isEnabled()) { 259 handler.onSwipeRight(event); 260 } 261 } 262 }, SwipeRightEvent.getType()); 263 } 264 265 @Override 266 public HandlerRegistration addOnStartSwipeLeftHandler(OnStartSwipeLeftEvent.OnStartSwipeLeftHandler<Widget> handler) { 267 return addHandler(handler, OnStartSwipeLeftEvent.getType()); 268 } 269 270 @Override 271 public HandlerRegistration addOnStartSwipeRightHandler(OnStartSwipeRightEvent.OnStartSwipeRightHandler<Widget> handler) { 272 return addHandler(handler, OnStartSwipeRightEvent.getType()); 273 } 274 275 @Override 276 public HandlerRegistration addOnEndSwipeLeftHandler(OnEndSwipeLeftEvent.OnEndSwipeLeftHandler<Widget> handler) { 277 return addHandler(handler, OnEndSwipeLeftEvent.getType()); 278 } 279 280 @Override 281 public HandlerRegistration addOnEndSwipeRightHandler(OnEndSwipeRightEvent.OnEndSwipeRightHandler<Widget> handler) { 282 return addHandler(handler, OnEndSwipeRightEvent.getType()); 283 } 284}