├── .gitignore ├── README.md ├── UNLICENSE ├── mockapp-backend ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── vaadin │ │ └── mockapp │ │ └── samples │ │ └── backend │ │ ├── DataService.java │ │ ├── data │ │ ├── Availability.java │ │ ├── Category.java │ │ └── Product.java │ │ └── mock │ │ ├── MockDataGenerator.java │ │ └── MockDataService.java │ └── test │ └── java │ └── org │ └── vaadin │ └── mockapp │ └── samples │ └── backend │ └── DataServiceTest.java ├── mockapp-production ├── pom.xml └── src │ └── main │ └── webapp │ └── WEB-INF │ └── web.xml ├── mockapp-ui ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── vaadin │ │ └── mockapp │ │ ├── MockAppUI.java │ │ └── samples │ │ ├── ErrorView.java │ │ ├── MainScreen.java │ │ ├── Menu.java │ │ ├── about │ │ └── AboutView.java │ │ ├── authentication │ │ ├── AccessControl.java │ │ ├── BasicAccessControl.java │ │ ├── CurrentUser.java │ │ └── LoginScreen.java │ │ └── crud │ │ ├── CategoryField.java │ │ ├── CollectionToStringConverter.java │ │ ├── EuroConverter.java │ │ ├── NumberField.java │ │ ├── ProductForm.java │ │ ├── ProductFormDesign.java │ │ ├── ProductGrid.java │ │ ├── SampleCrudLogic.java │ │ └── SampleCrudView.java │ ├── resources │ └── org │ │ └── vaadin │ │ └── mockapp │ │ └── samples │ │ └── crud │ │ └── ProductFormDesign.html │ └── webapp │ └── VAADIN │ └── themes │ └── mockapp │ ├── addons.scss │ ├── favicon.ico │ ├── img │ ├── archetype-login-bg.jpg │ └── table-logo.png │ ├── layouts │ └── aboutview.html │ ├── mockapp.scss │ └── styles.scss ├── mockapp-widgetset ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── vaadin │ │ └── mockapp │ │ ├── client │ │ └── samples │ │ │ └── ResetButtonForTextFieldConnector.java │ │ └── samples │ │ ├── AttributeExtension.java │ │ ├── AttributeExtensionState.java │ │ └── ResetButtonForTextField.java │ └── resources │ └── org │ └── vaadin │ └── mockapp │ ├── MockAppWidgetset.gwt.xml │ ├── public │ └── resetbuttonfortextfield │ │ ├── resetbutton-default.png │ │ ├── resetbutton-default.svg │ │ ├── resetbutton-hover.png │ │ ├── resetbutton-hover.svg │ │ └── styles.css │ └── samples │ └── attribute_extension_connector.js └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | *.iml 4 | target/ 5 | mockapp/overlays/ 6 | mockapp-server/src/main/webapp/VAADIN/themes/mockapp/styles.css 7 | .settings/ 8 | */.settings/ 9 | .classpath 10 | */.classpath 11 | .project 12 | */.project 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mockapp 2 | ============== 3 | 4 | Template for a full-blown Vaadin application that only requires a Servlet 3.0 container to run (no other JEE dependencies). 5 | 6 | 7 | Project Structure 8 | ================= 9 | 10 | The project consists of the following three modules: 11 | 12 | - parent project: common metadata and configuration 13 | - mockapp-widgetset: widgetset, custom client side code and dependencies to widget add-ons 14 | - mockapp-ui: main application module, development time 15 | - mockapp-production: module that produces a production mode WAR for deployment 16 | 17 | The production mode module recompiles the widgetset (obfuscated, not draft), activates production mode for Vaadin with a context parameter in web.xml and contains a precompiled theme. The ui module WAR contains an unobfuscated widgetset, and is meant to be used at development time only. 18 | 19 | Workflow 20 | ======== 21 | 22 | To compile the entire project, run "mvn install" in the parent project. 23 | 24 | Other basic workflow steps: 25 | 26 | - getting started 27 | - compiling the whole project 28 | - run "mvn install" in parent project 29 | - developing the application 30 | - edit code in the ui module 31 | - run "mvn jetty:run" in ui module 32 | - open http://localhost:8080/ 33 | - developing the theme 34 | - run the application as above 35 | - edit the theme in the ui module 36 | - optional: see below for precompiling the theme 37 | - reload the application page 38 | - client side changes or add-ons 39 | - edit code/POM in widgetset module 40 | - run "mvn install" in widgetset module 41 | - if a new add-on has an embedded theme, run "mvn vaadin:update-theme" in the ui module 42 | - debugging client side code 43 | - run "mvn vaadin:run-codeserver" in widgetset module 44 | - activate Super Dev Mode in the debug window of the application 45 | - creating a production mode war 46 | - run "mvn -Pproduction package" in the production mode module or in the parent module 47 | - testing the production mode war 48 | - run "mvn -Pproduction jetty:run-war" in the production mode module 49 | 50 | 51 | Using a precompiled theme 52 | ------------------------- 53 | 54 | When developing the UI module, Vaadin can compile the theme on the fly on every 55 | application reload, or the theme can be precompiled to speed up page loads. 56 | 57 | To precompile the theme run "mvn vaadin:compile-theme" in the ui module. Note, though, 58 | that once the theme has been precompiled, any theme changes will not be visible until 59 | the next theme compilation or running the "mvn clean" target. 60 | 61 | When developing the theme, running the application in the "run" mode (rather than 62 | in "debug") in the IDE can speed up consecutive on-the-fly theme compilations 63 | significantly. 64 | 65 | The production module always automatically precompiles the theme for the production WAR. 66 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /mockapp-backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | mockapp-parent 6 | org.vaadin.maven.mockapp 7 | 1.0-SNAPSHOT 8 | 9 | 4.0.0 10 | 11 | mockapp-backend 12 | mockapp-backend 13 | jar 14 | 15 | 16 | 17 | javax.validation 18 | validation-api 19 | 1.1.0.Final 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | 4.11 27 | test 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /mockapp-backend/src/main/java/org/vaadin/mockapp/samples/backend/DataService.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend; 2 | 3 | import java.util.Collection; 4 | 5 | import org.vaadin.mockapp.samples.backend.data.Category; 6 | import org.vaadin.mockapp.samples.backend.data.Product; 7 | import org.vaadin.mockapp.samples.backend.mock.MockDataService; 8 | 9 | /** 10 | * Back-end service interface for retrieving and updating product data. 11 | */ 12 | public abstract class DataService { 13 | 14 | public abstract Collection getAllProducts(); 15 | 16 | public abstract Collection getAllCategories(); 17 | 18 | public abstract void updateProduct(Product p); 19 | 20 | public abstract void deleteProduct(int productId); 21 | 22 | public abstract Product getProductById(int productId); 23 | 24 | public static DataService get() { 25 | return MockDataService.getInstance(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /mockapp-backend/src/main/java/org/vaadin/mockapp/samples/backend/data/Availability.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend.data; 2 | 3 | public enum Availability { 4 | COMING("Coming"), AVAILABLE("Available"), DISCONTINUED("Discontinued"); 5 | 6 | private final String name; 7 | 8 | private Availability(String name) { 9 | this.name = name; 10 | } 11 | 12 | @Override 13 | public String toString() { 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mockapp-backend/src/main/java/org/vaadin/mockapp/samples/backend/data/Category.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend.data; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.validation.constraints.NotNull; 6 | 7 | public class Category implements Serializable { 8 | 9 | @NotNull 10 | private int id; 11 | @NotNull 12 | private String name; 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | public void setId(int id) { 19 | this.id = id; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return getName(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mockapp-backend/src/main/java/org/vaadin/mockapp/samples/backend/data/Product.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend.data; 2 | 3 | import java.io.Serializable; 4 | import java.math.BigDecimal; 5 | import java.util.Set; 6 | 7 | import javax.validation.constraints.Min; 8 | import javax.validation.constraints.NotNull; 9 | import javax.validation.constraints.Size; 10 | 11 | public class Product implements Serializable { 12 | 13 | @NotNull 14 | private int id = -1; 15 | @NotNull 16 | @Size(min = 2, message = "Product name must have at least two characters") 17 | private String productName = ""; 18 | @Min(0) 19 | private BigDecimal price = BigDecimal.ZERO; 20 | private Set category; 21 | @Min(value = 0, message = "Can't have negative amount in stock") 22 | private int stockCount = 0; 23 | @NotNull 24 | private Availability availability = Availability.COMING; 25 | 26 | public int getId() { 27 | return id; 28 | } 29 | 30 | public void setId(int id) { 31 | this.id = id; 32 | } 33 | 34 | public String getProductName() { 35 | return productName; 36 | } 37 | 38 | public void setProductName(String productName) { 39 | this.productName = productName; 40 | } 41 | 42 | public BigDecimal getPrice() { 43 | return price; 44 | } 45 | 46 | public void setPrice(BigDecimal price) { 47 | this.price = price; 48 | } 49 | 50 | public Set getCategory() { 51 | return category; 52 | } 53 | 54 | public void setCategory(Set category) { 55 | this.category = category; 56 | } 57 | 58 | public int getStockCount() { 59 | return stockCount; 60 | } 61 | 62 | public void setStockCount(int stockCount) { 63 | this.stockCount = stockCount; 64 | } 65 | 66 | public Availability getAvailability() { 67 | return availability; 68 | } 69 | 70 | public void setAvailability(Availability availability) { 71 | this.availability = availability; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /mockapp-backend/src/main/java/org/vaadin/mockapp/samples/backend/mock/MockDataGenerator.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend.mock; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.ArrayList; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Random; 8 | import java.util.Set; 9 | 10 | import org.vaadin.mockapp.samples.backend.data.Availability; 11 | import org.vaadin.mockapp.samples.backend.data.Category; 12 | import org.vaadin.mockapp.samples.backend.data.Product; 13 | 14 | public class MockDataGenerator { 15 | private static int nextCategoryId = 1; 16 | private static int nextProductId = 1; 17 | private static final Random random = new Random(1); 18 | private static final String categoryNames[] = new String[] { 19 | "Children's books", "Best sellers", "Romance", "Mystery", 20 | "Thriller", "Sci-fi", "Non-fiction", "Cookbooks" }; 21 | 22 | private static String[] word1 = new String[] { "The art of", "Mastering", 23 | "The secrets of", "Avoiding", "For fun and profit: ", 24 | "How to fail at", "10 important facts about", 25 | "The ultimate guide to", "Book of", "Surviving", "Encyclopedia of", 26 | "Very much", "Learning the basics of", "The cheap way to", 27 | "Being awesome at", "The life changer:", "The Vaadin way:", 28 | "Becoming one with", "Beginners guide to", 29 | "The complete visual guide to", "The mother of all references:" }; 30 | 31 | private static String[] word2 = new String[] { "gardening", 32 | "living a healthy life", "designing tree houses", "home security", 33 | "intergalaxy travel", "meditation", "ice hockey", 34 | "children's education", "computer programming", "Vaadin TreeTable", 35 | "winter bathing", "playing the cello", "dummies", "rubber bands", 36 | "feeling down", "debugging", "running barefoot", 37 | "speaking to a big audience", "creating software", "giant needles", 38 | "elephants", "keeping your wife happy" }; 39 | 40 | static List createCategories() { 41 | List categories = new ArrayList(); 42 | for (String name : categoryNames) { 43 | Category c = createCategory(name); 44 | categories.add(c); 45 | } 46 | return categories; 47 | 48 | } 49 | 50 | static List createProducts(List categories) { 51 | List products = new ArrayList(); 52 | for (int i = 0; i < 100; i++) { 53 | Product p = createProduct(categories); 54 | products.add(p); 55 | } 56 | 57 | return products; 58 | } 59 | 60 | private static Category createCategory(String name) { 61 | Category c = new Category(); 62 | c.setId(nextCategoryId++); 63 | c.setName(name); 64 | return c; 65 | } 66 | 67 | private static Product createProduct(List categories) { 68 | Product p = new Product(); 69 | p.setId(nextProductId++); 70 | p.setProductName(generateName()); 71 | 72 | p.setPrice(new BigDecimal((random.nextInt(250) + 50) / 10.0)); 73 | p.setAvailability(Availability.values()[random.nextInt(Availability 74 | .values().length)]); 75 | if (p.getAvailability() == Availability.AVAILABLE) { 76 | p.setStockCount(random.nextInt(523)); 77 | } 78 | 79 | p.setCategory(getCategory(categories, 1, 2)); 80 | return p; 81 | } 82 | 83 | private static Set getCategory(List categories, 84 | int min, int max) { 85 | int nr = random.nextInt(max) + min; 86 | HashSet productCategories = new HashSet(); 87 | for (int i = 0; i < nr; i++) { 88 | productCategories.add(categories.get(random.nextInt(categories 89 | .size()))); 90 | } 91 | 92 | return productCategories; 93 | } 94 | 95 | private static String generateName() { 96 | return word1[random.nextInt(word1.length)] + " " 97 | + word2[random.nextInt(word2.length)]; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /mockapp-backend/src/main/java/org/vaadin/mockapp/samples/backend/mock/MockDataService.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend.mock; 2 | 3 | import java.util.List; 4 | 5 | import org.vaadin.mockapp.samples.backend.DataService; 6 | import org.vaadin.mockapp.samples.backend.data.Category; 7 | import org.vaadin.mockapp.samples.backend.data.Product; 8 | 9 | /** 10 | * Mock data model. This implementation has very simplistic locking and does not 11 | * notify users of modifications. 12 | */ 13 | public class MockDataService extends DataService { 14 | 15 | private static MockDataService INSTANCE; 16 | 17 | private List products; 18 | private List categories; 19 | private int nextProductId = 0; 20 | 21 | private MockDataService() { 22 | categories = MockDataGenerator.createCategories(); 23 | products = MockDataGenerator.createProducts(categories); 24 | nextProductId = products.size() + 1; 25 | } 26 | 27 | public synchronized static DataService getInstance() { 28 | if (INSTANCE == null) { 29 | INSTANCE = new MockDataService(); 30 | } 31 | return INSTANCE; 32 | } 33 | 34 | @Override 35 | public synchronized List getAllProducts() { 36 | return products; 37 | } 38 | 39 | @Override 40 | public synchronized List getAllCategories() { 41 | return categories; 42 | } 43 | 44 | @Override 45 | public synchronized void updateProduct(Product p) { 46 | if (p.getId() < 0) { 47 | // New product 48 | p.setId(nextProductId++); 49 | products.add(p); 50 | return; 51 | } 52 | for (int i = 0; i < products.size(); i++) { 53 | if (products.get(i).getId() == p.getId()) { 54 | products.set(i, p); 55 | return; 56 | } 57 | } 58 | 59 | throw new IllegalArgumentException("No product with id " + p.getId() 60 | + " found"); 61 | } 62 | 63 | @Override 64 | public synchronized Product getProductById(int productId) { 65 | for (int i = 0; i < products.size(); i++) { 66 | if (products.get(i).getId() == productId) { 67 | return products.get(i); 68 | } 69 | } 70 | return null; 71 | } 72 | 73 | @Override 74 | public synchronized void deleteProduct(int productId) { 75 | Product p = getProductById(productId); 76 | if (p == null) { 77 | throw new IllegalArgumentException("Product with id " + productId 78 | + " not found"); 79 | } 80 | products.remove(p); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /mockapp-backend/src/test/java/org/vaadin/mockapp/samples/backend/DataServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.backend; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.vaadin.mockapp.samples.backend.data.Product; 6 | import org.vaadin.mockapp.samples.backend.mock.MockDataService; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertFalse; 10 | 11 | /** 12 | * Simple unit test for the back-end data service. 13 | */ 14 | public class DataServiceTest { 15 | 16 | private DataService service; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | service = MockDataService.getInstance(); 21 | } 22 | 23 | @Test 24 | public void testDataServiceCanFetchProducts() throws Exception { 25 | assertFalse(service.getAllProducts().isEmpty()); 26 | } 27 | 28 | @Test 29 | public void testDataServiceCanFetchCategories() throws Exception { 30 | assertFalse(service.getAllCategories().isEmpty()); 31 | } 32 | 33 | @Test 34 | public void testUpdateProduct_updatesTheProduct() throws Exception { 35 | Product p = service.getAllProducts().iterator().next(); 36 | p.setProductName("My Test Name"); 37 | service.updateProduct(p); 38 | Product p2 = service.getAllProducts().iterator().next(); 39 | assertEquals("My Test Name", p2.getProductName()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mockapp-production/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | mockapp-parent 6 | org.vaadin.maven.mockapp 7 | 1.0-SNAPSHOT 8 | 9 | 4.0.0 10 | 11 | mockapp-production 12 | mockapp-production 13 | war 14 | 15 | 16 | ${project.build.directory}/${project.build.finalName} 17 | 18 | 19 | 20 | 21 | 22 | ${project.groupId} 23 | mockapp-ui 24 | ${project.version} 25 | war 26 | runtime 27 | 28 | 30 | 31 | ${project.groupId} 32 | mockapp-ui 33 | ${project.version} 34 | classes 35 | provided 36 | 37 | 38 | ${project.groupId} 39 | mockapp-widgetset 40 | ${project.version} 41 | sources 42 | provided 43 | 44 | 45 | 46 | 47 | 48 | production 49 | 50 | 51 | 53 | 54 | org.apache.maven.plugins 55 | maven-dependency-plugin 56 | 2.9 57 | 58 | 59 | unpack 60 | generate-resources 61 | 62 | unpack 63 | 64 | 65 | 66 | 67 | ${project.groupId} 68 | mockapp-ui 69 | ${project.version} 70 | war 71 | VAADIN/themes/**/* 72 | 74 | VAADIN/themes/**/styles.css 75 | ${vaadin.resource.directory} 76 | 77 | 78 | ${project.groupId} 79 | mockapp-widgetset 80 | ${project.version} 81 | jar 82 | org/vaadin/mockapp/** 83 | 85 | ${vaadin.resource.directory}/WEB-INF/classes 86 | 87 | 88 | true 89 | 90 | 91 | 92 | 93 | 94 | net.alchim31.maven 95 | yuicompressor-maven-plugin 96 | 1.5.1 97 | 98 | true 99 | ${vaadin.resource.directory} 100 | 101 | 102 | 103 | 104 | compress 105 | 106 | 107 | 108 | 109 | 110 | com.vaadin 111 | vaadin-maven-plugin 112 | 113 | org.vaadin.mockapp.MockAppWidgetset 114 | -Xmx512M -Xss1024k 115 | ${vaadin.resource.directory} 116 | ${vaadin.resource.directory}/VAADIN/widgetsets 117 | false 118 | false 119 | 120 | true 121 | mockapp 122 | 123 | 124 | 125 | 126 | compile 127 | compile-theme 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-war-plugin 135 | 136 | 137 | default-war 138 | package 139 | 140 | 141 | 142 | 143 | 144 | ${project.groupId} 145 | mockapp-ui 146 | 147 | 149 | 151 | WEB-INF/lib/mockapp-widgetset-*.jar 152 | 154 | 156 | WEB-INF/lib/vaadin-sass-compiler-*.jar 157 | WEB-INF/lib/sac-*.jar 158 | WEB-INF/lib/flute-*.jar 159 | 160 | 161 | 162 | 164 | VAADIN/gwt-unitCache/**,VAADIN/widgetsets/WEB-INF/** 165 | 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-install-plugin 171 | 2.4 172 | 173 | 174 | default-install 175 | install 176 | 177 | 178 | 179 | 181 | 182 | org.eclipse.jetty 183 | jetty-maven-plugin 184 | ${jetty.plugin.version} 185 | 186 | ${vaadin.resource.directory} 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 198 | 199 | org.apache.maven.plugins 200 | maven-war-plugin 201 | 202 | 203 | default-war 204 | none 205 | 206 | 207 | 208 | 209 | org.apache.maven.plugins 210 | maven-install-plugin 211 | 2.4 212 | 213 | 214 | default-install 215 | none 216 | 217 | 218 | 219 | 220 | 221 | 222 | 224 | 226 | 227 | org.eclipse.m2e 228 | lifecycle-mapping 229 | 1.0.0 230 | 231 | 232 | 233 | 234 | 235 | com.vaadin 236 | 237 | vaadin-maven-plugin 238 | 239 | 240 | [7.1.11,) 241 | 242 | 243 | compile-theme 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | org.apache.maven.plugins 254 | 255 | 256 | maven-dependency-plugin 257 | 258 | 259 | [2.9,) 260 | 261 | 262 | unpack 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /mockapp-production/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | productionMode 5 | true 6 | 7 | -------------------------------------------------------------------------------- /mockapp-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | mockapp-parent 6 | org.vaadin.maven.mockapp 7 | 1.0-SNAPSHOT 8 | 9 | 4.0.0 10 | 11 | mockapp-ui 12 | mockapp-ui 13 | war 14 | 15 | 16 | 17 | 18 | javax.servlet 19 | javax.servlet-api 20 | provided 21 | 22 | 23 | com.vaadin 24 | vaadin-server 25 | 26 | 27 | com.vaadin 28 | vaadin-push 29 | 30 | 31 | com.vaadin 32 | vaadin-themes 33 | 34 | 35 | 36 | ${project.groupId} 37 | mockapp-backend 38 | ${project.version} 39 | 40 | 41 | 42 | 43 | ${project.groupId} 44 | mockapp-widgetset 45 | ${project.version} 46 | 47 | 48 | 49 | 50 | org.hibernate 51 | hibernate-validator 52 | 5.1.0.CR1 53 | 54 | 55 | 56 | 57 | junit 58 | junit 59 | 4.11 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-war-plugin 69 | 70 | 73 | true 74 | 76 | WEB-INF/classes/VAADIN/gwt-unitCache/**, 77 | WEB-INF/classes/VAADIN/widgetsets/WEB-INF/** 78 | 79 | 80 | 81 | 83 | 84 | org.eclipse.jetty 85 | jetty-maven-plugin 86 | ${jetty.plugin.version} 87 | 88 | 2 89 | 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-failsafe-plugin 95 | 2.16 96 | 97 | 98 | default 99 | 100 | true 101 | 102 | 103 | 104 | integration-test 105 | 106 | integration-test 107 | 108 | 109 | 110 | verify 111 | 112 | verify 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/MockAppUI.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp; 2 | 3 | import javax.servlet.annotation.WebServlet; 4 | 5 | import org.vaadin.mockapp.samples.MainScreen; 6 | import org.vaadin.mockapp.samples.authentication.AccessControl; 7 | import org.vaadin.mockapp.samples.authentication.BasicAccessControl; 8 | import org.vaadin.mockapp.samples.authentication.LoginScreen; 9 | import org.vaadin.mockapp.samples.authentication.LoginScreen.LoginListener; 10 | 11 | import com.vaadin.annotations.Theme; 12 | import com.vaadin.annotations.VaadinServletConfiguration; 13 | import com.vaadin.annotations.Viewport; 14 | import com.vaadin.annotations.Widgetset; 15 | import com.vaadin.server.Responsive; 16 | import com.vaadin.server.VaadinRequest; 17 | import com.vaadin.server.VaadinServlet; 18 | import com.vaadin.ui.UI; 19 | import com.vaadin.ui.themes.ValoTheme; 20 | 21 | /** 22 | * Main UI class of the application that shows either the login screen or the 23 | * main view of the application depending on whether a user is signed in. 24 | * 25 | * The @Viewport annotation configures the viewport meta tags appropriately on 26 | * mobile devices. Instead of device based scaling (default), using responsive 27 | * layouts. 28 | */ 29 | @Viewport("user-scalable=no,initial-scale=1.0") 30 | @Theme("mockapp") 31 | @Widgetset("org.vaadin.mockapp.MockAppWidgetset") 32 | public class MockAppUI extends UI { 33 | 34 | private AccessControl accessControl = new BasicAccessControl(); 35 | 36 | @Override 37 | protected void init(VaadinRequest vaadinRequest) { 38 | Responsive.makeResponsive(this); 39 | setLocale(vaadinRequest.getLocale()); 40 | getPage().setTitle("MockApp"); 41 | if (!accessControl.isUserSignedIn()) { 42 | setContent(new LoginScreen(accessControl, new LoginListener() { 43 | @Override 44 | public void loginSuccessful() { 45 | showMainView(); 46 | } 47 | })); 48 | } else { 49 | showMainView(); 50 | } 51 | } 52 | 53 | protected void showMainView() { 54 | addStyleName(ValoTheme.UI_WITH_MENU); 55 | setContent(new MainScreen(MockAppUI.this)); 56 | getNavigator().navigateTo(getNavigator().getState()); 57 | } 58 | 59 | public static MockAppUI get() { 60 | return (MockAppUI) UI.getCurrent(); 61 | } 62 | 63 | public AccessControl getAccessControl() { 64 | return accessControl; 65 | } 66 | 67 | @WebServlet(urlPatterns = "/*", name = "MockAppUIServlet", asyncSupported = true) 68 | @VaadinServletConfiguration(ui = MockAppUI.class, productionMode = false) 69 | public static class MockAppUIServlet extends VaadinServlet { 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/ErrorView.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples; 2 | 3 | import com.vaadin.navigator.View; 4 | import com.vaadin.navigator.ViewChangeListener; 5 | import com.vaadin.ui.Label; 6 | import com.vaadin.ui.VerticalLayout; 7 | import com.vaadin.ui.themes.Reindeer; 8 | 9 | /** 10 | * View shown when trying to navigate to a view that does not exist using 11 | * {@link com.vaadin.navigator.Navigator}. 12 | * 13 | * 14 | */ 15 | public class ErrorView extends VerticalLayout implements View { 16 | 17 | private Label explanation; 18 | 19 | public ErrorView() { 20 | setMargin(true); 21 | setSpacing(true); 22 | 23 | Label header = new Label("The view could not be found"); 24 | header.addStyleName(Reindeer.LABEL_H1); 25 | addComponent(header); 26 | addComponent(explanation = new Label()); 27 | } 28 | 29 | @Override 30 | public void enter(ViewChangeListener.ViewChangeEvent event) { 31 | explanation.setValue(String.format( 32 | "You tried to navigate to a view ('%s') that does not exist.", 33 | event.getViewName())); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/MainScreen.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples; 2 | 3 | import org.vaadin.mockapp.MockAppUI; 4 | import org.vaadin.mockapp.samples.about.AboutView; 5 | import org.vaadin.mockapp.samples.crud.SampleCrudView; 6 | 7 | import com.vaadin.navigator.Navigator; 8 | import com.vaadin.navigator.ViewChangeListener; 9 | import com.vaadin.server.FontAwesome; 10 | import com.vaadin.ui.CssLayout; 11 | import com.vaadin.ui.HorizontalLayout; 12 | 13 | /** 14 | * Content of the UI when the user is logged in. 15 | * 16 | * 17 | */ 18 | public class MainScreen extends HorizontalLayout { 19 | private Menu menu; 20 | 21 | public MainScreen(MockAppUI ui) { 22 | 23 | setStyleName("main-screen"); 24 | 25 | CssLayout viewContainer = new CssLayout(); 26 | viewContainer.addStyleName("valo-content"); 27 | viewContainer.setSizeFull(); 28 | 29 | final Navigator navigator = new Navigator(ui, viewContainer); 30 | navigator.setErrorView(ErrorView.class); 31 | menu = new Menu(navigator); 32 | menu.addView(new SampleCrudView(), SampleCrudView.VIEW_NAME, 33 | SampleCrudView.VIEW_NAME, FontAwesome.EDIT); 34 | menu.addView(new AboutView(), AboutView.VIEW_NAME, AboutView.VIEW_NAME, 35 | FontAwesome.INFO_CIRCLE); 36 | 37 | navigator.addViewChangeListener(viewChangeListener); 38 | 39 | addComponent(menu); 40 | addComponent(viewContainer); 41 | setExpandRatio(viewContainer, 1); 42 | setSizeFull(); 43 | } 44 | 45 | // notify the view menu about view changes so that it can display which view 46 | // is currently active 47 | ViewChangeListener viewChangeListener = new ViewChangeListener() { 48 | 49 | @Override 50 | public boolean beforeViewChange(ViewChangeEvent event) { 51 | return true; 52 | } 53 | 54 | @Override 55 | public void afterViewChange(ViewChangeEvent event) { 56 | menu.setActiveView(event.getViewName()); 57 | } 58 | 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/Menu.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.vaadin.navigator.Navigator; 7 | import com.vaadin.navigator.View; 8 | import com.vaadin.server.FontAwesome; 9 | import com.vaadin.server.Page; 10 | import com.vaadin.server.Resource; 11 | import com.vaadin.server.ThemeResource; 12 | import com.vaadin.server.VaadinSession; 13 | import com.vaadin.ui.Alignment; 14 | import com.vaadin.ui.Button; 15 | import com.vaadin.ui.Button.ClickEvent; 16 | import com.vaadin.ui.Button.ClickListener; 17 | import com.vaadin.ui.CssLayout; 18 | import com.vaadin.ui.HorizontalLayout; 19 | import com.vaadin.ui.Image; 20 | import com.vaadin.ui.Label; 21 | import com.vaadin.ui.MenuBar; 22 | import com.vaadin.ui.MenuBar.Command; 23 | import com.vaadin.ui.MenuBar.MenuItem; 24 | import com.vaadin.ui.themes.ValoTheme; 25 | 26 | /** 27 | * Responsive navigation menu presenting a list of available views to the user. 28 | */ 29 | public class Menu extends CssLayout { 30 | 31 | private static final String VALO_MENUITEMS = "valo-menuitems"; 32 | private static final String VALO_MENU_TOGGLE = "valo-menu-toggle"; 33 | private static final String VALO_MENU_VISIBLE = "valo-menu-visible"; 34 | private Navigator navigator; 35 | private Map viewButtons = new HashMap(); 36 | 37 | private CssLayout menuItemsLayout; 38 | private CssLayout menuPart; 39 | 40 | public Menu(Navigator navigator) { 41 | this.navigator = navigator; 42 | setPrimaryStyleName(ValoTheme.MENU_ROOT); 43 | menuPart = new CssLayout(); 44 | menuPart.addStyleName(ValoTheme.MENU_PART); 45 | 46 | // header of the menu 47 | final HorizontalLayout top = new HorizontalLayout(); 48 | top.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT); 49 | top.addStyleName(ValoTheme.MENU_TITLE); 50 | top.setSpacing(true); 51 | Label title = new Label("My CRUD"); 52 | title.addStyleName(ValoTheme.LABEL_H3); 53 | title.setSizeUndefined(); 54 | Image image = new Image(null, new ThemeResource("img/table-logo.png")); 55 | image.setStyleName("logo"); 56 | top.addComponent(image); 57 | top.addComponent(title); 58 | menuPart.addComponent(top); 59 | 60 | // logout menu item 61 | MenuBar logoutMenu = new MenuBar(); 62 | logoutMenu.addItem("Logout", FontAwesome.SIGN_OUT, new Command() { 63 | 64 | @Override 65 | public void menuSelected(MenuItem selectedItem) { 66 | VaadinSession.getCurrent().getSession().invalidate(); 67 | Page.getCurrent().reload(); 68 | } 69 | }); 70 | 71 | logoutMenu.addStyleName("user-menu"); 72 | menuPart.addComponent(logoutMenu); 73 | 74 | // button for toggling the visibility of the menu when on a small screen 75 | final Button showMenu = new Button("Menu", new ClickListener() { 76 | @Override 77 | public void buttonClick(final ClickEvent event) { 78 | if (menuPart.getStyleName().contains(VALO_MENU_VISIBLE)) { 79 | menuPart.removeStyleName(VALO_MENU_VISIBLE); 80 | } else { 81 | menuPart.addStyleName(VALO_MENU_VISIBLE); 82 | } 83 | } 84 | }); 85 | showMenu.addStyleName(ValoTheme.BUTTON_PRIMARY); 86 | showMenu.addStyleName(ValoTheme.BUTTON_SMALL); 87 | showMenu.addStyleName(VALO_MENU_TOGGLE); 88 | showMenu.setIcon(FontAwesome.NAVICON); 89 | menuPart.addComponent(showMenu); 90 | 91 | // container for the navigation buttons, which are added by addView() 92 | menuItemsLayout = new CssLayout(); 93 | menuItemsLayout.setPrimaryStyleName(VALO_MENUITEMS); 94 | menuPart.addComponent(menuItemsLayout); 95 | 96 | addComponent(menuPart); 97 | } 98 | 99 | /** 100 | * Register a pre-created view instance in the navigation menu and in the 101 | * {@link Navigator}. 102 | * 103 | * @see Navigator#addView(String, View) 104 | * 105 | * @param view 106 | * view instance to register 107 | * @param name 108 | * view name 109 | * @param caption 110 | * view caption in the menu 111 | * @param icon 112 | * view icon in the menu 113 | */ 114 | public void addView(View view, final String name, String caption, 115 | Resource icon) { 116 | navigator.addView(name, view); 117 | createViewButton(name, caption, icon); 118 | } 119 | 120 | /** 121 | * Register a view in the navigation menu and in the {@link Navigator} based 122 | * on a view class. 123 | * 124 | * @see Navigator#addView(String, Class) 125 | * 126 | * @param viewClass 127 | * class of the views to create 128 | * @param name 129 | * view name 130 | * @param caption 131 | * view caption in the menu 132 | * @param icon 133 | * view icon in the menu 134 | */ 135 | public void addView(Class viewClass, final String name, 136 | String caption, Resource icon) { 137 | navigator.addView(name, viewClass); 138 | createViewButton(name, caption, icon); 139 | } 140 | 141 | private void createViewButton(final String name, String caption, 142 | Resource icon) { 143 | Button button = new Button(caption, new ClickListener() { 144 | 145 | @Override 146 | public void buttonClick(ClickEvent event) { 147 | navigator.navigateTo(name); 148 | 149 | } 150 | }); 151 | button.setPrimaryStyleName(ValoTheme.MENU_ITEM); 152 | button.setIcon(icon); 153 | menuItemsLayout.addComponent(button); 154 | viewButtons.put(name, button); 155 | } 156 | 157 | /** 158 | * Highlights a view navigation button as the currently active view in the 159 | * menu. This method does not perform the actual navigation. 160 | * 161 | * @param viewName 162 | * the name of the view to show as active 163 | */ 164 | public void setActiveView(String viewName) { 165 | for (Button button : viewButtons.values()) { 166 | button.removeStyleName("selected"); 167 | } 168 | Button selected = viewButtons.get(viewName); 169 | if (selected != null) { 170 | selected.addStyleName("selected"); 171 | } 172 | menuPart.removeStyleName(VALO_MENU_VISIBLE); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/about/AboutView.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.about; 2 | 3 | import com.vaadin.navigator.View; 4 | import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; 5 | import com.vaadin.server.FontAwesome; 6 | import com.vaadin.shared.Version; 7 | import com.vaadin.shared.ui.label.ContentMode; 8 | import com.vaadin.ui.Alignment; 9 | import com.vaadin.ui.CustomLayout; 10 | import com.vaadin.ui.Label; 11 | import com.vaadin.ui.VerticalLayout; 12 | 13 | public class AboutView extends VerticalLayout implements View { 14 | 15 | public static final String VIEW_NAME = "About"; 16 | 17 | public AboutView() { 18 | CustomLayout aboutContent = new CustomLayout("aboutview"); 19 | aboutContent.setStyleName("about-content"); 20 | 21 | // you can add Vaadin components in predefined slots in the custom 22 | // layout 23 | aboutContent.addComponent( 24 | new Label(FontAwesome.INFO_CIRCLE.getHtml() 25 | + " This application is using Vaadin " 26 | + Version.getFullVersion(), ContentMode.HTML), "info"); 27 | 28 | setSizeFull(); 29 | setStyleName("about-view"); 30 | addComponent(aboutContent); 31 | setComponentAlignment(aboutContent, Alignment.MIDDLE_CENTER); 32 | } 33 | 34 | @Override 35 | public void enter(ViewChangeEvent event) { 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/authentication/AccessControl.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.authentication; 2 | 3 | /** 4 | * Simple interface for authentication and authorization checks. 5 | */ 6 | public interface AccessControl { 7 | 8 | public boolean signIn(String username, String password); 9 | 10 | public boolean isUserSignedIn(); 11 | 12 | public boolean isUserInRole(String role); 13 | 14 | public String getPrincipalName(); 15 | } 16 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/authentication/BasicAccessControl.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.authentication; 2 | 3 | /** 4 | * Default mock implementation of {@link AccessControl}. This implementation 5 | * accepts any string as a password, and considers the user "admin" as the only 6 | * administrator. 7 | */ 8 | public class BasicAccessControl implements AccessControl { 9 | 10 | @Override 11 | public boolean signIn(String username, String password) { 12 | if (username == null || username.isEmpty()) 13 | return false; 14 | 15 | CurrentUser.set(username); 16 | return true; 17 | } 18 | 19 | @Override 20 | public boolean isUserSignedIn() { 21 | return !CurrentUser.get().isEmpty(); 22 | } 23 | 24 | @Override 25 | public boolean isUserInRole(String role) { 26 | if ("admin".equals(role)) { 27 | // Only the "admin" user is in the "admin" role 28 | return getPrincipalName().equals("admin"); 29 | } 30 | 31 | // All users are in all non-admin roles 32 | return true; 33 | } 34 | 35 | @Override 36 | public String getPrincipalName() { 37 | return CurrentUser.get(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/authentication/CurrentUser.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.authentication; 2 | 3 | import com.vaadin.server.VaadinRequest; 4 | import com.vaadin.server.VaadinSession; 5 | import com.vaadin.server.WrappedSession; 6 | 7 | /** 8 | * Class for retrieving and setting the name of the current user of the current 9 | * session (without using JAAS). All methods of this class require that a 10 | * {@link VaadinRequest} is bound to the current thread. 11 | * 12 | * 13 | * @see com.vaadin.server.VaadinService#getCurrentRequest() 14 | */ 15 | public final class CurrentUser { 16 | 17 | /** 18 | * The attribute key used to store the username in the session. 19 | */ 20 | public static final String CURRENT_USER_SESSION_ATTRIBUTE_KEY = CurrentUser.class 21 | .getCanonicalName(); 22 | 23 | private CurrentUser() { 24 | } 25 | 26 | /** 27 | * Returns the name of the current user stored in the current session, or an 28 | * empty string if no user name is stored. 29 | * 30 | * @throws IllegalStateException 31 | * if the current session cannot be accessed. 32 | */ 33 | public static String get() { 34 | String currentUser = (String) getCurrentHttpSession().getAttribute( 35 | CURRENT_USER_SESSION_ATTRIBUTE_KEY); 36 | if (currentUser == null) { 37 | return ""; 38 | } else { 39 | return currentUser; 40 | } 41 | } 42 | 43 | private static WrappedSession getCurrentHttpSession() { 44 | VaadinSession s = VaadinSession.getCurrent(); 45 | if (s == null) { 46 | throw new IllegalStateException( 47 | "No session found for current thread"); 48 | } 49 | return s.getSession(); 50 | } 51 | 52 | /** 53 | * Sets the name of the current user and stores it in the current session. 54 | * Using a {@code null} username will remove the username from the session. 55 | * 56 | * @throws IllegalStateException 57 | * if the current session cannot be accessed. 58 | */ 59 | public static void set(String currentUser) { 60 | if (currentUser == null) { 61 | getCurrentHttpSession().removeAttribute( 62 | CURRENT_USER_SESSION_ATTRIBUTE_KEY); 63 | } else { 64 | getCurrentHttpSession().setAttribute( 65 | CURRENT_USER_SESSION_ATTRIBUTE_KEY, currentUser); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/authentication/LoginScreen.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.authentication; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.vaadin.event.ShortcutAction; 6 | import com.vaadin.server.Page; 7 | import com.vaadin.shared.ui.label.ContentMode; 8 | import com.vaadin.ui.Alignment; 9 | import com.vaadin.ui.Button; 10 | import com.vaadin.ui.Component; 11 | import com.vaadin.ui.CssLayout; 12 | import com.vaadin.ui.FormLayout; 13 | import com.vaadin.ui.Label; 14 | import com.vaadin.ui.Notification; 15 | import com.vaadin.ui.PasswordField; 16 | import com.vaadin.ui.TextField; 17 | import com.vaadin.ui.VerticalLayout; 18 | import com.vaadin.ui.themes.ValoTheme; 19 | 20 | /** 21 | * UI content when the user is not logged in yet. 22 | */ 23 | public class LoginScreen extends CssLayout { 24 | 25 | private TextField username; 26 | private PasswordField password; 27 | private Button login; 28 | private Button forgotPassword; 29 | private LoginListener loginListener; 30 | private AccessControl accessControl; 31 | 32 | public LoginScreen(AccessControl accessControl, LoginListener loginListener) { 33 | this.loginListener = loginListener; 34 | this.accessControl = accessControl; 35 | buildUI(); 36 | username.focus(); 37 | } 38 | 39 | private void buildUI() { 40 | addStyleName("login-screen"); 41 | 42 | // login form, centered in the available part of the screen 43 | Component loginForm = buildLoginForm(); 44 | 45 | // layout to center login form when there is sufficient screen space 46 | // - see the theme for how this is made responsive for various screen 47 | // sizes 48 | VerticalLayout centeringLayout = new VerticalLayout(); 49 | centeringLayout.setStyleName("centering-layout"); 50 | centeringLayout.addComponent(loginForm); 51 | centeringLayout.setComponentAlignment(loginForm, 52 | Alignment.MIDDLE_CENTER); 53 | 54 | // information text about logging in 55 | CssLayout loginInformation = buildLoginInformation(); 56 | 57 | addComponent(centeringLayout); 58 | addComponent(loginInformation); 59 | } 60 | 61 | private Component buildLoginForm() { 62 | FormLayout loginForm = new FormLayout(); 63 | 64 | loginForm.addStyleName("login-form"); 65 | loginForm.setSizeUndefined(); 66 | loginForm.setMargin(false); 67 | 68 | loginForm.addComponent(username = new TextField("Username", "admin")); 69 | username.setWidth(15, Unit.EM); 70 | loginForm.addComponent(password = new PasswordField("Password")); 71 | password.setWidth(15, Unit.EM); 72 | password.setDescription("Write anything"); 73 | CssLayout buttons = new CssLayout(); 74 | buttons.setStyleName("buttons"); 75 | loginForm.addComponent(buttons); 76 | 77 | buttons.addComponent(login = new Button("Login")); 78 | login.setDisableOnClick(true); 79 | login.addClickListener(new Button.ClickListener() { 80 | @Override 81 | public void buttonClick(Button.ClickEvent event) { 82 | try { 83 | login(); 84 | } finally { 85 | login.setEnabled(true); 86 | } 87 | } 88 | }); 89 | login.setClickShortcut(ShortcutAction.KeyCode.ENTER); 90 | login.addStyleName(ValoTheme.BUTTON_FRIENDLY); 91 | 92 | buttons.addComponent(forgotPassword = new Button("Forgot password?")); 93 | forgotPassword.addClickListener(new Button.ClickListener() { 94 | @Override 95 | public void buttonClick(Button.ClickEvent event) { 96 | showNotification(new Notification("Hint: Try anything")); 97 | } 98 | }); 99 | forgotPassword.addStyleName(ValoTheme.BUTTON_LINK); 100 | return loginForm; 101 | } 102 | 103 | private CssLayout buildLoginInformation() { 104 | CssLayout loginInformation = new CssLayout(); 105 | loginInformation.setStyleName("login-information"); 106 | Label loginInfoText = new Label( 107 | "

