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