| @ -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 @@ | |||
| ____ _ _ _ _ ____ _ | |||
| / ___| ___ | |__ __ _ ___ | |__ | |(_) __ _ __ _ | _ \ ___ | | _ _ __ __ ___ | |||
| \___ \ / __|| '_ \ / _` | / __|| '_ \ | || | / _` | / _` | | | | | / _ \| | | | | |\ \/ / / _ \ | |||
| ___) || (__ | | | || (_| || (__ | | | || || || (_| || (_| | | |_| || __/| |___ | |_| | > < | __/ | |||
| |____/ \___||_| |_| \__,_| \___||_| |_||_||_| \__, | \__,_| |____/ \___||_____| \__,_|/_/\_\ \___| | |||
| |___/ | |||