Browse Source

add vaadin project

master
maddin 4 years ago
parent
commit
44c1f3fd3b
40 changed files with 12431 additions and 0 deletions
  1. +24
    -0
      LICENSE.md
  2. +43
    -0
      README.md
  3. +36
    -0
      frontend/components/leafletmap/leaflet-map.js
  4. +20
    -0
      frontend/styles/shared-styles.js
  5. +3
    -0
      frontend/views/about/about-view.css
  6. +10
    -0
      frontend/views/addressform/address-form-view.css
  7. +70
    -0
      frontend/views/cardlist/card-list-view.css
  8. +10
    -0
      frontend/views/creditcardform/credit-card-form-view.css
  9. +4
    -0
      frontend/views/helloworld/hello-world-view.css
  10. +45
    -0
      frontend/views/main/main-view.css
  11. +8
    -0
      frontend/views/map/map-view.css
  12. +33
    -0
      frontend/views/masterdetail/master-detail-view.css
  13. +10
    -0
      frontend/views/personform/person-form-view.css
  14. +10476
    -0
      package-lock.json
  15. +123
    -0
      package.json
  16. +269
    -0
      pom.xml
  17. +18
    -0
      src/main/java/com/example/application/Application.java
  18. +18
    -0
      src/main/java/com/example/application/components/leafletmap/LeafletMap.java
  19. +42
    -0
      src/main/java/com/example/application/data/AbstractEntity.java
  20. +47
    -0
      src/main/java/com/example/application/data/entity/SampleAddress.java
  21. +62
    -0
      src/main/java/com/example/application/data/entity/SamplePerson.java
  22. +49
    -0
      src/main/java/com/example/application/data/generator/DataGenerator.java
  23. +9
    -0
      src/main/java/com/example/application/data/service/SampleAddressRepository.java
  24. +23
    -0
      src/main/java/com/example/application/data/service/SampleAddressService.java
  25. +10
    -0
      src/main/java/com/example/application/data/service/SamplePersonRepository.java
  26. +24
    -0
      src/main/java/com/example/application/data/service/SamplePersonService.java
  27. +20
    -0
      src/main/java/com/example/application/views/about/AboutView.java
  28. +84
    -0
      src/main/java/com/example/application/views/addressform/AddressFormView.java
  29. +157
    -0
      src/main/java/com/example/application/views/cardlist/CardListView.java
  30. +71
    -0
      src/main/java/com/example/application/views/cardlist/Person.java
  31. +101
    -0
      src/main/java/com/example/application/views/creditcardform/CreditCardFormView.java
  32. +34
    -0
      src/main/java/com/example/application/views/helloworld/HelloWorldView.java
  33. +124
    -0
      src/main/java/com/example/application/views/main/MainView.java
  34. +23
    -0
      src/main/java/com/example/application/views/map/MapView.java
  35. +188
    -0
      src/main/java/com/example/application/views/masterdetail/MasterDetailView.java
  36. +129
    -0
      src/main/java/com/example/application/views/personform/PersonFormView.java
  37. BIN
      src/main/resources/META-INF/resources/icons/icon.png
  38. BIN
      src/main/resources/META-INF/resources/images/logo.png
  39. +8
    -0
      src/main/resources/application.properties
  40. +6
    -0
      src/main/resources/banner.txt

+ 24
- 0
LICENSE.md View File

@ -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>

+ 43
- 0
README.md View File

@ -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).

+ 36
- 0
frontend/components/leafletmap/leaflet-map.js View File

