| @ -0,0 +1,24 @@ | |||||
| This is free and unencumbered software released into the public domain. | |||||
| Anyone is free to copy, modify, publish, use, compile, sell, or | |||||
| distribute this software, either in source code form or as a compiled | |||||
| binary, for any purpose, commercial or non-commercial, and by any | |||||
| means. | |||||
| In jurisdictions that recognize copyright laws, the author or authors | |||||
| of this software dedicate any and all copyright interest in the | |||||
| software to the public domain. We make this dedication for the benefit | |||||
| of the public at large and to the detriment of our heirs and | |||||
| successors. We intend this dedication to be an overt act of | |||||
| relinquishment in perpetuity of all present and future rights to this | |||||
| software under copyright law. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||||
| IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |||||
| OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |||||
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||||
| OTHER DEALINGS IN THE SOFTWARE. | |||||
| For more information, please refer to <http://unlicense.org> | |||||
| @ -0,0 +1,43 @@ | |||||
| # Schachliga DeLuxe | |||||
| This is an example project that can be used as a starting point to create your own Vaadin application with Spring Boot. | |||||
| It contains all the necessary configuration and some placeholder files to get you started. | |||||
| The project is a standard Maven project, so you can import it to your IDE of choice. [Read more how to set up a development environment](https://vaadin.com/docs/v14/flow/installing/installing-overview.html) for Vaadin projects (Windows, Linux, macOS). | |||||
| This project was created from https://start.vaadin.com. | |||||
| ## Running and debugging the application | |||||
| ### Running the application from the command line. | |||||
| To run from the command line, use `mvn` and open http://localhost:8080 in your browser. | |||||
| ### Running and debugging the application in Intellij IDEA | |||||
| - Locate the Application.java class in the Project view. It is in the src folder, under the main package's root. | |||||
| - Right-click on the Application class | |||||
| - Select "Debug 'Application.main()'" from the list | |||||
| After the application has started, you can view it at http://localhost:8080/ in your browser. | |||||
| You can now also attach breakpoints in code for debugging purposes, by clicking next to a line number in any source file. | |||||
| ### Running and debugging the application in Eclipse | |||||
| - Locate the Application.java class in the Package Explorer. It is in `src/main/java`, under the main package. | |||||
| - Right-click on the file and select `Debug As` --> `Java Application`. | |||||
| Do not worry if the debugger breaks at a `SilentExitException`. This is a Spring Boot feature and happens on every startup. | |||||
| After the application has started, you can view it at http://localhost:8080/ in your browser. | |||||
| You can now also attach breakpoints in code for debugging purposes, by clicking next to a line number in any source file. | |||||
| ## Project structure | |||||
| - `MainView.java` in `src/main/java` contains the navigation setup. It uses [App Layout](https://vaadin.com/components/vaadin-app-layout). | |||||
| - `views` package in `src/main/java` contains the server-side Java views of your application. | |||||
| - `views` folder in `frontend/` contains the client-side JavaScript views of your application. | |||||
| ## What next? | |||||
| [vaadin.com](https://vaadin.com) has lots of material to help you get you started: | |||||
| - Follow the tutorials in [vaadin.com/tutorials](https://vaadin.com/tutorials). Especially [vaadin.com/tutorials/getting-started-with-flow](https://vaadin.com/tutorials/getting-started-with-flow) is good for getting a grasp of the basic Vaadin concepts. | |||||
| - Read the documentation in [vaadin.com/docs](https://vaadin.com/docs). | |||||
| - For a bigger Vaadin application example, check out the Full Stack App starter from [vaadin.com/start](https://vaadin.com/start). | |||||
| @ -0,0 +1,36 @@ | |||||
| import { PolymerElement } from '@polymer/polymer/polymer-element'; | |||||
| import { html } from '@polymer/polymer/lib/utils/html-tag'; | |||||
| import * as L from 'leaflet'; | |||||
| const openStreetMapLayer = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; | |||||
| const openStreetMapAttribution = `© <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors`; | |||||
| export class LeafletMap extends PolymerElement { | |||||
| _attachDom(dom) { | |||||
| // Do not use a shadow root | |||||
| this.appendChild(dom); | |||||
| } | |||||
| render() { | |||||
| return html``; | |||||
| } | |||||
| ready() { | |||||
| super.ready(); | |||||
| this.map = L.map(this); | |||||
| let tileLayer = L.tileLayer(openStreetMapLayer, { | |||||
| attribution: openStreetMapAttribution, | |||||
| maxZoom: 13, | |||||
| }); | |||||
| tileLayer.addTo(this.map); | |||||
| } | |||||
| async setView(latitude, longitude, zoomLevel) { | |||||
| this.map.setView([latitude, longitude], zoomLevel); | |||||
| } | |||||
| static get is() { | |||||
| return 'leaflet-map'; | |||||
| } | |||||
| } | |||||
| customElements.define(LeafletMap.is, LeafletMap); | |||||
| @ -0,0 +1,20 @@ | |||||
| // eagerly import theme styles so as we can override them | |||||
| import '@vaadin/vaadin-lumo-styles/all-imports'; | |||||
| const $_documentContainer = document.createElement('template'); | |||||
| $_documentContainer.innerHTML = ` | |||||
| <custom-style> | |||||
| <style include='lumo-badge'> | |||||
| html { | |||||
| --lumo-body-text-color: #fff; | |||||
| } | |||||
| </style> | |||||
| </custom-style> | |||||
| `; | |||||
| document.head.appendChild($_documentContainer.content); | |||||
| @ -0,0 +1,3 @@ | |||||
| .about-view { | |||||
| display: block; | |||||
| } | |||||
| @ -0,0 +1,10 @@ | |||||
| .address-form-view { | |||||
| display: block; | |||||
| margin: 0 auto; | |||||
| max-width: 1024px; | |||||
| padding: 0 var(--lumo-space-l); | |||||
| } | |||||
| .address-form-view .button-layout { | |||||
| margin-bottom: var(--lumo-space-l); | |||||
| margin-top: var(--lumo-space-m); | |||||
| } | |||||
| @ -0,0 +1,70 @@ | |||||
| .card-list-view { | |||||
| display: block; | |||||
| height: 100%; | |||||
| } | |||||
| .card-list-view vaadin-grid { | |||||
| height: 100%; | |||||
| line-height: var(--lumo-line-height-m); | |||||
| } | |||||
| .card-list-view vaadin-grid, | |||||
| .card-list-view vaadin-grid-cell-content { | |||||
| background-color: var(--lumo-contrast-10pct); | |||||
| } | |||||
| .card-list-view .card { | |||||
| background-color: var(--lumo-base-color); | |||||
| border-radius: var(--lumo-border-radius); | |||||
| box-shadow: var(--lumo-box-shadow-xs); | |||||
| padding: calc(var(--lumo-space-s) * 1.5) var(--lumo-space-m); | |||||
| } | |||||
| .card-list-view img { | |||||
| border-radius: 50%; | |||||
| flex-shrink: 0; | |||||
| height: var(--lumo-size-m); | |||||
| margin-right: calc(var(--lumo-space-s) * 1.5); | |||||
| width: var(--lumo-size-m); | |||||
| } | |||||
| .card-list-view .header { | |||||
| align-items: baseline; | |||||
| } | |||||
| .card-list-view .name { | |||||
| font-size: var(--lumo-font-size-s); | |||||
| font-weight: bold; | |||||
| margin-right: var(--lumo-space-s); | |||||
| } | |||||
| .card-list-view .date { | |||||
| color: var(--lumo-tertiary-text-color); | |||||
| font-size: var(--lumo-font-size-xs); | |||||
| } | |||||
| .card-list-view .post { | |||||
| color: var(--lumo-secondary-text-color); | |||||
| font-size: var(--lumo-font-size-s); | |||||
| margin-bottom: var(--lumo-space-s); | |||||
| white-space: normal; | |||||
| } | |||||
| .card-list-view .actions { | |||||
| align-items: center; | |||||
| } | |||||
| .card-list-view iron-icon { | |||||
| color: var(--lumo-tertiary-text-color); | |||||
| height: calc(var(--lumo-icon-size-s) * 0.8); | |||||
| margin-right: var(--lumo-space-s); | |||||
| width: calc(var(--lumo-icon-size-s) * 0.8); | |||||
| } | |||||
| .card-list-view .likes, | |||||
| .card-list-view .comments, | |||||
| .card-list-view .shares { | |||||
| color: var(--lumo-tertiary-text-color); | |||||
| font-size: var(--lumo-font-size-xs); | |||||
| margin-right: var(--lumo-space-l); | |||||
| } | |||||
| @ -0,0 +1,10 @@ | |||||
| .credit-card-form-view { | |||||
| display: block; | |||||
| margin: 0 auto; | |||||
| max-width: 1024px; | |||||
| padding: 0 var(--lumo-space-l); | |||||
| } | |||||
| .credit-card-form-view .button-layout { | |||||
| margin-bottom: var(--lumo-space-l); | |||||
| margin-top: var(--lumo-space-m); | |||||
| } | |||||
| @ -0,0 +1,4 @@ | |||||
| .hello-world-view { | |||||
| display: block; | |||||
| padding: 1em; | |||||
| } | |||||
| @ -0,0 +1,45 @@ | |||||
| #header { | |||||
| height: var(--lumo-size-xl); | |||||
| box-shadow: var(--lumo-box-shadow-s); | |||||
| } | |||||
| #header vaadin-avatar { | |||||
| margin-left: auto; | |||||
| margin-right: var(--lumo-space-m); | |||||
| } | |||||
| vaadin-app-layout[dir='rtl'] #header vaadin-avatar { | |||||
| margin-left: var(--lumo-space-m); | |||||
| margin-right: auto; | |||||
| } | |||||
| #header h1 { | |||||
| font-size: var(--lumo-font-size-l); | |||||
| margin: 0; | |||||
| } | |||||
| #logo { | |||||
| box-sizing: border-box; | |||||
| box-shadow: inset 0 -1px var(--lumo-contrast-10pct); | |||||
| padding: var(--lumo-space-s) var(--lumo-space-m); | |||||
| } | |||||
| #logo img { | |||||
| height: calc(var(--lumo-size-l) * 1.5); | |||||
| } | |||||
| #logo h1 { | |||||
| font-size: var(--lumo-font-size-xl); | |||||
| font-weight: 600; | |||||
| margin: 0 var(--lumo-space-s); | |||||
| } | |||||
| vaadin-tab { | |||||
| font-size: var(--lumo-font-size-s); | |||||
| height: var(--lumo-size-l); | |||||
| font-weight: 600; | |||||
| color: var(--lumo-body-text-color); | |||||
| } | |||||
| vaadin-tab:hover { | |||||
| background-color: var(--lumo-contrast-5pct); | |||||
| } | |||||
| vaadin-tab[selected] { | |||||
| background-color: var(--lumo-primary-color-10pct); | |||||
| color: var(--lumo-primary-text-color); | |||||
| } | |||||
| @ -0,0 +1,8 @@ | |||||
| .map-view { | |||||
| display: flex; | |||||
| height: 100%; | |||||
| } | |||||
| .map-view .map { | |||||
| flex: 1; | |||||
| } | |||||
| @ -0,0 +1,33 @@ | |||||
| .master-detail-view { | |||||
| display: flex; | |||||
| height: 100%; | |||||
| flex-direction: column; | |||||
| } | |||||
| .master-detail-view .grid-wrapper { | |||||
| flex-grow: 1; | |||||
| width: 100%; | |||||
| } | |||||
| .master-detail-view .full-size { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| .master-detail-view #editor-layout { | |||||
| width: 400px; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| } | |||||
| .master-detail-view #editor { | |||||
| padding: var(--lumo-space-l); | |||||
| flex-grow: 1; | |||||
| } | |||||
| .master-detail-view #button-layout { | |||||
| width: 100%; | |||||
| flex-wrap: wrap; | |||||
| background-color: var(--lumo-contrast-5pct); | |||||
| padding: var(--lumo-space-s) var(--lumo-space-l); | |||||
| } | |||||
| @ -0,0 +1,10 @@ | |||||
| .person-form-view { | |||||
| display: block; | |||||
| margin: 0 auto; | |||||
| max-width: 1024px; | |||||
| padding: 0 var(--lumo-space-l); | |||||
| } | |||||
| .person-form-view .button-layout { | |||||
| margin-bottom: var(--lumo-space-l); | |||||
| margin-top: var(--lumo-space-m); | |||||
| } | |||||
| @ -0,0 +1,123 @@ | |||||
| { | |||||
| "name": "no-name", | |||||
| "license": "UNLICENSED", | |||||
| "vaadin": { | |||||
| "dependencies": { | |||||
| "lit-element": "^2.2.1", | |||||
| "@polymer/polymer": "3.2.0", | |||||
| "@webcomponents/webcomponentsjs": "^2.2.10", | |||||
| "@vaadin/vaadin-crud": "1.3.0", | |||||
| "@vaadin/vaadin-grid": "5.7.9", | |||||
| "@vaadin/vaadin-icons": "4.3.2", | |||||
| "@vaadin/vaadin-split-layout": "4.3.0", | |||||
| "@vaadin/vaadin-combo-box": "5.4.7", | |||||
| "@vaadin/vaadin-cookie-consent": "1.2.0", | |||||
| "@vaadin/vaadin-core-shrinkwrap": "14.4.8", | |||||
| "@vaadin/vaadin-upload": "4.4.1", | |||||
| "@vaadin/vaadin-dialog": "2.5.2", | |||||
| "@vaadin/vaadin-select": "2.4.0", | |||||
| "@vaadin/vaadin-app-layout": "2.2.0", | |||||
| "@vaadin/vaadin-item": "2.3.0", | |||||
| "@vaadin/vaadin-board": "2.2.0", | |||||
| "@vaadin/vaadin-charts": "6.3.1", | |||||
| "@vaadin/vaadin-notification": "1.6.1", | |||||
| "@vaadin/vaadin-grid-pro": "2.2.2", | |||||
| "@vaadin/vaadin-progress-bar": "1.3.0", | |||||
| "@vaadin/vaadin-shrinkwrap": "14.4.8", | |||||
| "@vaadin/vaadin-date-time-picker": "1.4.0", | |||||
| "@vaadin/vaadin-ordered-layout": "1.4.0", | |||||
| "@vaadin/vaadin-login": "1.2.0", | |||||
| "@vaadin/vaadin-button": "2.4.0", | |||||
| "@vaadin/vaadin-date-picker": "4.4.1", | |||||
| "@vaadin/vaadin-text-field": "2.8.3", | |||||
| "@vaadin/vaadin-menu-bar": "1.2.1", | |||||
| "@vaadin/vaadin-custom-field": "1.3.0", | |||||
| "@vaadin/vaadin-form-layout": "2.3.0", | |||||
| "@vaadin/vaadin-confirm-dialog": "1.3.0", | |||||
| "@polymer/iron-list": "3.1.0", | |||||
| "@vaadin/vaadin-accordion": "1.2.0", | |||||
| "@vaadin/vaadin-list-box": "1.4.0", | |||||
| "@vaadin/vaadin-checkbox": "2.5.0", | |||||
| "@vaadin/vaadin-details": "1.2.0", | |||||
| "@polymer/iron-icon": "3.0.1", | |||||
| "@vaadin/vaadin-time-picker": "2.4.0", | |||||
| "@vaadin/vaadin-context-menu": "4.5.0", | |||||
| "@vaadin/vaadin-radio-button": "1.5.1", | |||||
| "@vaadin/vaadin-tabs": "3.2.0", | |||||
| "@vaadin/vaadin-lumo-styles": "1.6.0", | |||||
| "@vaadin/vaadin-material-styles": "1.3.2", | |||||
| "@vaadin/vaadin-rich-text-editor": "1.3.0" | |||||
| }, | |||||
| "devDependencies": { | |||||
| "webpack-babel-multi-target-plugin": "2.3.3", | |||||
| "copy-webpack-plugin": "5.1.2", | |||||
| "compression-webpack-plugin": "4.0.1", | |||||
| "raw-loader": "3.1.0", | |||||
| "webpack-cli": "3.3.11", | |||||
| "webpack": "4.42.0", | |||||
| "chokidar": "^3.5.0", | |||||
| "webpack-merge": "4.2.2", | |||||
| "webpack-dev-server": "3.11.0" | |||||
| }, | |||||
| "hash": "81077bf7aa0e1c9065dd7e9ca4953a7e706e0df572c3312c0a71a8a737a7af44" | |||||
| }, | |||||
| "dependencies": { | |||||
| "lit-element": "^2.2.1", | |||||
| "@polymer/polymer": "3.2.0", | |||||
| "@webcomponents/webcomponentsjs": "^2.2.10", | |||||
| "@vaadin/vaadin-crud": "1.3.0", | |||||
| "@vaadin/vaadin-grid": "5.7.9", | |||||
| "@vaadin/vaadin-icons": "4.3.2", | |||||
| "@vaadin/vaadin-split-layout": "4.3.0", | |||||
| "@vaadin/vaadin-combo-box": "5.4.7", | |||||
| "@vaadin/vaadin-cookie-consent": "1.2.0", | |||||
| "@vaadin/vaadin-core-shrinkwrap": "14.4.8", | |||||
| "@vaadin/vaadin-upload": "4.4.1", | |||||
| "@vaadin/vaadin-dialog": "2.5.2", | |||||
| "@vaadin/vaadin-select": "2.4.0", | |||||
| "@vaadin/vaadin-app-layout": "2.2.0", | |||||
| "@vaadin/vaadin-item": "2.3.0", | |||||
| "@vaadin/vaadin-board": "2.2.0", | |||||
| "@vaadin/vaadin-charts": "6.3.1", | |||||
| "@vaadin/vaadin-notification": "1.6.1", | |||||
| "@vaadin/vaadin-grid-pro": "2.2.2", | |||||
| "@vaadin/vaadin-progress-bar": "1.3.0", | |||||
| "@vaadin/vaadin-shrinkwrap": "14.4.8", | |||||
| "@vaadin/vaadin-date-time-picker": "1.4.0", | |||||
| "@vaadin/vaadin-ordered-layout": "1.4.0", | |||||
| "@vaadin/vaadin-login": "1.2.0", | |||||
| "@vaadin/vaadin-button": "2.4.0", | |||||
| "@vaadin/vaadin-date-picker": "4.4.1", | |||||
| "@vaadin/vaadin-text-field": "2.8.3", | |||||
| "@vaadin/vaadin-menu-bar": "1.2.1", | |||||
| "@vaadin/vaadin-custom-field": "1.3.0", | |||||
| "@vaadin/vaadin-form-layout": "2.3.0", | |||||
| "@vaadin/vaadin-confirm-dialog": "1.3.0", | |||||
| "@polymer/iron-list": "3.1.0", | |||||
| "@vaadin/vaadin-accordion": "1.2.0", | |||||
| "@vaadin/vaadin-list-box": "1.4.0", | |||||
| "@vaadin/vaadin-checkbox": "2.5.0", | |||||
| "@vaadin/vaadin-details": "1.2.0", | |||||
| "@polymer/iron-icon": "3.0.1", | |||||
| "@vaadin/vaadin-time-picker": "2.4.0", | |||||
| "@vaadin/vaadin-context-menu": "4.5.0", | |||||
| "@vaadin/vaadin-radio-button": "1.5.1", | |||||
| "@vaadin/vaadin-tabs": "3.2.0", | |||||
| "@vaadin/vaadin-lumo-styles": "1.6.0", | |||||
| "@vaadin/vaadin-material-styles": "1.3.2", | |||||
| "@vaadin/vaadin-rich-text-editor": "1.3.0", | |||||
| "leaflet": "^1.7.1", | |||||
| "@types/leaflet": "^1.5.23" | |||||
| }, | |||||
| "devDependencies": { | |||||
| "webpack-babel-multi-target-plugin": "2.3.3", | |||||
| "copy-webpack-plugin": "5.1.2", | |||||
| "compression-webpack-plugin": "4.0.1", | |||||
| "raw-loader": "3.1.0", | |||||
| "webpack-cli": "3.3.11", | |||||
| "webpack": "4.42.0", | |||||
| "chokidar": "^3.5.0", | |||||
| "webpack-merge": "4.2.2", | |||||
| "webpack-dev-server": "3.11.0" | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,269 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <groupId>com.example.application</groupId> | |||||
| <artifactId>schachligadeluxe</artifactId> | |||||
| <name>Project base for Spring Boot and Vaadin Flow</name> | |||||
| <version>1.0-SNAPSHOT</version> | |||||
| <packaging>jar</packaging> | |||||
| <properties> | |||||
| <java.version>11</java.version> | |||||
| <vaadin.version>14.4.8</vaadin.version> | |||||
| </properties> | |||||
| <parent> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-starter-parent</artifactId> | |||||
| <version>2.4.2</version> | |||||
| </parent> | |||||
| <repositories> | |||||
| <!-- The order of definitions matters. Explicitly defining central here to make sure it has the highest priority. --> | |||||
| <!-- Main Maven repository --> | |||||
| <repository> | |||||
| <id>central</id> | |||||
| <url>https://repo.maven.apache.org/maven2</url> | |||||
| <snapshots> | |||||
| <enabled>false</enabled> | |||||
| </snapshots> | |||||
| </repository> | |||||
| <!-- Repository used by many Vaadin add-ons --> | |||||
| <repository> | |||||
| <id>Vaadin Directory</id> | |||||
| <url>https://maven.vaadin.com/vaadin-addons</url> | |||||
| <snapshots> | |||||
| <enabled>false</enabled> | |||||
| </snapshots> | |||||
| </repository> | |||||
| </repositories> | |||||
| <pluginRepositories> | |||||
| <!-- Main Maven repository --> | |||||
| <pluginRepository> | |||||
| <id>central</id> | |||||
| <url>https://repo.maven.apache.org/maven2</url> | |||||
| <snapshots> | |||||
| <enabled>false</enabled> | |||||
| </snapshots> | |||||
| </pluginRepository> | |||||
| </pluginRepositories> | |||||
| <dependencyManagement> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-bom</artifactId> | |||||
| <version>${vaadin.version}</version> | |||||
| <type>pom</type> | |||||
| <scope>import</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </dependencyManagement> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <!-- Replace artifactId with vaadin-core to use only free components --> | |||||
| <artifactId>vaadin</artifactId> | |||||
| <exclusions> | |||||
| <!-- Webjars are only needed when running in Vaadin 13 compatibility mode --> | |||||
| <exclusion> | |||||
| <groupId>com.vaadin.webjar</groupId> | |||||
| <artifactId>*</artifactId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <groupId>org.webjars.bowergithub.insites</groupId> | |||||
| <artifactId>*</artifactId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <groupId>org.webjars.bowergithub.polymer</groupId> | |||||
| <artifactId>*</artifactId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <groupId>org.webjars.bowergithub.polymerelements</groupId> | |||||
| <artifactId>*</artifactId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <groupId>org.webjars.bowergithub.vaadin</groupId> | |||||
| <artifactId>*</artifactId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <groupId>org.webjars.bowergithub.webcomponents</groupId> | |||||
| <artifactId>*</artifactId> | |||||
| </exclusion> | |||||
| </exclusions> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-spring-boot-starter</artifactId> | |||||
| <exclusions> | |||||
| <!-- Excluding so that webjars are not included. --> | |||||
| <exclusion> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-core</artifactId> | |||||
| </exclusion> | |||||
| </exclusions> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-avatar-flow</artifactId> | |||||
| <version>1.0.1</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.vaadin.artur</groupId> | |||||
| <artifactId>a-vaadin-helper</artifactId> | |||||
| <version>1.5.0</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.vaadin.artur.exampledata</groupId> | |||||
| <artifactId>exampledata</artifactId> | |||||
| <version>3.0.0</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.h2database</groupId> | |||||
| <artifactId>h2</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-starter-data-jpa</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-starter-validation</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-devtools</artifactId> | |||||
| <optional>true</optional> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-testbench</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>io.github.bonigarcia</groupId> | |||||
| <artifactId>webdrivermanager</artifactId> | |||||
| <version>3.8.1</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <defaultGoal>spring-boot:run</defaultGoal> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-maven-plugin</artifactId> | |||||
| <!-- Clean build and startup time for Vaadin apps sometimes may exceed | |||||
| the default Spring Boot's 30sec timeout. --> | |||||
| <configuration> | |||||
| <wait>500</wait> | |||||
| <maxAttempts>240</maxAttempts> | |||||
| </configuration> | |||||
| </plugin> | |||||
| <!-- | |||||
| Take care of synchronizing java dependencies and imports in | |||||
| package.json and main.js files. | |||||
| It also creates webpack.config.js if not exists yet. | |||||
| --> | |||||
| <plugin> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-maven-plugin</artifactId> | |||||
| <version>${vaadin.version}</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <goals> | |||||
| <goal>prepare-frontend</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| <profiles> | |||||
| <profile> | |||||
| <!-- Production mode is activated using -Pproduction --> | |||||
| <id>production</id> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>com.vaadin</groupId> | |||||
| <artifactId>vaadin-maven-plugin</artifactId> | |||||
| <version>${vaadin.version}</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <goals> | |||||
| <goal>build-frontend</goal> | |||||
| </goals> | |||||
| <phase>compile</phase> | |||||
| </execution> | |||||
| </executions> | |||||
| <configuration> | |||||
| <productionMode>true</productionMode> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </profile> | |||||
| <profile> | |||||
| <id>it</id> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-maven-plugin</artifactId> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>start-spring-boot</id> | |||||
| <phase>pre-integration-test</phase> | |||||
| <goals> | |||||
| <goal>start</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| <execution> | |||||
| <id>stop-spring-boot</id> | |||||
| <phase>post-integration-test</phase> | |||||
| <goals> | |||||
| <goal>stop</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| </plugin> | |||||
| <!-- Runs the integration tests (*IT) after the server is started --> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-failsafe-plugin</artifactId> | |||||
| <executions> | |||||
| <execution> | |||||
| <goals> | |||||
| <goal>integration-test</goal> | |||||
| <goal>verify</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| <configuration> | |||||
| <trimStackTrace>false</trimStackTrace> | |||||
| <enableAssertions>true</enableAssertions> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </profile> | |||||
| </profiles> | |||||
| </project> | |||||
| @ -0,0 +1,18 @@ | |||||
| package com.example.application; | |||||
| import org.springframework.boot.SpringApplication; | |||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
| import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; | |||||
| import org.vaadin.artur.helpers.LaunchUtil; | |||||
| /** | |||||
| * The entry point of the Spring Boot application. | |||||
| */ | |||||
| @SpringBootApplication | |||||
| public class Application extends SpringBootServletInitializer { | |||||
| public static void main(String[] args) { | |||||
| LaunchUtil.launchBrowserInDevelopmentMode(SpringApplication.run(Application.class, args)); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,18 @@ | |||||
| package com.example.application.components.leafletmap; | |||||
| import com.vaadin.flow.component.Component; | |||||
| import com.vaadin.flow.component.HasSize; | |||||
| import com.vaadin.flow.component.Tag; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.dependency.JsModule; | |||||
| @CssImport("leaflet/dist/leaflet.css") | |||||
| @JsModule("./components/leafletmap/leaflet-map.js") | |||||
| @Tag("leaflet-map") | |||||
| public class LeafletMap extends Component implements HasSize { | |||||
| public void setView(double latitude, double longitude, int zoomLevel) { | |||||
| getElement().callJsFunction("setView", latitude, longitude, zoomLevel); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,42 @@ | |||||
| package com.example.application.data; | |||||
| import javax.persistence.GeneratedValue; | |||||
| import javax.persistence.Id; | |||||
| import javax.persistence.MappedSuperclass; | |||||
| @MappedSuperclass | |||||
| public abstract class AbstractEntity { | |||||
| @Id | |||||
| @GeneratedValue | |||||
| private Integer id; | |||||
| public Integer getId() { | |||||
| return id; | |||||
| } | |||||
| public void setId(Integer id) { | |||||
| this.id = id; | |||||
| } | |||||
| @Override | |||||
| public int hashCode() { | |||||
| if (id != null) { | |||||
| return id.hashCode(); | |||||
| } | |||||
| return super.hashCode(); | |||||
| } | |||||
| @Override | |||||
| public boolean equals(Object obj) { | |||||
| if (!(obj instanceof AbstractEntity)) { | |||||
| return false; // null or other class | |||||
| } | |||||
| AbstractEntity other = (AbstractEntity) obj; | |||||
| if (id != null) { | |||||
| return id.equals(other.id); | |||||
| } | |||||
| return super.equals(other); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,47 @@ | |||||
| package com.example.application.data.entity; | |||||
| import javax.persistence.Entity; | |||||
| import com.example.application.data.AbstractEntity; | |||||
| @Entity | |||||
| public class SampleAddress extends AbstractEntity { | |||||
| private String street; | |||||
| private String postalCode; | |||||
| private String city; | |||||
| private String state; | |||||
| private String country; | |||||
| public String getStreet() { | |||||
| return street; | |||||
| } | |||||
| public void setStreet(String street) { | |||||
| this.street = street; | |||||
| } | |||||
| public String getPostalCode() { | |||||
| return postalCode; | |||||
| } | |||||
| public void setPostalCode(String postalCode) { | |||||
| this.postalCode = postalCode; | |||||
| } | |||||
| public String getCity() { | |||||
| return city; | |||||
| } | |||||
| public void setCity(String city) { | |||||
| this.city = city; | |||||
| } | |||||
| public String getState() { | |||||
| return state; | |||||
| } | |||||
| public void setState(String state) { | |||||
| this.state = state; | |||||
| } | |||||
| public String getCountry() { | |||||
| return country; | |||||
| } | |||||
| public void setCountry(String country) { | |||||
| this.country = country; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,62 @@ | |||||
| package com.example.application.data.entity; | |||||
| import javax.persistence.Entity; | |||||
| import com.example.application.data.AbstractEntity; | |||||
| import java.time.LocalDate; | |||||
| @Entity | |||||
| public class SamplePerson extends AbstractEntity { | |||||
| private String firstName; | |||||
| private String lastName; | |||||
| private String email; | |||||
| private String phone; | |||||
| private LocalDate dateOfBirth; | |||||
| private String occupation; | |||||
| private boolean important; | |||||
| public String getFirstName() { | |||||
| return firstName; | |||||
| } | |||||
| public void setFirstName(String firstName) { | |||||
| this.firstName = firstName; | |||||
| } | |||||
| public String getLastName() { | |||||
| return lastName; | |||||
| } | |||||
| public void setLastName(String lastName) { | |||||
| this.lastName = lastName; | |||||
| } | |||||
| public String getEmail() { | |||||
| return email; | |||||
| } | |||||
| public void setEmail(String email) { | |||||
| this.email = email; | |||||
| } | |||||
| public String getPhone() { | |||||
| return phone; | |||||
| } | |||||
| public void setPhone(String phone) { | |||||
| this.phone = phone; | |||||
| } | |||||
| public LocalDate getDateOfBirth() { | |||||
| return dateOfBirth; | |||||
| } | |||||
| public void setDateOfBirth(LocalDate dateOfBirth) { | |||||
| this.dateOfBirth = dateOfBirth; | |||||
| } | |||||
| public String getOccupation() { | |||||
| return occupation; | |||||
| } | |||||
| public void setOccupation(String occupation) { | |||||
| this.occupation = occupation; | |||||
| } | |||||
| public boolean isImportant() { | |||||
| return important; | |||||
| } | |||||
| public void setImportant(boolean important) { | |||||
| this.important = important; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,49 @@ | |||||
| package com.example.application.data.generator; | |||||
| import com.vaadin.flow.spring.annotation.SpringComponent; | |||||
| import com.example.application.data.service.SamplePersonRepository; | |||||
| import com.example.application.data.entity.SamplePerson; | |||||
| import java.time.LocalDateTime; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.boot.CommandLineRunner; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.vaadin.artur.exampledata.DataType; | |||||
| import org.vaadin.artur.exampledata.ExampleDataGenerator; | |||||
| @SpringComponent | |||||
| public class DataGenerator { | |||||
| @Bean | |||||
| public CommandLineRunner loadData(SamplePersonRepository samplePersonRepository) { | |||||
| return args -> { | |||||
| Logger logger = LoggerFactory.getLogger(getClass()); | |||||
| if (samplePersonRepository.count() != 0L) { | |||||
| logger.info("Using existing database"); | |||||
| return; | |||||
| } | |||||
| int seed = 123; | |||||
| logger.info("Generating demo data"); | |||||
| logger.info("... generating 100 Sample Person entities..."); | |||||
| ExampleDataGenerator<SamplePerson> samplePersonRepositoryGenerator = new ExampleDataGenerator<>( | |||||
| SamplePerson.class, LocalDateTime.of(2021, 2, 26, 0, 0, 0)); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setId, DataType.ID); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setFirstName, DataType.FIRST_NAME); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setLastName, DataType.LAST_NAME); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setEmail, DataType.EMAIL); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setPhone, DataType.PHONE_NUMBER); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setDateOfBirth, DataType.DATE_OF_BIRTH); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setOccupation, DataType.OCCUPATION); | |||||
| samplePersonRepositoryGenerator.setData(SamplePerson::setImportant, DataType.BOOLEAN_10_90); | |||||
| samplePersonRepository.saveAll(samplePersonRepositoryGenerator.create(100, seed)); | |||||
| logger.info("Generated demo data"); | |||||
| }; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,9 @@ | |||||
| package com.example.application.data.service; | |||||
| import com.example.application.data.entity.SampleAddress; | |||||
| import org.springframework.data.jpa.repository.JpaRepository; | |||||
| public interface SampleAddressRepository extends JpaRepository<SampleAddress, Integer> { | |||||
| } | |||||
| @ -0,0 +1,23 @@ | |||||
| package com.example.application.data.service; | |||||
| import com.example.application.data.entity.SampleAddress; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Service; | |||||
| import org.vaadin.artur.helpers.CrudService; | |||||
| @Service | |||||
| public class SampleAddressService extends CrudService<SampleAddress, Integer> { | |||||
| private SampleAddressRepository repository; | |||||
| public SampleAddressService(@Autowired SampleAddressRepository repository) { | |||||
| this.repository = repository; | |||||
| } | |||||
| @Override | |||||
| protected SampleAddressRepository getRepository() { | |||||
| return repository; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,10 @@ | |||||
| package com.example.application.data.service; | |||||
| import com.example.application.data.entity.SamplePerson; | |||||
| import org.springframework.data.jpa.repository.JpaRepository; | |||||
| import java.time.LocalDate; | |||||
| public interface SamplePersonRepository extends JpaRepository<SamplePerson, Integer> { | |||||
| } | |||||
| @ -0,0 +1,24 @@ | |||||
| package com.example.application.data.service; | |||||
| import com.example.application.data.entity.SamplePerson; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Service; | |||||
| import org.vaadin.artur.helpers.CrudService; | |||||
| import java.time.LocalDate; | |||||
| @Service | |||||
| public class SamplePersonService extends CrudService<SamplePerson, Integer> { | |||||
| private SamplePersonRepository repository; | |||||
| public SamplePersonService(@Autowired SamplePersonRepository repository) { | |||||
| this.repository = repository; | |||||
| } | |||||
| @Override | |||||
| protected SamplePersonRepository getRepository() { | |||||
| return repository; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,20 @@ | |||||
| package com.example.application.views.about; | |||||
| import com.vaadin.flow.component.Text; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.html.Div; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| @CssImport("./views/about/about-view.css") | |||||
| @Route(value = "about", layout = MainView.class) | |||||
| @PageTitle("About") | |||||
| public class AboutView extends Div { | |||||
| public AboutView() { | |||||
| addClassName("about-view"); | |||||
| add(new Text("Content placeholder")); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,84 @@ | |||||
| package com.example.application.views.addressform; | |||||
| import com.example.application.data.entity.SampleAddress; | |||||
| import com.example.application.data.service.SampleAddressService; | |||||
| import com.vaadin.flow.component.Component; | |||||
| import com.vaadin.flow.component.button.Button; | |||||
| import com.vaadin.flow.component.button.ButtonVariant; | |||||
| import com.vaadin.flow.component.combobox.ComboBox; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.formlayout.FormLayout; | |||||
| import com.vaadin.flow.component.html.Div; | |||||
| import com.vaadin.flow.component.html.H3; | |||||
| import com.vaadin.flow.component.notification.Notification; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.textfield.TextField; | |||||
| import com.vaadin.flow.data.binder.Binder; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| @CssImport("./views/addressform/address-form-view.css") | |||||
| @Route(value = "address-form", layout = MainView.class) | |||||
| @PageTitle("Address Form") | |||||
| public class AddressFormView extends Div { | |||||
| private TextField street = new TextField("Street address"); | |||||
| private TextField postalCode = new TextField("Postal code"); | |||||
| private TextField city = new TextField("City"); | |||||
| private ComboBox<String> state = new ComboBox<>("State"); | |||||
| private ComboBox<String> country = new ComboBox<>("Country"); | |||||
| private Button cancel = new Button("Cancel"); | |||||
| private Button save = new Button("Save"); | |||||
| private Binder<SampleAddress> binder = new Binder<>(SampleAddress.class); | |||||
| public AddressFormView(SampleAddressService addressService) { | |||||
| addClassName("address-form-view"); | |||||
| add(createTitle()); | |||||
| add(createFormLayout()); | |||||
| add(createButtonLayout()); | |||||
| binder.bindInstanceFields(this); | |||||
| clearForm(); | |||||
| cancel.addClickListener(e -> clearForm()); | |||||
| save.addClickListener(e -> { | |||||
| addressService.update(binder.getBean()); | |||||
| Notification.show(binder.getBean().getClass().getSimpleName() + " stored."); | |||||
| clearForm(); | |||||
| }); | |||||
| } | |||||
| private Component createTitle() { | |||||
| return new H3("Address"); | |||||
| } | |||||
| private Component createFormLayout() { | |||||
| FormLayout formLayout = new FormLayout(); | |||||
| formLayout.add(street, 2); | |||||
| postalCode.setPattern("\\d*"); | |||||
| postalCode.setPreventInvalidInput(true); | |||||
| country.setItems("Country 1", "Country 2", "Country 3"); | |||||
| state.setItems("State A", "State B", "State C", "State D"); | |||||
| formLayout.add(postalCode, city, state, country); | |||||
| return formLayout; | |||||
| } | |||||
| private Component createButtonLayout() { | |||||
| HorizontalLayout buttonLayout = new HorizontalLayout(); | |||||
| buttonLayout.addClassName("button-layout"); | |||||
| save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | |||||
| buttonLayout.add(save); | |||||
| buttonLayout.add(cancel); | |||||
| return buttonLayout; | |||||
| } | |||||
| private void clearForm() { | |||||
| this.binder.setBean(new SampleAddress()); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,157 @@ | |||||
| package com.example.application.views.cardlist; | |||||
| import java.util.Arrays; | |||||
| import java.util.List; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.dependency.JsModule; | |||||
| import com.vaadin.flow.component.grid.Grid; | |||||
| import com.vaadin.flow.component.grid.GridVariant; | |||||
| import com.vaadin.flow.component.html.Div; | |||||
| import com.vaadin.flow.component.html.Image; | |||||
| import com.vaadin.flow.component.html.Span; | |||||
| import com.vaadin.flow.component.icon.IronIcon; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
| import com.vaadin.flow.router.AfterNavigationEvent; | |||||
| import com.vaadin.flow.router.AfterNavigationObserver; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| @CssImport("./views/cardlist/card-list-view.css") | |||||
| @Route(value = "card-list", layout = MainView.class) | |||||
| @PageTitle("Card List") | |||||
| public class CardListView extends Div implements AfterNavigationObserver { | |||||
| Grid<Person> grid = new Grid<>(); | |||||
| public CardListView() { | |||||
| addClassName("card-list-view"); | |||||
| setSizeFull(); | |||||
| grid.setHeight("100%"); | |||||
| grid.addThemeVariants(GridVariant.LUMO_NO_BORDER, GridVariant.LUMO_NO_ROW_BORDERS); | |||||
| grid.addComponentColumn(person -> createCard(person)); | |||||
| add(grid); | |||||
| } | |||||
| private HorizontalLayout createCard(Person person) { | |||||
| HorizontalLayout card = new HorizontalLayout(); | |||||
| card.addClassName("card"); | |||||
| card.setSpacing(false); | |||||
| card.getThemeList().add("spacing-s"); | |||||
| Image image = new Image(); | |||||
| image.setSrc(person.getImage()); | |||||
| VerticalLayout description = new VerticalLayout(); | |||||
| description.addClassName("description"); | |||||
| description.setSpacing(false); | |||||
| description.setPadding(false); | |||||
| HorizontalLayout header = new HorizontalLayout(); | |||||
| header.addClassName("header"); | |||||
| header.setSpacing(false); | |||||
| header.getThemeList().add("spacing-s"); | |||||
| Span name = new Span(person.getName()); | |||||
| name.addClassName("name"); | |||||
| Span date = new Span(person.getDate()); | |||||
| date.addClassName("date"); | |||||
| header.add(name, date); | |||||
| Span post = new Span(person.getPost()); | |||||
| post.addClassName("post"); | |||||
| HorizontalLayout actions = new HorizontalLayout(); | |||||
| actions.addClassName("actions"); | |||||
| actions.setSpacing(false); | |||||
| actions.getThemeList().add("spacing-s"); | |||||
| IronIcon likeIcon = new IronIcon("vaadin", "heart"); | |||||
| Span likes = new Span(person.getLikes()); | |||||
| likes.addClassName("likes"); | |||||
| IronIcon commentIcon = new IronIcon("vaadin", "comment"); | |||||
| Span comments = new Span(person.getComments()); | |||||
| comments.addClassName("comments"); | |||||
| IronIcon shareIcon = new IronIcon("vaadin", "connect"); | |||||
| Span shares = new Span(person.getShares()); | |||||
| shares.addClassName("shares"); | |||||
| actions.add(likeIcon, likes, commentIcon, comments, shareIcon, shares); | |||||
| description.add(header, post, actions); | |||||
| card.add(image, description); | |||||
| return card; | |||||
| } | |||||
| @Override | |||||
| public void afterNavigation(AfterNavigationEvent event) { | |||||
| // Set some data when this view is displayed. | |||||
| List<Person> persons = Arrays.asList( // | |||||
| createPerson("https://randomuser.me/api/portraits/men/42.jpg", "John Smith", "May 8", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/women/42.jpg", "Abagail Libbie", "May 3", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/men/24.jpg", "Alberto Raya", "May 3", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/women/24.jpg", "Emmy Elsner", "Apr 22", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/men/76.jpg", "Alf Huncoot", "Apr 21", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/women/76.jpg", "Lidmila Vilensky", "Apr 17", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/men/94.jpg", "Jarrett Cawsey", "Apr 17", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/women/94.jpg", "Tania Perfilyeva", "Mar 8", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/men/16.jpg", "Ivan Polo", "Mar 5", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/women/16.jpg", "Emelda Scandroot", "Mar 5", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/men/67.jpg", "Marcos Sá", "Mar 4", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20"), | |||||
| createPerson("https://randomuser.me/api/portraits/women/67.jpg", "Jacqueline Asong", "Mar 2", | |||||
| "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content (also called greeking).", | |||||
| "1K", "500", "20") | |||||
| ); | |||||
| grid.setItems(persons); | |||||
| } | |||||
| private static Person createPerson(String image, String name, String date, String post, String likes, | |||||
| String comments, String shares) { | |||||
| Person p = new Person(); | |||||
| p.setImage(image); | |||||
| p.setName(name); | |||||
| p.setDate(date); | |||||
| p.setPost(post); | |||||
| p.setLikes(likes); | |||||
| p.setComments(comments); | |||||
| p.setShares(shares); | |||||
| return p; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,71 @@ | |||||
| package com.example.application.views.cardlist; | |||||
| public class Person { | |||||
| private String image; | |||||
| private String name; | |||||
| private String date; | |||||
| private String post; | |||||
| private String likes; | |||||
| private String comments; | |||||
| private String shares; | |||||
| public Person() { | |||||
| } | |||||
| public String getImage() { | |||||
| return image; | |||||
| } | |||||
| public void setImage(String image) { | |||||
| this.image = image; | |||||
| } | |||||
| public String getName() { | |||||
| return name; | |||||
| } | |||||
| public void setName(String name) { | |||||
| this.name = name; | |||||
| } | |||||
| public String getDate() { | |||||
| return date; | |||||
| } | |||||
| public void setDate(String date) { | |||||
| this.date = date; | |||||
| } | |||||
| public String getPost() { | |||||
| return post; | |||||
| } | |||||
| public void setPost(String post) { | |||||
| this.post = post; | |||||
| } | |||||
| public String getLikes() { | |||||
| return likes; | |||||
| } | |||||
| public void setLikes(String likes) { | |||||
| this.likes = likes; | |||||
| } | |||||
| public String getComments() { | |||||
| return comments; | |||||
| } | |||||
| public void setComments(String comments) { | |||||
| this.comments = comments; | |||||
| } | |||||
| public String getShares() { | |||||
| return shares; | |||||
| } | |||||
| public void setShares(String shares) { | |||||
| this.shares = shares; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,101 @@ | |||||
| package com.example.application.views.creditcardform; | |||||
| import com.vaadin.flow.component.Component; | |||||
| import com.vaadin.flow.component.button.Button; | |||||
| import com.vaadin.flow.component.button.ButtonVariant; | |||||
| import com.vaadin.flow.component.customfield.CustomField; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.formlayout.FormLayout; | |||||
| import com.vaadin.flow.component.html.Div; | |||||
| import com.vaadin.flow.component.html.H3; | |||||
| import com.vaadin.flow.component.notification.Notification; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.select.Select; | |||||
| import com.vaadin.flow.component.textfield.PasswordField; | |||||
| import com.vaadin.flow.component.textfield.TextField; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| @CssImport("./views/creditcardform/credit-card-form-view.css") | |||||
| @Route(value = "credit-card-form", layout = MainView.class) | |||||
| @PageTitle("Credit Card Form") | |||||
| public class CreditCardFormView extends Div { | |||||
| private TextField cardNumber = new TextField("Credit card number"); | |||||
| private TextField cardholderName = new TextField("Cardholder name"); | |||||
| private Select<Integer> month = new Select<>(); | |||||
| private Select<Integer> year = new Select<>(); | |||||
| private ExpirationDateField expiration = new ExpirationDateField("Expiration date", month, year); | |||||
| private PasswordField csc = new PasswordField("CSC"); | |||||
| private Button cancel = new Button("Cancel"); | |||||
| private Button submit = new Button("Submit"); | |||||
| public CreditCardFormView() { | |||||
| addClassName("credit-card-form-view"); | |||||
| add(createTitle()); | |||||
| add(createFormLayout()); | |||||
| add(createButtonLayout()); | |||||
| cancel.addClickListener(e -> { | |||||
| Notification.show("Not implemented"); | |||||
| }); | |||||
| submit.addClickListener(e -> { | |||||
| Notification.show("Not implemented"); | |||||
| }); | |||||
| } | |||||
| private Component createTitle() { | |||||
| return new H3("Credit Card"); | |||||
| } | |||||
| private Component createFormLayout() { | |||||
| FormLayout formLayout = new FormLayout(); | |||||
| formLayout.add(cardNumber, cardholderName, expiration, csc); | |||||
| return formLayout; | |||||
| } | |||||
| private Component createButtonLayout() { | |||||
| HorizontalLayout buttonLayout = new HorizontalLayout(); | |||||
| buttonLayout.addClassName("button-layout"); | |||||
| cardNumber.setPlaceholder("1234 5678 9123 4567"); | |||||
| cardNumber.setPattern("[\\d ]*"); | |||||
| cardNumber.setPreventInvalidInput(true); | |||||
| cardNumber.setRequired(true); | |||||
| cardNumber.setErrorMessage("Please enter a valid credit card number"); | |||||
| month.setPlaceholder("Month"); | |||||
| month.setItems(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); | |||||
| year.setPlaceholder("Year"); | |||||
| year.setItems(20, 21, 22, 23, 24, 25); | |||||
| submit.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | |||||
| buttonLayout.add(submit); | |||||
| buttonLayout.add(cancel); | |||||
| return buttonLayout; | |||||
| } | |||||
| private class ExpirationDateField extends CustomField<String> { | |||||
| public ExpirationDateField(String label, Select<Integer> month, Select<Integer> year) { | |||||
| setLabel(label); | |||||
| HorizontalLayout layout = new HorizontalLayout(month, year); | |||||
| layout.setFlexGrow(1.0, month, year); | |||||
| month.setWidth("100px"); | |||||
| year.setWidth("100px"); | |||||
| add(layout); | |||||
| } | |||||
| @Override | |||||
| protected String generateModelValue() { | |||||
| // Unused as month and year fields part are of the outer class | |||||
| return ""; | |||||
| } | |||||
| @Override | |||||
| protected void setPresentationValue(String newPresentationValue) { | |||||
| // Unused as month and year fields part are of the outer class | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,34 @@ | |||||
| package com.example.application.views.helloworld; | |||||
| import com.vaadin.flow.component.button.Button; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.notification.Notification; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
| import com.vaadin.flow.component.textfield.TextField; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| import com.vaadin.flow.router.RouteAlias; | |||||
| @CssImport("./views/helloworld/hello-world-view.css") | |||||
| @Route(value = "hello", layout = MainView.class) | |||||
| @RouteAlias(value = "", layout = MainView.class) | |||||
| @PageTitle("Hello World") | |||||
| public class HelloWorldView extends HorizontalLayout { | |||||
| private TextField name; | |||||
| private Button sayHello; | |||||
| public HelloWorldView() { | |||||
| addClassName("hello-world-view"); | |||||
| name = new TextField("Your name"); | |||||
| sayHello = new Button("Say hello"); | |||||
| add(name, sayHello); | |||||
| setVerticalComponentAlignment(Alignment.END, name, sayHello); | |||||
| sayHello.addClickListener(e -> { | |||||
| Notification.show("Hello " + name.getValue()); | |||||
| }); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,124 @@ | |||||
| package com.example.application.views.main; | |||||
| import java.util.Optional; | |||||
| import com.vaadin.flow.component.Component; | |||||
| import com.vaadin.flow.component.ComponentUtil; | |||||
| import com.vaadin.flow.component.applayout.AppLayout; | |||||
| import com.vaadin.flow.component.applayout.DrawerToggle; | |||||
| import com.vaadin.flow.component.avatar.Avatar; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.dependency.JsModule; | |||||
| import com.vaadin.flow.component.html.Image; | |||||
| import com.vaadin.flow.component.html.H1; | |||||
| import com.vaadin.flow.component.orderedlayout.FlexComponent; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
| import com.vaadin.flow.component.tabs.Tab; | |||||
| import com.vaadin.flow.component.tabs.Tabs; | |||||
| import com.vaadin.flow.component.tabs.TabsVariant; | |||||
| import com.vaadin.flow.router.RouterLink; | |||||
| import com.vaadin.flow.server.PWA; | |||||
| import com.vaadin.flow.theme.Theme; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| import com.example.application.views.helloworld.HelloWorldView; | |||||
| import com.example.application.views.about.AboutView; | |||||
| import com.example.application.views.cardlist.CardListView; | |||||
| import com.example.application.views.masterdetail.MasterDetailView; | |||||
| import com.example.application.views.personform.PersonFormView; | |||||
| import com.example.application.views.addressform.AddressFormView; | |||||
| import com.example.application.views.creditcardform.CreditCardFormView; | |||||
| import com.example.application.views.map.MapView; | |||||
| import com.vaadin.flow.theme.lumo.Lumo; | |||||
| /** | |||||
| * The main view is a top-level placeholder for other views. | |||||
| */ | |||||
| @CssImport("./views/main/main-view.css") | |||||
| @PWA(name = "Schachliga DeLuxe", shortName = "Schachliga DeLuxe", enableInstallPrompt = false) | |||||
| @JsModule("./styles/shared-styles.js") | |||||
| @Theme(value = Lumo.class, variant = Lumo.DARK) | |||||
| public class MainView extends AppLayout { | |||||
| private final Tabs menu; | |||||
| private H1 viewTitle; | |||||
| public MainView() { | |||||
| setPrimarySection(Section.DRAWER); | |||||
| addToNavbar(true, createHeaderContent()); | |||||
| menu = createMenu(); | |||||
| addToDrawer(createDrawerContent(menu)); | |||||
| } | |||||
| private Component createHeaderContent() { | |||||
| HorizontalLayout layout = new HorizontalLayout(); | |||||
| layout.setId("header"); | |||||
| layout.getThemeList().set("dark", true); | |||||
| layout.setWidthFull(); | |||||
| layout.setSpacing(false); | |||||
| layout.setAlignItems(FlexComponent.Alignment.CENTER); | |||||
| layout.add(new DrawerToggle()); | |||||
| viewTitle = new H1(); | |||||
| layout.add(viewTitle); | |||||
| layout.add(new Avatar()); | |||||
| return layout; | |||||
| } | |||||
| private Component createDrawerContent(Tabs menu) { | |||||
| VerticalLayout layout = new VerticalLayout(); | |||||
| layout.setSizeFull(); | |||||
| layout.setPadding(false); | |||||
| layout.setSpacing(false); | |||||
| layout.getThemeList().set("spacing-s", true); | |||||
| layout.setAlignItems(FlexComponent.Alignment.STRETCH); | |||||
| HorizontalLayout logoLayout = new HorizontalLayout(); | |||||
| logoLayout.setId("logo"); | |||||
| logoLayout.setAlignItems(FlexComponent.Alignment.CENTER); | |||||
| logoLayout.add(new Image("images/logo.png", "Schachliga DeLuxe logo")); | |||||
| logoLayout.add(new H1("Schachliga DeLuxe")); | |||||
| layout.add(logoLayout, menu); | |||||
| return layout; | |||||
| } | |||||
| private Tabs createMenu() { | |||||
| final Tabs tabs = new Tabs(); | |||||
| tabs.setOrientation(Tabs.Orientation.VERTICAL); | |||||
| tabs.addThemeVariants(TabsVariant.LUMO_MINIMAL); | |||||
| tabs.setId("tabs"); | |||||
| tabs.add(createMenuItems()); | |||||
| return tabs; | |||||
| } | |||||
| private Component[] createMenuItems() { | |||||
| return new Tab[]{createTab("Hello World", HelloWorldView.class), createTab("About", AboutView.class), | |||||
| createTab("Card List", CardListView.class), createTab("Master-Detail", MasterDetailView.class), | |||||
| createTab("Person Form", PersonFormView.class), createTab("Address Form", AddressFormView.class), | |||||
| createTab("Credit Card Form", CreditCardFormView.class), createTab("Map", MapView.class)}; | |||||
| } | |||||
| private static Tab createTab(String text, Class<? extends Component> navigationTarget) { | |||||
| final Tab tab = new Tab(); | |||||
| tab.add(new RouterLink(text, navigationTarget)); | |||||
| ComponentUtil.setData(tab, Class.class, navigationTarget); | |||||
| return tab; | |||||
| } | |||||
| @Override | |||||
| protected void afterNavigation() { | |||||
| super.afterNavigation(); | |||||
| getTabForComponent(getContent()).ifPresent(menu::setSelectedTab); | |||||
| viewTitle.setText(getCurrentPageTitle()); | |||||
| } | |||||
| private Optional<Tab> getTabForComponent(Component component) { | |||||
| return menu.getChildren().filter(tab -> ComponentUtil.getData(tab, Class.class).equals(component.getClass())) | |||||
| .findFirst().map(Tab.class::cast); | |||||
| } | |||||
| private String getCurrentPageTitle() { | |||||
| PageTitle title = getContent().getClass().getAnnotation(PageTitle.class); | |||||
| return title == null ? "" : title.value(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,23 @@ | |||||
| package com.example.application.views.map; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
| import com.example.application.components.leafletmap.LeafletMap; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| @Route(value = "map", layout = MainView.class) | |||||
| @PageTitle("Map") | |||||
| public class MapView extends VerticalLayout { | |||||
| private LeafletMap map = new LeafletMap(); | |||||
| public MapView() { | |||||
| setSizeFull(); | |||||
| setPadding(false); | |||||
| map.setSizeFull(); | |||||
| map.setView(55.0, 10.0, 4); | |||||
| add(map); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,188 @@ | |||||
| package com.example.application.views.masterdetail; | |||||
| import java.util.Optional; | |||||
| import com.example.application.data.entity.SamplePerson; | |||||
| import com.example.application.data.service.SamplePersonService; | |||||
| import com.vaadin.flow.component.Component; | |||||
| import com.vaadin.flow.component.HasStyle; | |||||
| import com.vaadin.flow.component.button.Button; | |||||
| import com.vaadin.flow.component.button.ButtonVariant; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.formlayout.FormLayout; | |||||
| import com.vaadin.flow.component.grid.Grid; | |||||
| import com.vaadin.flow.component.grid.GridVariant; | |||||
| import com.vaadin.flow.component.html.Div; | |||||
| import com.vaadin.flow.component.notification.Notification; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.splitlayout.SplitLayout; | |||||
| import com.vaadin.flow.data.binder.BeanValidationBinder; | |||||
| import com.vaadin.flow.data.binder.ValidationException; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.vaadin.artur.helpers.CrudServiceDataProvider; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| import com.vaadin.flow.data.renderer.TemplateRenderer; | |||||
| import com.vaadin.flow.component.checkbox.Checkbox; | |||||
| import com.vaadin.flow.component.datepicker.DatePicker; | |||||
| import com.vaadin.flow.component.textfield.TextField; | |||||
| @CssImport("./views/masterdetail/master-detail-view.css") | |||||
| @Route(value = "master-detail", layout = MainView.class) | |||||
| @PageTitle("Master-Detail") | |||||
| public class MasterDetailView extends Div { | |||||
| private Grid<SamplePerson> grid = new Grid<>(SamplePerson.class, false); | |||||
| private TextField firstName; | |||||
| private TextField lastName; | |||||
| private TextField email; | |||||
| private TextField phone; | |||||
| private DatePicker dateOfBirth; | |||||
| private TextField occupation; | |||||
| private Checkbox important; | |||||
| private Button cancel = new Button("Cancel"); | |||||
| private Button save = new Button("Save"); | |||||
| private BeanValidationBinder<SamplePerson> binder; | |||||
| private SamplePerson samplePerson; | |||||
| public MasterDetailView(@Autowired SamplePersonService samplePersonService) { | |||||
| addClassName("master-detail-view"); | |||||
| // Create UI | |||||
| SplitLayout splitLayout = new SplitLayout(); | |||||
| splitLayout.setSizeFull(); | |||||
| createGridLayout(splitLayout); | |||||
| createEditorLayout(splitLayout); | |||||
| add(splitLayout); | |||||
| // Configure Grid | |||||
| grid.addColumn("firstName").setAutoWidth(true); | |||||
| grid.addColumn("lastName").setAutoWidth(true); | |||||
| grid.addColumn("email").setAutoWidth(true); | |||||
| grid.addColumn("phone").setAutoWidth(true); | |||||
| grid.addColumn("dateOfBirth").setAutoWidth(true); | |||||
| grid.addColumn("occupation").setAutoWidth(true); | |||||
| TemplateRenderer<SamplePerson> importantRenderer = TemplateRenderer.<SamplePerson>of( | |||||
| "<iron-icon hidden='[[!item.important]]' icon='vaadin:check' style='width: var(--lumo-icon-size-s); height: var(--lumo-icon-size-s); color: var(--lumo-primary-text-color);'></iron-icon><iron-icon hidden='[[item.important]]' icon='vaadin:minus' style='width: var(--lumo-icon-size-s); height: var(--lumo-icon-size-s); color: var(--lumo-disabled-text-color);'></iron-icon>") | |||||
| .withProperty("important", SamplePerson::isImportant); | |||||
| grid.addColumn(importantRenderer).setHeader("Important").setAutoWidth(true); | |||||
| grid.setDataProvider(new CrudServiceDataProvider<>(samplePersonService)); | |||||
| grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); | |||||
| grid.setHeightFull(); | |||||
| // when a row is selected or deselected, populate form | |||||
| grid.asSingleSelect().addValueChangeListener(event -> { | |||||
| if (event.getValue() != null) { | |||||
| Optional<SamplePerson> samplePersonFromBackend = samplePersonService.get(event.getValue().getId()); | |||||
| // when a row is selected but the data is no longer available, refresh grid | |||||
| if (samplePersonFromBackend.isPresent()) { | |||||
| populateForm(samplePersonFromBackend.get()); | |||||
| } else { | |||||
| refreshGrid(); | |||||
| } | |||||
| } else { | |||||
| clearForm(); | |||||
| } | |||||
| }); | |||||
| // Configure Form | |||||
| binder = new BeanValidationBinder<>(SamplePerson.class); | |||||
| // Bind fields. This where you'd define e.g. validation rules | |||||
| binder.bindInstanceFields(this); | |||||
| cancel.addClickListener(e -> { | |||||
| clearForm(); | |||||
| refreshGrid(); | |||||
| }); | |||||
| save.addClickListener(e -> { | |||||
| try { | |||||
| if (this.samplePerson == null) { | |||||
| this.samplePerson = new SamplePerson(); | |||||
| } | |||||
| binder.writeBean(this.samplePerson); | |||||
| samplePersonService.update(this.samplePerson); | |||||
| clearForm(); | |||||
| refreshGrid(); | |||||
| Notification.show("SamplePerson details stored."); | |||||
| } catch (ValidationException validationException) { | |||||
| Notification.show("An exception happened while trying to store the samplePerson details."); | |||||
| } | |||||
| }); | |||||
| } | |||||
| private void createEditorLayout(SplitLayout splitLayout) { | |||||
| Div editorLayoutDiv = new Div(); | |||||
| editorLayoutDiv.setId("editor-layout"); | |||||
| Div editorDiv = new Div(); | |||||
| editorDiv.setId("editor"); | |||||
| editorLayoutDiv.add(editorDiv); | |||||
| FormLayout formLayout = new FormLayout(); | |||||
| firstName = new TextField("First Name"); | |||||
| lastName = new TextField("Last Name"); | |||||
| email = new TextField("Email"); | |||||
| phone = new TextField("Phone"); | |||||
| dateOfBirth = new DatePicker("Date Of Birth"); | |||||
| occupation = new TextField("Occupation"); | |||||
| important = new Checkbox("Important"); | |||||
| important.getStyle().set("padding-top", "var(--lumo-space-m)"); | |||||
| Component[] fields = new Component[]{firstName, lastName, email, phone, dateOfBirth, occupation, important}; | |||||
| for (Component field : fields) { | |||||
| ((HasStyle) field).addClassName("full-width"); | |||||
| } | |||||
| formLayout.add(fields); | |||||
| editorDiv.add(formLayout); | |||||
| createButtonLayout(editorLayoutDiv); | |||||
| splitLayout.addToSecondary(editorLayoutDiv); | |||||
| } | |||||
| private void createButtonLayout(Div editorLayoutDiv) { | |||||
| HorizontalLayout buttonLayout = new HorizontalLayout(); | |||||
| buttonLayout.setId("button-layout"); | |||||
| buttonLayout.setWidthFull(); | |||||
| buttonLayout.setSpacing(true); | |||||
| cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY); | |||||
| save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | |||||
| buttonLayout.add(save, cancel); | |||||
| editorLayoutDiv.add(buttonLayout); | |||||
| } | |||||
| private void createGridLayout(SplitLayout splitLayout) { | |||||
| Div wrapper = new Div(); | |||||
| wrapper.setId("grid-wrapper"); | |||||
| wrapper.setWidthFull(); | |||||
| splitLayout.addToPrimary(wrapper); | |||||
| wrapper.add(grid); | |||||
| } | |||||
| private void refreshGrid() { | |||||
| grid.select(null); | |||||
| grid.getDataProvider().refreshAll(); | |||||
| } | |||||
| private void clearForm() { | |||||
| populateForm(null); | |||||
| } | |||||
| private void populateForm(SamplePerson value) { | |||||
| this.samplePerson = value; | |||||
| binder.readBean(this.samplePerson); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,129 @@ | |||||
| package com.example.application.views.personform; | |||||
| import com.example.application.data.entity.SamplePerson; | |||||
| import com.example.application.data.service.SamplePersonService; | |||||
| import com.vaadin.flow.component.Component; | |||||
| import com.vaadin.flow.component.button.Button; | |||||
| import com.vaadin.flow.component.button.ButtonVariant; | |||||
| import com.vaadin.flow.component.combobox.ComboBox; | |||||
| import com.vaadin.flow.component.customfield.CustomField; | |||||
| import com.vaadin.flow.component.datepicker.DatePicker; | |||||
| import com.vaadin.flow.component.dependency.CssImport; | |||||
| import com.vaadin.flow.component.formlayout.FormLayout; | |||||
| import com.vaadin.flow.component.html.Div; | |||||
| import com.vaadin.flow.component.html.H3; | |||||
| import com.vaadin.flow.component.notification.Notification; | |||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
| import com.vaadin.flow.component.textfield.EmailField; | |||||
| import com.vaadin.flow.component.textfield.TextField; | |||||
| import com.vaadin.flow.data.binder.Binder; | |||||
| import com.vaadin.flow.router.Route; | |||||
| import com.vaadin.flow.router.PageTitle; | |||||
| import com.example.application.views.main.MainView; | |||||
| import com.vaadin.flow.data.renderer.TemplateRenderer; | |||||
| import com.vaadin.flow.component.checkbox.Checkbox; | |||||
| @CssImport("./views/personform/person-form-view.css") | |||||
| @Route(value = "person-form", layout = MainView.class) | |||||
| @PageTitle("Person Form") | |||||
| public class PersonFormView extends Div { | |||||
| private TextField firstName = new TextField("First name"); | |||||
| private TextField lastName = new TextField("Last name"); | |||||
| private EmailField email = new EmailField("Email address"); | |||||
| private DatePicker dateOfBirth = new DatePicker("Birthday"); | |||||
| private PhoneNumberField phone = new PhoneNumberField("Phone number"); | |||||
| private TextField occupation = new TextField("Occupation"); | |||||
| private Button cancel = new Button("Cancel"); | |||||
| private Button save = new Button("Save"); | |||||
| private Binder<SamplePerson> binder = new Binder(SamplePerson.class); | |||||
| public PersonFormView(SamplePersonService personService) { | |||||
| addClassName("person-form-view"); | |||||
| add(createTitle()); | |||||
| add(createFormLayout()); | |||||
| add(createButtonLayout()); | |||||
| binder.bindInstanceFields(this); | |||||
| clearForm(); | |||||
| cancel.addClickListener(e -> clearForm()); | |||||
| save.addClickListener(e -> { | |||||
| personService.update(binder.getBean()); | |||||
| Notification.show(binder.getBean().getClass().getSimpleName() + " details stored."); | |||||
| clearForm(); | |||||
| }); | |||||
| } | |||||
| private void clearForm() { | |||||
| binder.setBean(new SamplePerson()); | |||||
| } | |||||
| private Component createTitle() { | |||||
| return new H3("Personal information"); | |||||
| } | |||||
| private Component createFormLayout() { | |||||
| FormLayout formLayout = new FormLayout(); | |||||
| email.setErrorMessage("Please enter a valid email address"); | |||||
| formLayout.add(firstName, lastName, dateOfBirth, phone, email, occupation); | |||||
| return formLayout; | |||||
| } | |||||
| private Component createButtonLayout() { | |||||
| HorizontalLayout buttonLayout = new HorizontalLayout(); | |||||
| buttonLayout.addClassName("button-layout"); | |||||
| save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | |||||
| buttonLayout.add(save); | |||||
| buttonLayout.add(cancel); | |||||
| return buttonLayout; | |||||
| } | |||||
| private static class PhoneNumberField extends CustomField<String> { | |||||
| private ComboBox<String> countryCode = new ComboBox<>(); | |||||
| private TextField number = new TextField(); | |||||
| public PhoneNumberField(String label) { | |||||
| setLabel(label); | |||||
| countryCode.setWidth("120px"); | |||||
| countryCode.setPlaceholder("Country"); | |||||
| countryCode.setPattern("\\+\\d*"); | |||||
| countryCode.setPreventInvalidInput(true); | |||||
| countryCode.setItems("+354", "+91", "+62", "+98", "+964", "+353", "+44", "+972", "+39", "+225"); | |||||
| countryCode.addCustomValueSetListener(e -> countryCode.setValue(e.getDetail())); | |||||
| number.setPattern("\\d*"); | |||||
| number.setPreventInvalidInput(true); | |||||
| HorizontalLayout layout = new HorizontalLayout(countryCode, number); | |||||
| layout.setFlexGrow(1.0, number); | |||||
| add(layout); | |||||
| } | |||||
| @Override | |||||
| protected String generateModelValue() { | |||||
| if (countryCode.getValue() != null && number.getValue() != null) { | |||||
| String s = countryCode.getValue() + " " + number.getValue(); | |||||
| return s; | |||||
| } | |||||
| return ""; | |||||
| } | |||||
| @Override | |||||
| protected void setPresentationValue(String phoneNumber) { | |||||
| String[] parts = phoneNumber != null ? phoneNumber.split(" ", 2) : new String[0]; | |||||
| if (parts.length == 1) { | |||||
| countryCode.clear(); | |||||
| number.setValue(parts[0]); | |||||
| } else if (parts.length == 2) { | |||||
| countryCode.setValue(parts[0]); | |||||
| number.setValue(parts[1]); | |||||
| } else { | |||||
| countryCode.clear(); | |||||
| number.clear(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,8 @@ | |||||
| server.port=${PORT:8080} | |||||
| # Ensure application is run in Vaadin 14/npm mode | |||||
| vaadin.compatibilityMode = false | |||||
| logging.level.org.atmosphere = warn | |||||
| # To improve the performance during development. | |||||
| # For more information https://vaadin.com/docs/v14/flow/spring/tutorial-spring-configuration.html#special-configuration-parameters | |||||
| # vaadin.whitelisted-packages= org/vaadin/example | |||||
| @ -0,0 +1,6 @@ | |||||
| ____ _ _ _ _ ____ _ | |||||
| / ___| ___ | |__ __ _ ___ | |__ | |(_) __ _ __ _ | _ \ ___ | | _ _ __ __ ___ | |||||
| \___ \ / __|| '_ \ / _` | / __|| '_ \ | || | / _` | / _` | | | | | / _ \| | | | | |\ \/ / / _ \ | |||||
| ___) || (__ | | | || (_| || (__ | | | || || || (_| || (_| | | |_| || __/| |___ | |_| | > < | __/ | |||||
| |____/ \___||_| |_| \__,_| \___||_| |_||_||_| \__, | \__,_| |____/ \___||_____| \__,_|/_/\_\ \___| | |||||
| |___/ | |||||