Login Information

" 108 | + "Log in as "admin" to have full access. Log in with any other username to have read-only access. For all users, any password is fine", 109 | ContentMode.HTML); 110 | loginInformation.addComponent(loginInfoText); 111 | return loginInformation; 112 | } 113 | 114 | private void login() { 115 | if (accessControl.signIn(username.getValue(), password.getValue())) { 116 | loginListener.loginSuccessful(); 117 | } else { 118 | showNotification(new Notification("Login failed", 119 | "Please check your username and password and try again.", 120 | Notification.Type.HUMANIZED_MESSAGE)); 121 | username.focus(); 122 | } 123 | } 124 | 125 | private void showNotification(Notification notification) { 126 | // keep the notification visible a little while after moving the 127 | // mouse, or until clicked 128 | notification.setDelayMsec(2000); 129 | notification.show(Page.getCurrent()); 130 | } 131 | 132 | public interface LoginListener extends Serializable { 133 | void loginSuccessful(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/CategoryField.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.util.Collection; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | import org.vaadin.mockapp.samples.backend.data.Category; 10 | 11 | import com.vaadin.data.util.converter.Converter.ConversionException; 12 | import com.vaadin.ui.CheckBox; 13 | import com.vaadin.ui.Component; 14 | import com.vaadin.ui.CustomField; 15 | import com.vaadin.ui.VerticalLayout; 16 | 17 | /** 18 | * A custom Field implementation that allows selecting a set of categories using 19 | * checkboxes rather than multi-selection in a list/table or a TwinColSelect. 20 | */ 21 | public class CategoryField extends CustomField> { 22 | 23 | private VerticalLayout options; 24 | private Map checkboxes = new HashMap(); 25 | private boolean updatingField = false; 26 | 27 | public CategoryField() { 28 | options = new VerticalLayout(); 29 | } 30 | 31 | public CategoryField(String caption) { 32 | this(); 33 | setCaption(caption); 34 | } 35 | 36 | @Override 37 | protected Component initContent() { 38 | return options; 39 | } 40 | 41 | /** 42 | * Set the collection of categories among which the used can select a 43 | * subset. 44 | * 45 | * @param categories 46 | * all available categories 47 | */ 48 | public void setOptions(Collection categories) { 49 | options.removeAllComponents(); 50 | checkboxes.clear(); 51 | for (final Category category : categories) { 52 | final CheckBox box = new CheckBox(category.getName()); 53 | checkboxes.put(category, box); 54 | box.addValueChangeListener(new ValueChangeListener() { 55 | 56 | @Override 57 | public void valueChange( 58 | com.vaadin.data.Property.ValueChangeEvent event) { 59 | if (!updatingField) { 60 | Set oldCategories = getValue(); 61 | Set categories; 62 | if (oldCategories != null) { 63 | categories = new HashSet(oldCategories); 64 | } else { 65 | categories = new HashSet(); 66 | } 67 | if (box.getValue()) { 68 | categories.add(category); 69 | } else { 70 | categories.remove(category); 71 | } 72 | setInternalValue(categories); 73 | } 74 | } 75 | }); 76 | options.addComponent(box); 77 | } 78 | } 79 | 80 | @Override 81 | public Class getType() { 82 | return Set.class; 83 | } 84 | 85 | @Override 86 | protected void setInternalValue(Set newValue) { 87 | updatingField = true; 88 | super.setInternalValue(newValue); 89 | if (newValue != null) { 90 | for (Category category : checkboxes.keySet()) { 91 | checkboxes.get(category).setValue(newValue.contains(category)); 92 | } 93 | } else { 94 | for (Category category : checkboxes.keySet()) { 95 | checkboxes.get(category).setValue(false); 96 | } 97 | } 98 | updatingField = false; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/CollectionToStringConverter.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.util.Collection; 4 | import java.util.Locale; 5 | 6 | import com.vaadin.data.util.converter.Converter; 7 | 8 | /** 9 | * A converter that allows displaying a collection as a comma separated list of 10 | * strings. 11 | */ 12 | public class CollectionToStringConverter implements 13 | Converter { 14 | 15 | @Override 16 | public Collection convertToModel(String value, 17 | Class targetType, Locale locale) 18 | throws com.vaadin.data.util.converter.Converter.ConversionException { 19 | throw new UnsupportedOperationException( 20 | "Can only convert from collection to string"); 21 | } 22 | 23 | @Override 24 | public String convertToPresentation(Collection value, 25 | Class targetType, Locale locale) 26 | throws com.vaadin.data.util.converter.Converter.ConversionException { 27 | if (value == null) 28 | return ""; 29 | StringBuilder b = new StringBuilder(); 30 | for (Object o : value) { 31 | b.append(o.toString()); 32 | b.append(", "); 33 | } 34 | return b.substring(0, b.length() - 2); 35 | 36 | } 37 | 38 | @Override 39 | public Class getModelType() { 40 | return Collection.class; 41 | } 42 | 43 | @Override 44 | public Class getPresentationType() { 45 | return String.class; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/EuroConverter.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.math.BigDecimal; 4 | import java.text.DecimalFormat; 5 | import java.text.NumberFormat; 6 | import java.util.Locale; 7 | 8 | import com.vaadin.data.util.converter.StringToBigDecimalConverter; 9 | 10 | /** 11 | * A converter that adds/removes the euro sign and formats currencies with two 12 | * decimal places. 13 | */ 14 | public class EuroConverter extends StringToBigDecimalConverter { 15 | 16 | @Override 17 | public BigDecimal convertToModel(String value, 18 | Class targetType, Locale locale) 19 | throws com.vaadin.data.util.converter.Converter.ConversionException { 20 | value = value.replaceAll("[€\\s]", "").trim(); 21 | if ("".equals(value)) { 22 | value = "0"; 23 | } 24 | return super.convertToModel(value, targetType, locale); 25 | } 26 | 27 | @Override 28 | protected NumberFormat getFormat(Locale locale) { 29 | // Always display currency with two decimals 30 | NumberFormat format = super.getFormat(locale); 31 | if (format instanceof DecimalFormat) { 32 | ((DecimalFormat) format).setMaximumFractionDigits(2); 33 | ((DecimalFormat) format).setMinimumFractionDigits(2); 34 | } 35 | return format; 36 | } 37 | 38 | @Override 39 | public String convertToPresentation(BigDecimal value, 40 | Class targetType, Locale locale) 41 | throws com.vaadin.data.util.converter.Converter.ConversionException { 42 | return super.convertToPresentation(value, targetType, locale) + " €"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/NumberField.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.text.DecimalFormat; 4 | import java.text.NumberFormat; 5 | import java.util.Locale; 6 | 7 | import org.vaadin.mockapp.samples.AttributeExtension; 8 | 9 | import com.vaadin.data.util.converter.StringToIntegerConverter; 10 | import com.vaadin.ui.TextField; 11 | 12 | /** 13 | * A field for entering numbers. On touch devices, a numeric keyboard is shown 14 | * instead of the normal one. 15 | */ 16 | public class NumberField extends TextField { 17 | public NumberField() { 18 | // Mark the field as numeric. 19 | // This affects the virtual keyboard shown on mobile devices. 20 | AttributeExtension ae = new AttributeExtension(); 21 | ae.extend(this); 22 | ae.setAttribute("type", "number"); 23 | 24 | setConverter(new StringToIntegerConverter() { 25 | @Override 26 | protected NumberFormat getFormat(Locale locale) { 27 | // do not use a thousands separator, as HTML5 input type 28 | // number expects a fixed wire/DOM number format regardless 29 | // of how the browser presents it to the user (which could 30 | // depend on the browser locale) 31 | DecimalFormat format = new DecimalFormat(); 32 | format.setMaximumFractionDigits(0); 33 | format.setDecimalSeparatorAlwaysShown(false); 34 | format.setParseIntegerOnly(true); 35 | format.setGroupingUsed(false); 36 | return format; 37 | } 38 | }); 39 | } 40 | 41 | public NumberField(String caption) { 42 | this(); 43 | setCaption(caption); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/ProductForm.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.util.Collection; 4 | 5 | import org.vaadin.mockapp.samples.backend.DataService; 6 | import org.vaadin.mockapp.samples.backend.data.Availability; 7 | import org.vaadin.mockapp.samples.backend.data.Category; 8 | import org.vaadin.mockapp.samples.backend.data.Product; 9 | 10 | import com.vaadin.data.Property.ValueChangeEvent; 11 | import com.vaadin.data.Property.ValueChangeListener; 12 | import com.vaadin.data.fieldgroup.BeanFieldGroup; 13 | import com.vaadin.data.fieldgroup.FieldGroup.CommitEvent; 14 | import com.vaadin.data.fieldgroup.FieldGroup.CommitException; 15 | import com.vaadin.data.fieldgroup.FieldGroup.CommitHandler; 16 | import com.vaadin.data.util.BeanItem; 17 | import com.vaadin.server.Page; 18 | import com.vaadin.ui.Button.ClickEvent; 19 | import com.vaadin.ui.Button.ClickListener; 20 | import com.vaadin.ui.Field; 21 | import com.vaadin.ui.Notification; 22 | import com.vaadin.ui.Notification.Type; 23 | 24 | /** 25 | * A form for editing a single product. 26 | * 27 | * Using responsive layouts, the form can be displayed either sliding out on the 28 | * side of the view or filling the whole screen - see the theme for the related 29 | * CSS rules. 30 | */ 31 | public class ProductForm extends ProductFormDesign { 32 | 33 | private SampleCrudLogic viewLogic; 34 | private BeanFieldGroup fieldGroup; 35 | 36 | public ProductForm(SampleCrudLogic sampleCrudLogic) { 37 | super(); 38 | addStyleName("product-form"); 39 | viewLogic = sampleCrudLogic; 40 | 41 | price.setConverter(new EuroConverter()); 42 | 43 | for (Availability s : Availability.values()) { 44 | availability.addItem(s); 45 | } 46 | 47 | fieldGroup = new BeanFieldGroup(Product.class); 48 | fieldGroup.bindMemberFields(this); 49 | 50 | // perform validation and enable/disable buttons while editing 51 | ValueChangeListener valueListener = new ValueChangeListener() { 52 | @Override 53 | public void valueChange(ValueChangeEvent event) { 54 | formHasChanged(); 55 | } 56 | }; 57 | for (Field f : fieldGroup.getFields()) { 58 | f.addValueChangeListener(valueListener); 59 | } 60 | 61 | fieldGroup.addCommitHandler(new CommitHandler() { 62 | 63 | @Override 64 | public void preCommit(CommitEvent commitEvent) 65 | throws CommitException { 66 | } 67 | 68 | @Override 69 | public void postCommit(CommitEvent commitEvent) 70 | throws CommitException { 71 | DataService.get().updateProduct( 72 | fieldGroup.getItemDataSource().getBean()); 73 | } 74 | }); 75 | 76 | save.addClickListener(new ClickListener() { 77 | @Override 78 | public void buttonClick(ClickEvent event) { 79 | try { 80 | fieldGroup.commit(); 81 | 82 | // only if validation succeeds 83 | Product product = fieldGroup.getItemDataSource().getBean(); 84 | viewLogic.saveProduct(product); 85 | } catch (CommitException e) { 86 | Notification n = new Notification( 87 | "Please re-check the fields", Type.ERROR_MESSAGE); 88 | n.setDelayMsec(500); 89 | n.show(getUI().getPage()); 90 | } 91 | } 92 | }); 93 | 94 | cancel.addClickListener(new ClickListener() { 95 | @Override 96 | public void buttonClick(ClickEvent event) { 97 | viewLogic.cancelProduct(); 98 | } 99 | }); 100 | 101 | delete.addClickListener(new ClickListener() { 102 | @Override 103 | public void buttonClick(ClickEvent event) { 104 | Product product = fieldGroup.getItemDataSource().getBean(); 105 | viewLogic.deleteProduct(product); 106 | } 107 | }); 108 | } 109 | 110 | public void setCategories(Collection categories) { 111 | category.setOptions(categories); 112 | } 113 | 114 | public void editProduct(Product product) { 115 | if (product == null) { 116 | product = new Product(); 117 | } 118 | fieldGroup.setItemDataSource(new BeanItem(product)); 119 | 120 | // before the user makes any changes, disable validation error indicator 121 | // of the product name field (which may be empty) 122 | productName.setValidationVisible(false); 123 | 124 | // Scroll to the top 125 | // As this is not a Panel, using JavaScript 126 | String scrollScript = "window.document.getElementById('" + getId() 127 | + "').scrollTop = 0;"; 128 | Page.getCurrent().getJavaScript().execute(scrollScript); 129 | } 130 | 131 | private void formHasChanged() { 132 | // show validation errors after the user has changed something 133 | productName.setValidationVisible(true); 134 | 135 | // only products that have been saved should be removable 136 | boolean canRemoveProduct = false; 137 | BeanItem item = fieldGroup.getItemDataSource(); 138 | if (item != null) { 139 | Product product = item.getBean(); 140 | canRemoveProduct = product.getId() != -1; 141 | } 142 | delete.setEnabled(canRemoveProduct); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/ProductFormDesign.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import com.vaadin.annotations.AutoGenerated; 4 | import com.vaadin.annotations.DesignRoot; 5 | import com.vaadin.ui.Button; 6 | import com.vaadin.ui.ComboBox; 7 | import com.vaadin.ui.CssLayout; 8 | import com.vaadin.ui.TextField; 9 | import com.vaadin.ui.declarative.Design; 10 | 11 | /** 12 | * !! DO NOT EDIT THIS FILE !! 13 | * 14 | * This class is generated by Vaadin Designer and will be overwritten. 15 | * 16 | * Please make a subclass with logic and additional interfaces as needed, e.g 17 | * class LoginView extends LoginDesign implements View { … } 18 | */ 19 | @DesignRoot 20 | @AutoGenerated 21 | @SuppressWarnings("serial") 22 | public class ProductFormDesign extends CssLayout { 23 | protected TextField productName; 24 | protected TextField price; 25 | protected NumberField stockCount; 26 | protected ComboBox availability; 27 | protected CategoryField category; 28 | protected Button save; 29 | protected Button cancel; 30 | protected Button delete; 31 | 32 | public ProductFormDesign() { 33 | Design.read(this); 34 | } 35 | } -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/ProductGrid.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.util.Collection; 4 | import java.util.Locale; 5 | 6 | import org.vaadin.mockapp.samples.backend.data.Availability; 7 | import org.vaadin.mockapp.samples.backend.data.Product; 8 | 9 | import com.vaadin.data.util.BeanItem; 10 | import com.vaadin.data.util.BeanItemContainer; 11 | import com.vaadin.data.util.MethodProperty; 12 | import com.vaadin.data.util.converter.Converter; 13 | import com.vaadin.data.util.converter.StringToEnumConverter; 14 | import com.vaadin.data.util.converter.StringToIntegerConverter; 15 | import com.vaadin.data.util.filter.Or; 16 | import com.vaadin.data.util.filter.SimpleStringFilter; 17 | import com.vaadin.server.FontAwesome; 18 | import com.vaadin.ui.Grid; 19 | import com.vaadin.ui.renderers.HtmlRenderer; 20 | 21 | /** 22 | * Grid of products, handling the visual presentation and filtering of a set of 23 | * items. This version uses an in-memory data source that is suitable for small 24 | * data sets. 25 | */ 26 | public class ProductGrid extends Grid { 27 | 28 | private StringToEnumConverter availabilityConverter = new StringToEnumConverter() { 29 | @Override 30 | public String convertToPresentation(Enum availability, 31 | java.lang.Class targetType, Locale locale) 32 | throws Converter.ConversionException { 33 | String text = super.convertToPresentation(availability, targetType, 34 | locale); 35 | 36 | String color = ""; 37 | if (availability == Availability.AVAILABLE) { 38 | color = "#2dd085"; 39 | } else if (availability == Availability.COMING) { 40 | color = "#ffc66e"; 41 | } else if (availability == Availability.DISCONTINUED) { 42 | color = "#f54993"; 43 | } 44 | 45 | String iconCode = "&#x" 48 | + Integer.toHexString(FontAwesome.CIRCLE.getCodepoint()) 49 | + ";"; 50 | 51 | return iconCode + " " + text; 52 | }; 53 | }; 54 | 55 | public ProductGrid() { 56 | setSizeFull(); 57 | 58 | setSelectionMode(SelectionMode.SINGLE); 59 | 60 | BeanItemContainer container = new BeanItemContainer( 61 | Product.class); 62 | setContainerDataSource(container); 63 | setColumnOrder("id", "productName", "price", "availability", 64 | "stockCount", "category"); 65 | 66 | // Show empty stock as "-" 67 | getColumn("stockCount").setConverter(new StringToIntegerConverter() { 68 | @Override 69 | public String convertToPresentation(Integer value, 70 | java.lang.Class targetType, Locale locale) 71 | throws Converter.ConversionException { 72 | if (value == 0) { 73 | return "-"; 74 | } 75 | 76 | return super.convertToPresentation(value, targetType, locale); 77 | }; 78 | }); 79 | 80 | // Add an traffic light icon in front of availability 81 | getColumn("availability").setConverter(availabilityConverter) 82 | .setRenderer(new HtmlRenderer()); 83 | 84 | // Add " €" automatically after price 85 | getColumn("price").setConverter(new EuroConverter()); 86 | 87 | // Show categories as a comma separated list 88 | getColumn("category").setConverter(new CollectionToStringConverter()); 89 | 90 | // Align columns using a style generator and theme rule until #15438 91 | setCellStyleGenerator(new CellStyleGenerator() { 92 | 93 | @Override 94 | public String getStyle(CellReference cellReference) { 95 | if (cellReference.getPropertyId().equals("price") 96 | || cellReference.getPropertyId().equals("stockCount")) { 97 | return "align-right"; 98 | } 99 | return null; 100 | } 101 | }); 102 | } 103 | 104 | /** 105 | * Filter the grid based on a search string that is searched for in the 106 | * product name, availability and category columns. 107 | * 108 | * @param filterString 109 | * string to look for 110 | */ 111 | public void setFilter(String filterString) { 112 | getContainer().removeAllContainerFilters(); 113 | if (filterString.length() > 0) { 114 | SimpleStringFilter nameFilter = new SimpleStringFilter( 115 | "productName", filterString, true, false); 116 | SimpleStringFilter availabilityFilter = new SimpleStringFilter( 117 | "availability", filterString, true, false); 118 | SimpleStringFilter categoryFilter = new SimpleStringFilter( 119 | "category", filterString, true, false); 120 | getContainer().addContainerFilter( 121 | new Or(nameFilter, availabilityFilter, categoryFilter)); 122 | } 123 | 124 | } 125 | 126 | private BeanItemContainer getContainer() { 127 | return (BeanItemContainer) super.getContainerDataSource(); 128 | } 129 | 130 | @Override 131 | public Product getSelectedRow() throws IllegalStateException { 132 | return (Product) super.getSelectedRow(); 133 | } 134 | 135 | public void setProducts(Collection products) { 136 | getContainer().removeAllItems(); 137 | getContainer().addAll(products); 138 | } 139 | 140 | public void refresh(Product product) { 141 | // We avoid updating the whole table through the backend here so we can 142 | // get a partial update for the grid 143 | BeanItem item = getContainer().getItem(product); 144 | if (item != null) { 145 | // Updated product 146 | MethodProperty p = (MethodProperty) item.getItemProperty("id"); 147 | p.fireValueChange(); 148 | } else { 149 | // New product 150 | getContainer().addBean(product); 151 | } 152 | } 153 | 154 | public void remove(Product product) { 155 | getContainer().removeItem(product); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/SampleCrudLogic.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import org.vaadin.mockapp.MockAppUI; 4 | import org.vaadin.mockapp.samples.backend.DataService; 5 | import org.vaadin.mockapp.samples.backend.data.Product; 6 | 7 | import com.vaadin.server.Page; 8 | 9 | /** 10 | * This class provides an interface for the logical operations between the CRUD 11 | * view, its parts like the product editor form and the data source, including 12 | * fetching and saving products. 13 | * 14 | * Having this separate from the view makes it easier to test various parts of 15 | * the system separately, and to e.g. provide alternative views for the same 16 | * data. 17 | */ 18 | public class SampleCrudLogic { 19 | 20 | private SampleCrudView view; 21 | 22 | public SampleCrudLogic(SampleCrudView simpleCrudView) { 23 | view = simpleCrudView; 24 | } 25 | 26 | public void init() { 27 | editProduct(null); 28 | // Hide and disable if not admin 29 | if (!MockAppUI.get().getAccessControl().isUserInRole("admin")) { 30 | view.setNewProductEnabled(false); 31 | } 32 | 33 | view.showProducts(DataService.get().getAllProducts()); 34 | } 35 | 36 | public void cancelProduct() { 37 | setFragmentParameter(""); 38 | view.clearSelection(); 39 | view.editProduct(null); 40 | } 41 | 42 | /** 43 | * Update the fragment without causing navigator to change view 44 | */ 45 | private void setFragmentParameter(String productId) { 46 | String fragmentParameter; 47 | if (productId == null || productId.isEmpty()) { 48 | fragmentParameter = ""; 49 | } else { 50 | fragmentParameter = productId; 51 | } 52 | 53 | Page page = MockAppUI.get().getPage(); 54 | page.setUriFragment("!" + SampleCrudView.VIEW_NAME + "/" 55 | + fragmentParameter, false); 56 | } 57 | 58 | public void enter(String productId) { 59 | if (productId != null && !productId.isEmpty()) { 60 | if (productId.equals("new")) { 61 | newProduct(); 62 | } else { 63 | // Ensure this is selected even if coming directly here from 64 | // login 65 | try { 66 | int pid = Integer.parseInt(productId); 67 | Product product = findProduct(pid); 68 | view.selectRow(product); 69 | } catch (NumberFormatException e) { 70 | } 71 | } 72 | } 73 | } 74 | 75 | private Product findProduct(int productId) { 76 | return DataService.get().getProductById(productId); 77 | } 78 | 79 | public void saveProduct(Product product) { 80 | view.showSaveNotification(product.getProductName() + " (" 81 | + product.getId() + ") updated"); 82 | view.clearSelection(); 83 | view.editProduct(null); 84 | view.refreshProduct(product); 85 | setFragmentParameter(""); 86 | } 87 | 88 | public void deleteProduct(Product product) { 89 | DataService.get().deleteProduct(product.getId()); 90 | view.showSaveNotification(product.getProductName() + " (" 91 | + product.getId() + ") removed"); 92 | 93 | view.clearSelection(); 94 | view.editProduct(null); 95 | view.removeProduct(product); 96 | setFragmentParameter(""); 97 | } 98 | 99 | public void editProduct(Product product) { 100 | if (product == null) { 101 | setFragmentParameter(""); 102 | } else { 103 | setFragmentParameter(product.getId() + ""); 104 | } 105 | view.editProduct(product); 106 | } 107 | 108 | public void newProduct() { 109 | view.clearSelection(); 110 | setFragmentParameter("new"); 111 | view.editProduct(new Product()); 112 | } 113 | 114 | public void rowSelected(Product product) { 115 | if (MockAppUI.get().getAccessControl().isUserInRole("admin")) { 116 | view.editProduct(product); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/java/org/vaadin/mockapp/samples/crud/SampleCrudView.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples.crud; 2 | 3 | import java.util.Collection; 4 | 5 | import org.vaadin.mockapp.samples.ResetButtonForTextField; 6 | import org.vaadin.mockapp.samples.backend.DataService; 7 | import org.vaadin.mockapp.samples.backend.data.Product; 8 | 9 | import com.vaadin.event.FieldEvents; 10 | import com.vaadin.event.SelectionEvent; 11 | import com.vaadin.event.SelectionEvent.SelectionListener; 12 | import com.vaadin.navigator.View; 13 | import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; 14 | import com.vaadin.server.FontAwesome; 15 | import com.vaadin.ui.Alignment; 16 | import com.vaadin.ui.Button; 17 | import com.vaadin.ui.Button.ClickEvent; 18 | import com.vaadin.ui.Button.ClickListener; 19 | import com.vaadin.ui.CssLayout; 20 | import com.vaadin.ui.Grid.SelectionModel; 21 | import com.vaadin.ui.HorizontalLayout; 22 | import com.vaadin.ui.Notification; 23 | import com.vaadin.ui.Notification.Type; 24 | import com.vaadin.ui.TextField; 25 | import com.vaadin.ui.VerticalLayout; 26 | import com.vaadin.ui.themes.ValoTheme; 27 | 28 | /** 29 | * A view for performing create-read-update-delete operations on products. 30 | * 31 | * See also {@link SampleCrudLogic} for fetching the data, the actual CRUD 32 | * operations and controlling the view based on events from outside. 33 | */ 34 | public class SampleCrudView extends CssLayout implements View { 35 | 36 | public static final String VIEW_NAME = "Inventory"; 37 | private ProductGrid grid; 38 | private ProductForm form; 39 | 40 | private SampleCrudLogic viewLogic = new SampleCrudLogic(this); 41 | private Button newProduct; 42 | 43 | public SampleCrudView() { 44 | setSizeFull(); 45 | addStyleName("crud-view"); 46 | HorizontalLayout topLayout = createTopBar(); 47 | 48 | grid = new ProductGrid(); 49 | grid.addSelectionListener(new SelectionListener() { 50 | 51 | @Override 52 | public void select(SelectionEvent event) { 53 | viewLogic.rowSelected(grid.getSelectedRow()); 54 | } 55 | }); 56 | 57 | form = new ProductForm(viewLogic); 58 | form.setCategories(DataService.get().getAllCategories()); 59 | 60 | VerticalLayout barAndGridLayout = new VerticalLayout(); 61 | barAndGridLayout.addComponent(topLayout); 62 | barAndGridLayout.addComponent(grid); 63 | barAndGridLayout.setMargin(true); 64 | barAndGridLayout.setSpacing(true); 65 | barAndGridLayout.setSizeFull(); 66 | barAndGridLayout.setExpandRatio(grid, 1); 67 | barAndGridLayout.setStyleName("crud-main-layout"); 68 | 69 | addComponent(barAndGridLayout); 70 | addComponent(form); 71 | 72 | viewLogic.init(); 73 | } 74 | 75 | public HorizontalLayout createTopBar() { 76 | TextField filter = new TextField(); 77 | filter.setStyleName("filter-textfield"); 78 | filter.setInputPrompt("Filter"); 79 | ResetButtonForTextField.extend(filter); 80 | filter.setImmediate(true); 81 | filter.addTextChangeListener(new FieldEvents.TextChangeListener() { 82 | @Override 83 | public void textChange(FieldEvents.TextChangeEvent event) { 84 | grid.setFilter(event.getText()); 85 | } 86 | }); 87 | 88 | newProduct = new Button("New product"); 89 | newProduct.addStyleName(ValoTheme.BUTTON_PRIMARY); 90 | newProduct.setIcon(FontAwesome.PLUS_CIRCLE); 91 | newProduct.addClickListener(new ClickListener() { 92 | @Override 93 | public void buttonClick(ClickEvent event) { 94 | viewLogic.newProduct(); 95 | } 96 | }); 97 | 98 | HorizontalLayout topLayout = new HorizontalLayout(); 99 | topLayout.setSpacing(true); 100 | topLayout.setWidth("100%"); 101 | topLayout.addComponent(filter); 102 | topLayout.addComponent(newProduct); 103 | topLayout.setComponentAlignment(filter, Alignment.MIDDLE_LEFT); 104 | topLayout.setExpandRatio(filter, 1); 105 | topLayout.setStyleName("top-bar"); 106 | return topLayout; 107 | } 108 | 109 | @Override 110 | public void enter(ViewChangeEvent event) { 111 | viewLogic.enter(event.getParameters()); 112 | } 113 | 114 | public void showError(String msg) { 115 | Notification.show(msg, Type.ERROR_MESSAGE); 116 | } 117 | 118 | public void showSaveNotification(String msg) { 119 | Notification.show(msg, Type.TRAY_NOTIFICATION); 120 | } 121 | 122 | public void setNewProductEnabled(boolean enabled) { 123 | newProduct.setEnabled(enabled); 124 | } 125 | 126 | public void clearSelection() { 127 | grid.getSelectionModel().reset(); 128 | } 129 | 130 | public void selectRow(Product row) { 131 | ((SelectionModel.Single) grid.getSelectionModel()).select(row); 132 | } 133 | 134 | public Product getSelectedRow() { 135 | return grid.getSelectedRow(); 136 | } 137 | 138 | public void editProduct(Product product) { 139 | if (product != null) { 140 | form.addStyleName("visible"); 141 | form.setEnabled(true); 142 | } else { 143 | form.removeStyleName("visible"); 144 | form.setEnabled(false); 145 | } 146 | form.editProduct(product); 147 | } 148 | 149 | public void showProducts(Collection products) { 150 | grid.setProducts(products); 151 | } 152 | 153 | public void refreshProduct(Product product) { 154 | grid.refresh(product); 155 | grid.scrollTo(product); 156 | } 157 | 158 | public void removeProduct(Product product) { 159 | grid.remove(product); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/resources/org/vaadin/mockapp/samples/crud/ProductFormDesign.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Save 18 | Cancel 19 | Delete 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/addons.scss: -------------------------------------------------------------------------------- 1 | /* This file is automatically managed and will be overwritten from time to time. */ 2 | /* Do not manually edit this file. */ 3 | 4 | /* Import and include this mixin into your project theme to include the addon themes */ 5 | @mixin addons { 6 | } 7 | 8 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaadin/archetype-application-example/2aa89b8c56eccb41ce266f8ae7490c9bf3b3faff/mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/favicon.ico -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/img/archetype-login-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaadin/archetype-application-example/2aa89b8c56eccb41ce266f8ae7490c9bf3b3faff/mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/img/archetype-login-bg.jpg -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/img/table-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaadin/archetype-application-example/2aa89b8c56eccb41ce266f8ae7490c9bf3b3faff/mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/img/table-logo.png -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/layouts/aboutview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