@ -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 = `&copy; <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);

+ 20
- 0
frontend/styles/shared-styles.js View File

@ -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);

+ 3
- 0
frontend/views/about/about-view.css View File

@ -0,0 +1,3 @@
.about-view {
display: block;
}

+ 10
- 0
frontend/views/addressform/address-form-view.css View File

@ -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);
}

+ 70
- 0
frontend/views/cardlist/card-list-view.css View File

@ -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);
}

+ 10
- 0
frontend/views/creditcardform/credit-card-form-view.css View File

@ -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);
}

+ 4
- 0
frontend/views/helloworld/hello-world-view.css View File

@ -0,0 +1,4 @@
.hello-world-view {
display: block;
padding: 1em;
}

+ 45
- 0
frontend/views/main/main-view.css View File

@ -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);
}

+ 8
- 0
frontend/views/map/map-view.css View File

@ -0,0 +1,8 @@
.map-view {
display: flex;
height: 100%;
}
.map-view .map {
flex: 1;
}

+ 33
- 0
frontend/views/masterdetail/master-detail-view.css View File

@ -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);
}

+ 10
- 0
frontend/views/personform/person-form-view.css View File

@ -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);
}

+ 10476
- 0
package-lock.json
File diff suppressed because it is too large
View File


+ 123
- 0
package.json View File

@ -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"
}
}

+ 269
- 0
pom.xml View File

@ -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>

+ 18
- 0
src/main/java/com/example/application/Application.java View File

@ -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));
}
}

+ 18
- 0
src/main/java/com/example/application/components/leafletmap/LeafletMap.java View File

@ -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);
}
}

+ 42
- 0
src/main/java/com/example/application/data/AbstractEntity.java View File

@ -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);
}
}

+ 47
- 0
src/main/java/com/example/application/data/entity/SampleAddress.java View File

@ -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;
}
}

+ 62
- 0
src/main/java/com/example/application/data/entity/SamplePerson.java View File

@ -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;
}
}

+ 49
- 0
src/main/java/com/example/application/data/generator/DataGenerator.java View File

@ -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");
};
}
}

+ 9
- 0
src/main/java/com/example/application/data/service/SampleAddressRepository.java View File

@ -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> {
}

+ 23
- 0
src/main/java/com/example/application/data/service/SampleAddressService.java View File

@ -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;
}
}

+ 10
- 0
src/main/java/com/example/application/data/service/SamplePersonRepository.java View File

@ -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> {
}

+ 24
- 0
src/main/java/com/example/application/data/service/SamplePersonService.java View File

@ -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;
}
}

+ 20
- 0
src/main/java/com/example/application/views/about/AboutView.java View File

@ -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"));
}
}

+ 84
- 0
src/main/java/com/example/application/views/addressform/AddressFormView.java View File

@ -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());
}
}

+ 157
- 0
src/main/java/com/example/application/views/cardlist/CardListView.java View File

@ -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;
}
}

+ 71
- 0
src/main/java/com/example/application/views/cardlist/Person.java View File

@ -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;
}
}

+ 101
- 0
src/main/java/com/example/application/views/creditcardform/CreditCardFormView.java View File

@ -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
}
}
}

+ 34
- 0
src/main/java/com/example/application/views/helloworld/HelloWorldView.java View File

@ -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());
});
}
}

+ 124
- 0
src/main/java/com/example/application/views/main/MainView.java View File

@ -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();
}
}

+ 23
- 0
src/main/java/com/example/application/views/map/MapView.java View File

@ -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);
}
}

+ 188
- 0
src/main/java/com/example/application/views/masterdetail/MasterDetailView.java View File

@ -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);
}
}

+ 129
- 0
src/main/java/com/example/application/views/personform/PersonFormView.java View File

@ -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();
}
}
}
}

BIN
src/main/resources/META-INF/resources/icons/icon.png View File

Before After
Width: 512  |  Height: 512  |  Size: 16 KiB

BIN
src/main/resources/META-INF/resources/images/logo.png View File

Before After
Width: 225  |  Height: 225  |  Size: 16 KiB

+ 8
- 0
src/main/resources/application.properties View File

@ -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

+ 6
- 0
src/main/resources/banner.txt View File

@ -0,0 +1,6 @@
____ _ _ _ _ ____ _
/ ___| ___ | |__ __ _ ___ | |__ | |(_) __ _ __ _ | _ \ ___ | | _ _ __ __ ___
\___ \ / __|| '_ \ / _` | / __|| '_ \ | || | / _` | / _` | | | | | / _ \| | | | | |\ \/ / / _ \
___) || (__ | | | || (_| || (__ | | | || || || (_| || (_| | | |_| || __/| |___ | |_| | > < | __/
|____/ \___||_| |_| \__,_| \___||_| |_||_||_| \__, | \__,_| |____/ \___||_____| \__,_|/_/\_\ \___|
|___/

Loading…
Cancel
Save