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