About

9 | 10 |
11 | 12 | Vaadin web page 13 | 14 | 15 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/mockapp.scss: -------------------------------------------------------------------------------- 1 | // Global variable overrides. Must be declared before importing Valo. 2 | 3 | // Defines the plaintext font size, weight and family. Font size affects general component sizing. 4 | //$v-font-size: 16px; 5 | //$v-font-weight: 300; 6 | //$v-font-family: "Open Sans", sans-serif; 7 | 8 | // Defines the border used by all components. 9 | //$v-border: 1px solid (v-shade 0.7); 10 | //$v-border-radius: 4px; 11 | 12 | // Affects the color of some component elements, e.g Button, Panel title, etc 13 | //$v-background-color: hsl(210, 0%, 98%); 14 | // Affects the color of content areas, e.g Panel and Window content, TextField input etc 15 | //$v-app-background-color: $v-background-color; 16 | 17 | // Affects the visual appearance of all components 18 | //$v-gradient: v-linear 8%; 19 | //$v-bevel-depth: 30%; 20 | //$v-shadow-opacity: 5%; 21 | 22 | // Defines colors for indicating status (focus, success, failure) 23 | //$v-focus-color: valo-focus-color(); // Calculates a suitable color automatically 24 | //$v-friendly-color: #2c9720; 25 | //$v-error-indicator-color: #ed473b; 26 | 27 | // For more information, see: https://vaadin.com/book/-/page/themes.valo.html 28 | // Example variants can be copy/pasted from https://vaadin.com/wiki/-/wiki/Main/Valo+Examples 29 | 30 | $v-luminance-threshold: 180 !default; 31 | 32 | $editor-background-color: #3b3f42 !default; 33 | $valo-menu-background-color: $editor-background-color !default; 34 | 35 | $v-focus-color: rgb(96, 160, 234) !default; 36 | $v-error-indicator-color: #eb2977 !default; 37 | $v-friendly-color: rgb(54, 185, 85); 38 | 39 | $v-font-size: 15px !default; 40 | $v-font-weight: 400 !default; 41 | $v-unit-size: 32px !default; 42 | 43 | $login-info-width: 300px !default; 44 | $login-info-opacity: 0.7 !default; 45 | $login-background-color: $editor-background-color !default; 46 | // Get more background images from unsplash.com (remember to optimize the filesize) 47 | $login-background-image: "img/archetype-login-bg.jpg" !default; 48 | 49 | $editor-shadow: 0 0 10px 10px rgba(0,0,0,.1) !default; 50 | $editor-embed-background-color: darken($editor-background-color, 5%) !default; 51 | $editor-raised-background-color: lighten($editor-background-color, 10%) !default; 52 | $editor-caption-font-color: valo-font-color($editor-background-color, 0.5) !default; 53 | 54 | $v-layout-margin-top: round($v-unit-size / 1.5) !default; 55 | $v-layout-margin-right: $v-layout-margin-top !default; 56 | $v-layout-margin-bottom: $v-layout-margin-top !default; 57 | $v-layout-margin-left: $v-layout-margin-top !default; 58 | $v-layout-spacing-vertical: round($v-unit-size / 1.8) !default; 59 | $v-layout-spacing-horizontal: round($v-unit-size / 1.8) !default; 60 | 61 | 62 | @import "../valo/valo.scss"; 63 | 64 | 65 | @mixin mockapp { 66 | @include valo; 67 | 68 | 69 | // login screen - for small screens, see below 70 | .login-screen { 71 | background: $editor-background-color; 72 | 73 | @if $login-background-image { 74 | background-image: url(#{$login-background-image}); 75 | background-size: cover; 76 | background-position: 50% 50%; 77 | } 78 | 79 | width: 100%; 80 | height: 100%; 81 | 82 | .login-form { 83 | @include valo-panel-style; 84 | border: none; 85 | padding: $v-layout-margin; 86 | @include valo-animate-in-fade($duration: 1s); 87 | } 88 | 89 | .login-information { 90 | display: inline-block; 91 | position: absolute; 92 | top: 0; 93 | left: 0; 94 | width: $login-info-width; 95 | height: 100%; 96 | background: $v-selection-color; // For IE8 97 | background-color: rgba($v-selection-color, $login-info-opacity); 98 | padding: $v-layout-margin; 99 | color: valo-font-color($v-selection-color, 0.9); 100 | @include animation(valo-animate-in-fade 1s 1s backwards); 101 | 102 | h1 { 103 | color: inherit; 104 | } 105 | } 106 | 107 | .centering-layout { 108 | display: inline-block; 109 | width: 100%; 110 | height: 100%; 111 | padding-left: $login-info-width; 112 | 113 | .v-slot { 114 | height: 100%; 115 | } 116 | } 117 | } 118 | 119 | // makes the CRUD view keep the sidebar editor within the view as sidebar is absolutely positioned 120 | .crud-view { 121 | position: relative; 122 | 123 | .filter-textfield { 124 | width: round($v-unit-size * 9); 125 | } 126 | } 127 | 128 | // sidebar editor for CRUD, scrolls if there is not enough space vertically 129 | .product-form-wrapper { 130 | position: absolute; 131 | top: 0; 132 | bottom: 0; 133 | right: 0; 134 | z-index: 100; 135 | width: round($v-unit-size * 9); 136 | height: 100%; 137 | overflow: auto; 138 | padding: $v-layout-spacing-vertical $v-layout-spacing-horizontal; 139 | background-color: $editor-background-color; 140 | color: valo-font-color($editor-background-color, 0.8); 141 | 142 | // Set the context color for the style mixins 143 | $temp: $v-app-background-color; 144 | $v-app-background-color: $editor-background-color; 145 | 146 | .v-textfield { 147 | @include valo-textfield-style($background-color: $editor-embed-background-color); 148 | } 149 | 150 | .v-checkbox { 151 | @include valo-checkbox-style($background-color: $editor-raised-background-color); 152 | } 153 | 154 | .v-filterselect { 155 | @include valo-combobox-style($background-color: $editor-raised-background-color, $bevel: $v-bevel, $gradient: $v-gradient); 156 | } 157 | 158 | // Restore variable 159 | $v-app-background-color: $temp; 160 | 161 | .v-button { 162 | display: block; 163 | } 164 | 165 | .v-caption { 166 | color: $editor-caption-font-color; 167 | } 168 | 169 | // try to ensure there is space under the last button also on small displays (does not work on IE8) 170 | .form-layout > .v-expand > .v-slot:last-child { 171 | padding-bottom: $v-layout-spacing-vertical; 172 | } 173 | 174 | } 175 | 176 | .product-form { 177 | right: 0; 178 | @include transition(all 300ms); 179 | @include transform(translatex(100%)); 180 | } 181 | 182 | // Enables animation for opening CRUD editor 183 | .visible { 184 | @include transform(none); 185 | @include box-shadow($editor-shadow); 186 | } 187 | 188 | // About view 189 | 190 | .about-view { 191 | overflow: auto; 192 | 193 | .about-content { 194 | @include valo-panel-style; 195 | max-width: 500px; 196 | // Override the default of CustomLayout 197 | padding: $v-unit-size !important; 198 | } 199 | } 200 | 201 | // Style rules for smaller display sizes 202 | 203 | // No top menu on the login view, login screen layout changes 204 | .v-ui[width-range~="0-800px"] { 205 | 206 | .main-screen { 207 | padding-top: $v-unit-size; 208 | } 209 | // TODO also move loading indicator if using the hack above 210 | 211 | // More compact login screen 212 | .login-screen { 213 | height: auto; 214 | min-height: 100%; 215 | 216 | .login-information { 217 | position: static; 218 | width: 100%; 219 | height: auto; 220 | 221 | .v-label { 222 | text-align: center; 223 | 224 | h1 { 225 | margin-top: .4em; 226 | } 227 | } 228 | } 229 | 230 | .centering-layout { 231 | display: block; 232 | width: 100%; 233 | height: auto; 234 | padding-left: 0; 235 | padding-top: 60px; 236 | padding-bottom: 60px; 237 | } 238 | 239 | .login-form { 240 | width: 400px; 241 | max-width: 100%; 242 | 243 | table { 244 | width: 100%; 245 | } 246 | 247 | .v-textfield { 248 | width: 100% !important; 249 | } 250 | 251 | .v-formlayout-captioncell, 252 | .v-formlayout-contentcell, 253 | .v-formlayout-errorcell { 254 | display: block; 255 | text-align: center; 256 | padding-top: 0; 257 | } 258 | .buttons { 259 | width: 100%; 260 | .v-button { 261 | display: block; 262 | text-align: center; 263 | } 264 | } 265 | } 266 | } 267 | } 268 | 269 | // hide the logo for a more compact header when the menu is narrow 270 | .v-ui[width-range~="801px-1100px"] .valo-menu-part { 271 | .v-slot-logo, 272 | .v-slot-logo + .v-spacing { 273 | display: none; 274 | } 275 | } 276 | 277 | // Move logout button to the bottom of the menu on large screens 278 | .v-ui[width-range~="801px-"] .valo-menu-part { 279 | .user-menu { 280 | position: fixed; 281 | bottom: 0; 282 | margin-bottom: 0; 283 | } 284 | } 285 | 286 | // Editor should take whole browser width when we are under 550px in width. 287 | .v-ui[width-range~="0-550px"] { 288 | .product-form-wrapper { 289 | width:100%; 290 | } 291 | 292 | // Remove margins around the grid and reduce top bar margins on small screens 293 | .crud-view .crud-main-layout { 294 | padding: 0 0 0 0; 295 | 296 | .top-bar { 297 | // Use spacing to the grid below as the margin - smaller than default margin 298 | padding: $v-layout-spacing-vertical $v-layout-spacing-horizontal 0 $v-layout-spacing-horizontal; 299 | 300 | .filter-textfield { 301 | width: 100%; 302 | } 303 | } 304 | } 305 | 306 | // About view fills the display on small screens 307 | .about-view { 308 | padding-bottom: 0; 309 | padding-top: 0; 310 | height: auto; 311 | padding: 0.1 * $v-unit-size; 312 | 313 | .v-slot-about-content { 314 | overflow: auto; 315 | } 316 | 317 | .about-content { 318 | width: 100%; 319 | max-width: 100%; 320 | height: auto; 321 | vertical-align: top; 322 | padding: 0; 323 | background: transparent; 324 | @include box-shadow(none); 325 | } 326 | } 327 | } 328 | 329 | // Override valo default narrow menu button on small screens to have the full logout text visible 330 | .v-ui[width-range~="0-500px"] { 331 | .valo-menu .v-menubar-user-menu .v-menubar-menuitem-caption { 332 | width: auto; 333 | } 334 | } 335 | 336 | // Hide spinner buttons from number input 337 | input[type=number]::-webkit-inner-spin-button, 338 | input[type=number]::-webkit-outer-spin-button { 339 | -webkit-appearance: none; 340 | margin: 0; 341 | } 342 | 343 | // For aligning in grid until #15438 is done 344 | .align-right { 345 | text-align: right; 346 | } 347 | 348 | } 349 | -------------------------------------------------------------------------------- /mockapp-ui/src/main/webapp/VAADIN/themes/mockapp/styles.scss: -------------------------------------------------------------------------------- 1 | @import "mockapp.scss"; 2 | @import "addons.scss"; 3 | 4 | // This should be in the Valo theme as a shorthand 5 | $v-layout-margin: $v-layout-margin-top $v-layout-margin-right $v-layout-margin-bottom $v-layout-margin-left !default; 6 | 7 | // This file prefixes all rules with the theme name to avoid causing conflicts with other themes. 8 | // The actual styles should be defined in mockapp.scss 9 | 10 | .mockapp { 11 | @include addons; 12 | @include mockapp; 13 | } 14 | -------------------------------------------------------------------------------- /mockapp-widgetset/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | mockapp-parent 6 | org.vaadin.maven.mockapp 7 | 1.0-SNAPSHOT 8 | 9 | 4.0.0 10 | 11 | mockapp-widgetset 12 | mockapp-widgetset 13 | jar 14 | 15 | 16 | 17 | 18 | com.vaadin 19 | vaadin-client 20 | 22 | 23 | 24 | 25 | com.vaadin 26 | vaadin-client-compiler 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | com.vaadin 37 | vaadin-maven-plugin 38 | 39 | -Xmx512M -Xss1024k 40 | ${basedir}/target/classes/VAADIN/widgetsets 41 | true 42 | false 43 | 44 | true 45 | 46 | 47 | 48 | 49 | update-widgetset 50 | compile 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-source-plugin 58 | 59 | 60 | 61 | jar 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-jar-plugin 69 | 70 | 71 | 72 | 1 73 | org.vaadin.mockapp.MockAppWidgetset 74 | 75 | 76 | 78 | 79 | VAADIN/gwt-unitCache/** 80 | VAADIN/widgetsets/WEB-INF/** 81 | 82 | 83 | 84 | 85 | 86 | 87 | 89 | 91 | 92 | org.eclipse.m2e 93 | lifecycle-mapping 94 | 1.0.0 95 | 96 | 97 | 98 | 99 | 100 | com.vaadin 101 | 102 | vaadin-maven-plugin 103 | 104 | 105 | [7.1.11,) 106 | 107 | 108 | resources 109 | 110 | update-widgetset 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/java/org/vaadin/mockapp/client/samples/ResetButtonForTextFieldConnector.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.client.samples; 2 | 3 | import com.google.gwt.core.client.Scheduler; 4 | import com.google.gwt.dom.client.Element; 5 | import com.google.gwt.dom.client.Style; 6 | import com.google.gwt.event.dom.client.KeyUpEvent; 7 | import com.google.gwt.event.dom.client.KeyUpHandler; 8 | import com.google.gwt.event.logical.shared.AttachEvent; 9 | import com.google.gwt.user.client.DOM; 10 | import com.vaadin.client.ComponentConnector; 11 | import com.vaadin.client.ServerConnector; 12 | import com.vaadin.client.communication.StateChangeEvent; 13 | import com.vaadin.client.extensions.AbstractExtensionConnector; 14 | import com.vaadin.client.ui.VTextField; 15 | import com.vaadin.shared.ui.Connect; 16 | import org.vaadin.mockapp.samples.ResetButtonForTextField; 17 | 18 | /** 19 | * Client side implementation of {@link ResetButtonForTextField}. 20 | * 21 | * @see Extending components 22 | * in Vaadin 7 23 | */ 24 | @Connect(ResetButtonForTextField.class) 25 | public class ResetButtonForTextFieldConnector extends 26 | AbstractExtensionConnector implements KeyUpHandler, AttachEvent.Handler { 27 | 28 | public static final String CLASSNAME = "resetbuttonfortextfield"; 29 | private VTextField textField; 30 | private Element resetButtonElement; 31 | 32 | @Override 33 | protected void extend(ServerConnector serverConnector) { 34 | serverConnector 35 | .addStateChangeHandler(new StateChangeEvent.StateChangeHandler() { 36 | @Override 37 | public void onStateChanged(StateChangeEvent stateChangeEvent) { 38 | Scheduler.get().scheduleDeferred( 39 | new Scheduler.ScheduledCommand() { 40 | @Override 41 | public void execute() { 42 | updateResetButtonVisibility(); 43 | } 44 | }); 45 | } 46 | }); 47 | 48 | textField = (VTextField) ((ComponentConnector) serverConnector) 49 | .getWidget(); 50 | textField.addStyleName(CLASSNAME + "-textfield"); 51 | 52 | resetButtonElement = DOM.createDiv(); 53 | resetButtonElement.addClassName(CLASSNAME + "-resetbutton"); 54 | 55 | textField.addAttachHandler(this); 56 | textField.addKeyUpHandler(this); 57 | } 58 | 59 | private void updateResetButtonVisibility() { 60 | if (textField.getValue().isEmpty() 61 | || textField.getStyleName().contains("v-textfield-prompt")) { 62 | resetButtonElement.getStyle().setDisplay(Style.Display.NONE); 63 | } else { 64 | resetButtonElement.getStyle().clearDisplay(); 65 | } 66 | } 67 | 68 | public native void addResetButtonClickListener(Element el) 69 | /*-{ 70 | var self = this; 71 | el.onclick = $entry(function () { 72 | self.@org.vaadin.mockapp.client.samples.ResetButtonForTextFieldConnector::clearTextField()(); 73 | }); 74 | }-*/; 75 | 76 | public native void removeResetButtonClickListener(Element el) 77 | /*-{ 78 | el.onclick = null; 79 | }-*/; 80 | 81 | @Override 82 | public void onKeyUp(KeyUpEvent keyUpEvent) { 83 | updateResetButtonVisibility(); 84 | } 85 | 86 | @Override 87 | public void onAttachOrDetach(AttachEvent attachEvent) { 88 | if (attachEvent.isAttached()) { 89 | textField.getElement().getParentElement() 90 | .insertAfter(resetButtonElement, textField.getElement()); 91 | updateResetButtonVisibility(); 92 | addResetButtonClickListener(resetButtonElement); 93 | } else { 94 | Element parentElement = resetButtonElement.getParentElement(); 95 | if (parentElement != null) { 96 | parentElement.removeChild(resetButtonElement); 97 | } 98 | removeResetButtonClickListener(resetButtonElement); 99 | } 100 | } 101 | 102 | private void clearTextField() { 103 | textField.setValue(""); 104 | textField.valueChange(true); 105 | updateResetButtonVisibility(); 106 | textField.getElement().focus(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/java/org/vaadin/mockapp/samples/AttributeExtension.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples; 2 | 3 | import com.vaadin.annotations.JavaScript; 4 | import com.vaadin.server.AbstractJavaScriptExtension; 5 | import com.vaadin.ui.TextField; 6 | 7 | /** 8 | * A JavaScript extension for adding arbitrary HTML attributes for components. 9 | */ 10 | @JavaScript("attribute_extension_connector.js") 11 | public class AttributeExtension extends AbstractJavaScriptExtension { 12 | 13 | public void extend(TextField target) { 14 | super.extend(target); 15 | } 16 | 17 | @Override 18 | protected AttributeExtensionState getState() { 19 | return (AttributeExtensionState) super.getState(); 20 | } 21 | 22 | public void setAttribute(String attribute, String value) { 23 | getState().attributes.put(attribute, value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/java/org/vaadin/mockapp/samples/AttributeExtensionState.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples; 2 | 3 | import com.vaadin.shared.JavaScriptExtensionState; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Shared state class for {@link AttributeExtension} communication from server 9 | * to client. 10 | */ 11 | public class AttributeExtensionState extends JavaScriptExtensionState { 12 | public HashMap attributes = new HashMap(); 13 | } 14 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/java/org/vaadin/mockapp/samples/ResetButtonForTextField.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.mockapp.samples; 2 | 3 | import com.vaadin.server.AbstractClientConnector; 4 | import com.vaadin.server.AbstractExtension; 5 | import com.vaadin.ui.TextField; 6 | 7 | /** 8 | * An extension adding a button in a text field for clearing the field. Only 9 | * shown when the text field is non-empty. 10 | * 11 | * @see Extending components 12 | * in Vaadin 7 13 | */ 14 | public class ResetButtonForTextField extends AbstractExtension { 15 | 16 | public static void extend(TextField field) { 17 | new ResetButtonForTextField().extend((AbstractClientConnector) field); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/MockAppWidgetset.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/resetbutton-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaadin/archetype-application-example/2aa89b8c56eccb41ce266f8ae7490c9bf3b3faff/mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/resetbutton-default.png -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/resetbutton-default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 57 | 67 | 71 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/resetbutton-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaadin/archetype-application-example/2aa89b8c56eccb41ce266f8ae7490c9bf3b3faff/mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/resetbutton-hover.png -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/resetbutton-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 57 | 67 | 71 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/public/resetbuttonfortextfield/styles.css: -------------------------------------------------------------------------------- 1 | .resetbuttonfortextfield-textfield { 2 | padding-right: 16pt !important; 3 | } 4 | 5 | .resetbuttonfortextfield-resetbutton { 6 | position: relative; 7 | display: inline-block; 8 | vertical-align: middle; 9 | right: 18pt; 10 | margin-right: -18pt; 11 | cursor: pointer; 12 | width: 16px; 13 | height: 16px; 14 | background-image: url(resetbutton-default.svg); 15 | background-position: center center; 16 | background-repeat: no-repeat; 17 | } 18 | 19 | .resetbuttonfortextfield-resetbutton:hover { 20 | background-image: url(resetbutton-hover.svg); 21 | cursor: pointer; 22 | } 23 | 24 | .v-ie8 .resetbuttonfortextfield-resetbutton { 25 | background-image: url(resetbutton-default.png); 26 | } 27 | 28 | .v-ie8 .resetbuttonfortextfield-resetbutton:hover { 29 | background-image: url(resetbutton-hover.png); 30 | } 31 | -------------------------------------------------------------------------------- /mockapp-widgetset/src/main/resources/org/vaadin/mockapp/samples/attribute_extension_connector.js: -------------------------------------------------------------------------------- 1 | window.org_vaadin_mockapp_samples_AttributeExtension = function() { 2 | 3 | this.onStateChange = function() { 4 | var element = this.getElement(this.getParentId()); 5 | if (element) { 6 | var attributes = this.getState().attributes; 7 | for (var attr in attributes) { 8 | if (attributes.hasOwnProperty(attr)) { 9 | element.setAttribute(attr, attributes[attr]); 10 | } 11 | } 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.vaadin.maven.mockapp 7 | mockapp-parent 8 | pom 9 | 1.0-SNAPSHOT 10 | mockapp-parent 11 | 12 | 13 | 14 | UNLICENSE 15 | http://unlicense.org/ 16 | repo 17 | 18 | 19 | 20 | 21 | mockapp-widgetset 22 | mockapp-backend 23 | mockapp-ui 24 | mockapp-production 25 | 26 | 27 | 28 | 7.4.3 29 | ${vaadin.version} 30 | 9.2.3.v20140905 31 | 1.7 32 | 1.7 33 | UTF-8 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-compiler-plugin 41 | 3.0 42 | 43 | ${project.encoding} 44 | ${project.source.version} 45 | ${project.target.version} 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-resources-plugin 51 | 2.6 52 | 53 | ${project.encoding} 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-war-plugin 62 | 2.3 63 | 64 | false 65 | 66 | 67 | 68 | com.vaadin 69 | vaadin-maven-plugin 70 | ${vaadin.plugin.version} 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-jar-plugin 75 | 2.5 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-source-plugin 80 | 2.4 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | javax.servlet 90 | javax.servlet-api 91 | 3.0.1 92 | provided 93 | 94 | 95 | com.vaadin 96 | vaadin-bom 97 | ${vaadin.version} 98 | pom 99 | import 100 | 101 | 102 | 103 | 104 | 105 | 106 | vaadin-addons 107 | https://maven.vaadin.com/vaadin-addons 108 | 109 | 110 | vaadin-snapshots 111 | https://oss.sonatype.org/content/repositories/vaadin-snapshots/ 112 | 113 | false 114 | 115 | 116 | true 117 | 118 | 119 | 120 | 121 | 122 | --------------------------------------------------------------------------------