@ -1,24 +1,16 @@ | |||
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. | |||
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. | |||
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. | |||
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> |
@ -1,8 +1,8 @@ | |||
ALTER TABLE "game_info" | |||
ADD COLUMN "pgn" varchar, | |||
ADD COLUMN "rated" boolean, | |||
ADD COLUMN "time_class" varchar, | |||
ADD COLUMN "rules" varchar, | |||
ADD COLUMN "pgn" varchar, | |||
ADD COLUMN "rated" boolean, | |||
ADD COLUMN "time_class" varchar, | |||
ADD COLUMN "rules" varchar, | |||
ADD COLUMN "white_rating" int, | |||
ADD COLUMN "black_rating" int, | |||
ADD COLUMN "white_result" varchar, |
@ -1,5 +1,5 @@ | |||
ALTER TABLE "season" | |||
ADD COLUMN "week_of_first_matchday" int not null default 9 CHECK ( week_of_first_matchday > 0 AND week_of_first_matchday <= 53 ), | |||
ADD COLUMN "week_of_first_matchday" int not null default 9 CHECK ( week_of_first_matchday > 0 AND week_of_first_matchday <= 53 ), | |||
ADD COLUMN "first_weekday_of_matchday" int not null default 2 CHECK ( first_weekday_of_matchday > 0 AND first_weekday_of_matchday <= 7 ), | |||
ADD COLUMN "last_weekday_of_matchday" int not null default 1 CHECK ( last_weekday_of_matchday > 0 AND last_weekday_of_matchday <= 7 ), | |||
ADD COLUMN "weekday_of_view_change" int not null default 4 CHECK ( weekday_of_view_change > 0 AND weekday_of_view_change <= 7 ); | |||
ADD COLUMN "last_weekday_of_matchday" int not null default 1 CHECK ( last_weekday_of_matchday > 0 AND last_weekday_of_matchday <= 7 ), | |||
ADD COLUMN "weekday_of_view_change" int not null default 4 CHECK ( weekday_of_view_change > 0 AND weekday_of_view_change <= 7 ); |
@ -1,3 +1,3 @@ | |||
.about-view { | |||
display: block; | |||
display: block; | |||
} |
@ -1,10 +1,11 @@ | |||
.address-form-view { | |||
display: block; | |||
margin: 0 auto; | |||
max-width: 1024px; | |||
padding: 0 var(--lumo-space-l); | |||
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); | |||
margin-bottom: var(--lumo-space-l); | |||
margin-top: var(--lumo-space-m); | |||
} |
@ -1,70 +1,70 @@ | |||
.card-list-view { | |||
display: block; | |||
height: 100%; | |||
display: block; | |||
height: 100%; | |||
} | |||
.card-list-view vaadin-grid { | |||
height: 100%; | |||
line-height: var(--lumo-line-height-m); | |||
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); | |||
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); | |||
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); | |||
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; | |||
align-items: baseline; | |||
} | |||
.card-list-view .name { | |||
font-size: var(--lumo-font-size-s); | |||
font-weight: bold; | |||
margin-right: var(--lumo-space-s); | |||
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); | |||
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; | |||
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; | |||
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); | |||
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); | |||
color: var(--lumo-tertiary-text-color); | |||
font-size: var(--lumo-font-size-xs); | |||
margin-right: var(--lumo-space-l); | |||
} |
@ -1,10 +1,11 @@ | |||
.credit-card-form-view { | |||
display: block; | |||
margin: 0 auto; | |||
max-width: 1024px; | |||
padding: 0 var(--lumo-space-l); | |||
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); | |||
margin-bottom: var(--lumo-space-l); | |||
margin-top: var(--lumo-space-m); | |||
} |
@ -1,4 +1,4 @@ | |||
.hello-world-view { | |||
display: block; | |||
padding: 1em; | |||
display: block; | |||
padding: 1em; | |||
} |
@ -1,8 +1,8 @@ | |||
.map-view { | |||
display: flex; | |||
height: 100%; | |||
display: flex; | |||
height: 100%; | |||
} | |||
.map-view .map { | |||
flex: 1; | |||
flex: 1; | |||
} |
@ -1,33 +1,33 @@ | |||
.master-detail-view { | |||
display: flex; | |||
height: 100%; | |||
flex-direction: column; | |||
display: flex; | |||
height: 100%; | |||
flex-direction: column; | |||
} | |||
.master-detail-view .grid-wrapper { | |||
flex-grow: 1; | |||
width: 100%; | |||
flex-grow: 1; | |||
width: 100%; | |||
} | |||
.master-detail-view .full-size { | |||
width: 100%; | |||
height: 100%; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
.master-detail-view #editor-layout { | |||
width: 400px; | |||
display: flex; | |||
flex-direction: column; | |||
width: 400px; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.master-detail-view #editor { | |||
padding: var(--lumo-space-l); | |||
flex-grow: 1; | |||
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); | |||
width: 100%; | |||
flex-wrap: wrap; | |||
background-color: var(--lumo-contrast-5pct); | |||
padding: var(--lumo-space-s) var(--lumo-space-l); | |||
} |
@ -1,10 +1,11 @@ | |||
.person-form-view { | |||
display: block; | |||
margin: 0 auto; | |||
max-width: 1024px; | |||
padding: 0 var(--lumo-space-l); | |||
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); | |||
margin-bottom: var(--lumo-space-l); | |||
margin-top: var(--lumo-space-m); | |||
} |
@ -1,11 +1,11 @@ | |||
.matchday-view .column_header { | |||
font-weight: bold; | |||
font-size: large; | |||
font-weight: bold; | |||
font-size: large; | |||
} | |||
.matchday-view .matchday-header-label-layout { | |||
width: 100%; | |||
justify-content: center; | |||
font-size: x-large; | |||
width: 100%; | |||
justify-content: center; | |||
font-size: x-large; | |||
} | |||
@ -1,10 +1,10 @@ | |||
.table-view .important-table-column-header { | |||
font-weight: bold; | |||
font-size: large; | |||
font-weight: bold; | |||
font-size: large; | |||
} | |||
.table-view .table-header-label-layout { | |||
width: 100%; | |||
justify-content: center; | |||
font-size: x-large; | |||
width: 100%; | |||
justify-content: center; | |||
font-size: x-large; | |||
} |
@ -1,8 +0,0 @@ | |||
package app.navigation; | |||
public enum NavigationLevel { | |||
NONE, | |||
SEASON, | |||
MATCHDAY, | |||
MATCH | |||
} |
@ -1,40 +0,0 @@ | |||
package app.navigation.components; | |||
import app.navigation.Navigation; | |||
import com.vaadin.flow.component.html.Label; | |||
import com.vaadin.flow.component.orderedlayout.FlexComponent; | |||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||
public class NavigationHeader extends HorizontalLayout { | |||
private final Navigation navigation; | |||
private final Label seasonLabel = new Label("Season:"); | |||
private final Label matchdayLabel = new Label("Matchday:"); | |||
private final Label matchLabel = new Label("Match:"); | |||
public NavigationHeader(Navigation navigation) { | |||
this.navigation = navigation; | |||
configureLayout(); | |||
configureChildren(); | |||
} | |||
private void configureLayout() { | |||
setWidthFull(); | |||
setAlignItems(FlexComponent.Alignment.CENTER); | |||
setJustifyContentMode(FlexComponent.JustifyContentMode.END); | |||
} | |||
private void configureChildren() { | |||
removeAll(); | |||
if (navigation.seasonEnabled()) { | |||
add(seasonLabel, navigation.getSeasonSelect()); | |||
} | |||
if (navigation.matchdayEnabled()) { | |||
add(matchdayLabel, navigation.getMatchdaySelect()); | |||
} | |||
if (navigation.matchEnabled()) { | |||
add(matchLabel, navigation.getMatchSelect()); | |||
} | |||
} | |||
} |
@ -1,14 +0,0 @@ | |||
package app.navigation.components.button; | |||
import app.navigation.Navigation; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
class ButtonUtils { | |||
static int getMatchdayIndex(Navigation navigation) { | |||
AtomicInteger index = new AtomicInteger(); | |||
navigation.getSelectedMatchday().ifPresent(matchday -> index.set(navigation.getMatchdayList().indexOf(matchday))); | |||
return index.get(); | |||
} | |||
} |
@ -1,38 +0,0 @@ | |||
package app.navigation.components.button; | |||
import app.data.entity.Matchday; | |||
import app.navigation.Navigation; | |||
import com.vaadin.flow.component.button.Button; | |||
import com.vaadin.flow.component.icon.Icon; | |||
import com.vaadin.flow.component.icon.VaadinIcon; | |||
import java.util.Optional; | |||
public class NextMatchdayButton extends Button { | |||
private final Navigation navigation; | |||
public NextMatchdayButton(Navigation navigation) { | |||
this.navigation = navigation; | |||
if (!navigation.matchdayEnabled()) | |||
throw new IllegalStateException("Cannot instantiate NextMatchdayButton when Matchdays are not enabled!"); | |||
setIcon(new Icon(VaadinIcon.ARROW_RIGHT)); | |||
navigation.addRunnableToBeRunAfterSelection(this::configure); | |||
} | |||
private void configure() { | |||
Optional<Matchday> nextMatchday = getNextMatchday(); | |||
setEnabled(nextMatchday.isPresent()); | |||
addClickListener(event -> nextMatchday.ifPresent(matchday -> navigation.getMatchdaySelect().setValue(matchday))); | |||
} | |||
private Optional<Matchday> getNextMatchday() { | |||
int index = ButtonUtils.getMatchdayIndex(navigation); | |||
if (index >= 0 && index < navigation.getMatchdayList().size() - 1) | |||
return Optional.ofNullable(navigation.getMatchdayList().get(index + 1)); | |||
return Optional.empty(); | |||
} | |||
} |
@ -1,37 +0,0 @@ | |||
package app.navigation.components.button; | |||
import app.data.entity.Matchday; | |||
import app.navigation.Navigation; | |||
import com.vaadin.flow.component.button.Button; | |||
import com.vaadin.flow.component.icon.Icon; | |||
import com.vaadin.flow.component.icon.VaadinIcon; | |||
import java.util.Optional; | |||
public class PrevMatchdayButton extends Button { | |||
private final Navigation navigation; | |||
public PrevMatchdayButton(Navigation navigation) { | |||
this.navigation = navigation; | |||
if (!navigation.matchdayEnabled()) | |||
throw new IllegalStateException("Cannot instantiate PrevMatchdayButton when Matchdays are not enabled!"); | |||
setIcon(new Icon(VaadinIcon.ARROW_LEFT)); | |||
navigation.addRunnableToBeRunAfterSelection(this::configure); | |||
} | |||
private void configure() { | |||
Optional<Matchday> prevMatchday = getPrevMatchday(); | |||
setEnabled(prevMatchday.isPresent()); | |||
addClickListener(event -> prevMatchday.ifPresent(matchday -> navigation.getMatchdaySelect().setValue(matchday))); | |||
} | |||
private Optional<Matchday> getPrevMatchday() { | |||
int index = ButtonUtils.getMatchdayIndex(navigation); | |||
if (index > 0) return Optional.ofNullable(navigation.getMatchdayList().get(index - 1)); | |||
return Optional.empty(); | |||
} | |||
} |
@ -0,0 +1,194 @@ | |||
package app.navigation.player; | |||
import app.components.label.ValidationLabel; | |||
import app.data.entity.Player; | |||
import app.data.entity.Season; | |||
import app.data.service.PlayerService; | |||
import app.data.service.SeasonService; | |||
import app.navigation.NavigationUtils; | |||
import app.utils.EntityStringUtils; | |||
import com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent; | |||
import com.vaadin.flow.component.HasValue.ValueChangeListener; | |||
import com.vaadin.flow.component.UI; | |||
import com.vaadin.flow.component.select.Select; | |||
import com.vaadin.flow.router.BeforeEvent; | |||
import com.vaadin.flow.router.HasUrlParameter; | |||
import com.vaadin.flow.router.WildcardParameter; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") | |||
public class PlayerNavigation implements HasUrlParameter<String> { | |||
private String route; | |||
private final List<Runnable> runnablesToBeRunAfterSelection = new ArrayList<>(); | |||
private final PlayerService playerService; | |||
private final SeasonService seasonService; | |||
private final List<Player> playerList = new ArrayList<>(); | |||
private final List<Season> seasonList = new ArrayList<>(); | |||
private final Select<Player> playerSelect = new Select<>(); | |||
private final Select<Season> seasonSelect = new Select<>(); | |||
private final ValidationLabel validationLabel = new ValidationLabel(); | |||
public PlayerNavigation(@Autowired PlayerService playerService, | |||
@Autowired SeasonService seasonService) { | |||
this.playerService = playerService; | |||
this.seasonService = seasonService; | |||
fillPlayerSelectWithData(); | |||
playerSelect.addValueChangeListener(playerSelectValueChangeListener()); | |||
seasonSelect.addValueChangeListener(seasonSelectValueChangeListener()); | |||
playerSelect.setItemLabelGenerator(EntityStringUtils::getPlayerString); | |||
seasonSelect.setItemLabelGenerator(EntityStringUtils::getSeasonString); | |||
} | |||
public void setRoute(String route) { | |||
this.route = route; | |||
} | |||
public void addRunnableToBeRunAfterSelection(Runnable runnable) { | |||
runnablesToBeRunAfterSelection.add(runnable); | |||
} | |||
private String getRoute() { | |||
if (route != null) return route; | |||
throw new IllegalStateException("Route must be set!"); | |||
} | |||
private void updateUrl() { | |||
String playerParam = null; | |||
String seasonParam = null; | |||
if (playerSelect.getOptionalValue().isPresent()) | |||
playerParam = EntityStringUtils.getPlayerStringForURL(playerSelect.getValue()); | |||
if (seasonSelect.getOptionalValue().isPresent()) | |||
seasonParam = EntityStringUtils.getSeasonStringForURL(seasonSelect.getValue()); | |||
String params = NavigationUtils.getWildcardParam(false, playerParam, seasonParam); | |||
UI.getCurrent().getPage().getHistory().pushState(null, String.format("%s/%s", getRoute(), params)); | |||
} | |||
private ValueChangeListener<ComponentValueChangeEvent<Select<Player>, Player>> playerSelectValueChangeListener() { | |||
return event -> { | |||
fillSeasonSelectWithData(event.getValue()); | |||
autoselectSeason(); | |||
}; | |||
} | |||
private ValueChangeListener<ComponentValueChangeEvent<Select<Season>, Season>> seasonSelectValueChangeListener() { | |||
return event -> doPostSelectionStuff(); | |||
} | |||
private void doPostSelectionStuff() { | |||
validationLabel.setValid(true); | |||
updateUrl(); | |||
runnablesToBeRunAfterSelection.forEach(Runnable::run); | |||
} | |||
private void autoselectPlayer() { | |||
if (playerList.isEmpty()) { | |||
validationLabel.setText("No Players in List!"); | |||
validationLabel.setValid(false); | |||
return; | |||
} | |||
playerSelect.setValue(playerList.get(0)); | |||
} | |||
private void autoselectSeason() { | |||
if (seasonList.isEmpty()) { | |||
validationLabel.setText("No Season in List!"); | |||
validationLabel.setValid(false); | |||
return; | |||
} | |||
seasonSelect.setValue(seasonList.get(seasonList.size() - 1)); | |||
} | |||
private void fillPlayerSelectWithData() { | |||
playerList.clear(); | |||
playerList.addAll(playerService.getAllPlayersSorted()); | |||
playerSelect.setItems(playerList); | |||
} | |||
private void fillSeasonSelectWithData(Player player) { | |||
seasonList.clear(); | |||
seasonList.addAll(seasonService.getAllSeasonsForPlayerSorted(player)); | |||
seasonSelect.setItems(seasonList); | |||
} | |||
@Override | |||
public void setParameter(BeforeEvent event, @WildcardParameter String param) { | |||
Map<PlayerNavigationLevel, Optional<String>> map = PlayerNavigationUtils.getParameterMap(param); | |||
navigate(map.get(PlayerNavigationLevel.PLAYER), map.get(PlayerNavigationLevel.SEASON)); | |||
} | |||
private void navigate(Optional<String> playerParam, Optional<String> seasonParam) { | |||
Optional<Player> player = NavigationUtils.getObjectFromParam(playerList, playerParam); | |||
if (player.isPresent()) playerSelect.setValue(player.get()); | |||
else autoselectPlayer(); | |||
Optional<Season> season = NavigationUtils.getObjectFromParam(seasonList, seasonParam); | |||
if (season.isPresent()) seasonSelect.setValue(season.get()); | |||
else autoselectSeason(); | |||
} | |||
public ValidationLabel getValidationLabel() { | |||
return validationLabel; | |||
} | |||
public List<Player> getPlayerList() { | |||
return playerList; | |||
} | |||
public List<Season> getSeasonList() { | |||
return seasonList; | |||
} | |||
public Select<Player> getPlayerSelect() { | |||
return playerSelect; | |||
} | |||
public Select<Season> getSeasonSelect() { | |||
return seasonSelect; | |||
} | |||
public Optional<Player> getSelectedPlayer() { | |||
return playerSelect.getOptionalValue(); | |||
} | |||
public Optional<Season> getSelectedSeason() { | |||
return seasonSelect.getOptionalValue(); | |||
} | |||
public PlayerService getPlayerService() { | |||
return playerService; | |||
} | |||
public SeasonService getSeasonService() { | |||
return seasonService; | |||
} | |||
public String getWildcardParam() { | |||
return NavigationUtils.getWildcardParam( | |||
false, | |||
getPlayerParam().orElse(null), | |||
getSeasonParam().orElse(null)); | |||
} | |||
public Optional<String> getPlayerParam() { | |||
return getSelectedPlayer().map(EntityStringUtils::getPlayerStringForURL); | |||
} | |||
public Optional<String> getSeasonParam() { | |||
return getSelectedSeason().map(EntityStringUtils::getSeasonStringForURL); | |||
} | |||
} |
@ -0,0 +1,7 @@ | |||
package app.navigation.player; | |||
public enum PlayerNavigationLevel { | |||
NONE, | |||
PLAYER, | |||
SEASON, | |||
} |
@ -0,0 +1,23 @@ | |||
package app.navigation.player; | |||
import app.data.service.PlayerService; | |||
import app.data.service.SeasonService; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
@Service | |||
public class PlayerNavigationService { | |||
private final SeasonService seasonService; | |||
private final PlayerService playerService; | |||
public PlayerNavigationService(@Autowired PlayerService playerService, | |||
@Autowired SeasonService seasonService) { | |||
this.playerService = playerService; | |||
this.seasonService = seasonService; | |||
} | |||
public PlayerNavigation getNewNavigation() { | |||
return new PlayerNavigation(playerService, seasonService); | |||
} | |||
} |
@ -0,0 +1,25 @@ | |||
package app.navigation.player; | |||
import com.vaadin.flow.router.WildcardParameter; | |||
import org.springframework.lang.NonNull; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
class PlayerNavigationUtils { | |||
private PlayerNavigationUtils() { | |||
} | |||
@NonNull | |||
static Map<PlayerNavigationLevel, Optional<String>> getParameterMap(@WildcardParameter String param) { | |||
Map<PlayerNavigationLevel, Optional<String>> map = new HashMap<>(); | |||
String[] params = param.split("/"); | |||
if (params.length >= 1) map.put(PlayerNavigationLevel.PLAYER, Optional.of(params[0])); | |||
if (params.length >= 2) map.put(PlayerNavigationLevel.SEASON, Optional.of(params[1])); | |||
map.putIfAbsent(PlayerNavigationLevel.PLAYER, Optional.empty()); | |||
map.putIfAbsent(PlayerNavigationLevel.SEASON, Optional.empty()); | |||
return map; | |||
} | |||
} |
@ -0,0 +1,31 @@ | |||
package app.navigation.player.components; | |||
import app.navigation.player.PlayerNavigation; | |||
import com.vaadin.flow.component.html.Label; | |||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||
public class PlayerNavigationHeader extends HorizontalLayout { | |||
private final PlayerNavigation playerNavigation; | |||
private final Label seasonLabel = new Label("Season:"); | |||
private final Label playerLabel = new Label("Player:"); | |||
public PlayerNavigationHeader(PlayerNavigation playerNavigation) { | |||
this.playerNavigation = playerNavigation; | |||
defineLayout(); | |||
configureChildren(); | |||
} | |||
private void defineLayout() { | |||
setWidthFull(); | |||
setAlignItems(Alignment.CENTER); | |||
setJustifyContentMode(JustifyContentMode.END); | |||
} | |||
private void configureChildren() { | |||
removeAll(); | |||
add(playerLabel, playerNavigation.getPlayerSelect()); | |||
add(seasonLabel, playerNavigation.getSeasonSelect()); | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
package app.navigation.player.components.button; | |||
import app.data.entity.Player; | |||
import app.navigation.player.PlayerNavigation; | |||
import com.vaadin.flow.component.button.Button; | |||
import com.vaadin.flow.component.icon.VaadinIcon; | |||
import java.util.List; | |||
import java.util.Optional; | |||
public class NextPlayerButton extends Button { | |||
private final PlayerNavigation playerNavigation; | |||
public NextPlayerButton(PlayerNavigation playerNavigation) { | |||
this.playerNavigation = playerNavigation; | |||
setIcon(VaadinIcon.ARROW_RIGHT.create()); | |||
playerNavigation.addRunnableToBeRunAfterSelection(this::configure); | |||
} | |||
private void configure() { | |||
Optional<Player> nextPlayer = getNextPlayer(); | |||
setEnabled(nextPlayer.isPresent()); | |||
addClickListener(event -> nextPlayer.ifPresent(player -> playerNavigation.getPlayerSelect().setValue(player))); | |||
} | |||
private Optional<Player> getNextPlayer() { | |||
int index = PlayerButtonUtils.getPlayerIndex(playerNavigation); | |||
if (index < 0) return Optional.empty(); | |||
List<Player> playerList = playerNavigation.getPlayerList(); | |||
return Optional.ofNullable(playerList.get((index + 1) % playerList.size())); | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
package app.navigation.player.components.button; | |||
import app.navigation.player.PlayerNavigation; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
class PlayerButtonUtils { | |||
static int getPlayerIndex(PlayerNavigation playerNavigation) { | |||
AtomicInteger index = new AtomicInteger(-1); | |||
playerNavigation.getSelectedPlayer().ifPresent(player -> index.set(playerNavigation.getPlayerList().indexOf(player))); | |||
return index.get(); | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
package app.navigation.player.components.button; | |||
import app.data.entity.Player; | |||
import app.navigation.player.PlayerNavigation; | |||
import com.vaadin.flow.component.button.Button; | |||
import com.vaadin.flow.component.icon.VaadinIcon; | |||
import java.util.List; | |||
import java.util.Optional; | |||
public class PrevPlayerButton extends Button { | |||
private final PlayerNavigation playerNavigation; | |||
public PrevPlayerButton(PlayerNavigation playerNavigation) { | |||
this.playerNavigation = playerNavigation; | |||
setIcon(VaadinIcon.ARROW_LEFT.create()); | |||
playerNavigation.addRunnableToBeRunAfterSelection(this::configure); | |||
} | |||
private void configure() { | |||
Optional<Player> prevPlayer = getPrevPlayer(); | |||
setEnabled(prevPlayer.isPresent()); | |||
addClickListener(event -> prevPlayer.ifPresent(player -> playerNavigation.getPlayerSelect().setValue(player))); | |||
} | |||
private Optional<Player> getPrevPlayer() { | |||
int index = PlayerButtonUtils.getPlayerIndex(playerNavigation); | |||
if (index < 0) return Optional.empty(); | |||
List<Player> playerList = playerNavigation.getPlayerList(); | |||
return Optional.ofNullable(playerList.get((index - 1) % playerList.size())); | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
package app.navigation.regular; | |||
public enum RegularNavigationLevel { | |||
NONE, | |||
SEASON, | |||
MATCHDAY, | |||
MATCH | |||
} |
@ -0,0 +1,31 @@ | |||
package app.navigation.regular; | |||
import app.navigation.NavigationUtils; | |||
import com.vaadin.flow.router.WildcardParameter; | |||
import org.springframework.lang.NonNull; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
class RegularNavigationUtils { | |||
private RegularNavigationUtils() { | |||
} | |||
@NonNull | |||
static Map<RegularNavigationLevel, Optional<String>> getParameterMap(@WildcardParameter String param) { | |||
Map<RegularNavigationLevel, Optional<String>> map = new HashMap<>(); | |||
String[] params = param.split("/"); | |||
if (params.length >= 1 && !params[0].equals(NavigationUtils.EDIT)) | |||
map.put(RegularNavigationLevel.SEASON, Optional.of(params[0])); | |||
if (params.length >= 2 && !params[1].equals(NavigationUtils.EDIT)) | |||
map.put(RegularNavigationLevel.MATCHDAY, Optional.of(params[1])); | |||
if (params.length >= 3 && !params[2].equals(NavigationUtils.EDIT)) | |||
map.put(RegularNavigationLevel.MATCH, Optional.of(params[2])); | |||
map.putIfAbsent(RegularNavigationLevel.MATCH, Optional.empty()); | |||
map.putIfAbsent(RegularNavigationLevel.MATCHDAY, Optional.empty()); | |||
map.putIfAbsent(RegularNavigationLevel.SEASON, Optional.empty()); | |||
return map; | |||
} | |||
} |
@ -0,0 +1,40 @@ | |||
package app.navigation.regular.components; | |||
import app.navigation.regular.RegularNavigation; | |||
import com.vaadin.flow.component.html.Label; | |||
import com.vaadin.flow.component.orderedlayout.FlexComponent; | |||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||
public class RegularNavigationHeader extends HorizontalLayout { | |||
private final RegularNavigation regularNavigation; | |||
private final Label seasonLabel = new Label("Season:"); | |||
private final Label matchdayLabel = new Label("Matchday:"); | |||
private final Label matchLabel = new Label("Match:"); | |||
public RegularNavigationHeader(RegularNavigation regularNavigation) { | |||
this.regularNavigation = regularNavigation; | |||
defineLayout(); | |||
configureChildren(); | |||
} | |||
private void defineLayout() { | |||
setWidthFull(); | |||
setAlignItems(FlexComponent.Alignment.CENTER); | |||
setJustifyContentMode(FlexComponent.JustifyContentMode.END); | |||
} | |||
private void configureChildren() { | |||
removeAll(); | |||
if (regularNavigation.seasonEnabled()) { | |||
add(seasonLabel, regularNavigation.getSeasonSelect()); | |||
} | |||
if (regularNavigation.matchdayEnabled()) { | |||
add(matchdayLabel, regularNavigation.getMatchdaySelect()); | |||
} | |||
if (regularNavigation.matchEnabled()) { | |||
add(matchLabel, regularNavigation.getMatchSelect()); | |||
} | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
package app.navigation.regular.components.button; | |||
import app.navigation.regular.RegularNavigation; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
class MatchdayButtonUtils { | |||
static int getMatchdayIndex(RegularNavigation regularNavigation) { | |||
AtomicInteger index = new AtomicInteger(-1); | |||
regularNavigation.getSelectedMatchday().ifPresent(matchday -> index.set(regularNavigation.getMatchdayList().indexOf(matchday))); | |||
return index.get(); | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
package app.navigation.regular.components.button; | |||
import app.data.entity.Matchday; | |||
import app.navigation.regular.RegularNavigation; | |||
import com.vaadin.flow.component.button.Button; | |||
import com.vaadin.flow.component.icon.VaadinIcon; | |||
import java.util.Optional; | |||
public class NextMatchdayButton extends Button { | |||
private final RegularNavigation regularNavigation; | |||
public NextMatchdayButton(RegularNavigation regularNavigation) { | |||
this.regularNavigation = regularNavigation; | |||
if (!regularNavigation.matchdayEnabled()) | |||
throw new IllegalStateException("Cannot instantiate NextMatchdayButton when Matchdays are not enabled!"); | |||
setIcon(VaadinIcon.ARROW_RIGHT.create()); | |||
regularNavigation.addRunnableToBeRunAfterSelection(this::configure); | |||
} | |||
private void configure() { | |||
Optional<Matchday> nextMatchday = getNextMatchday(); | |||
setEnabled(nextMatchday.isPresent()); | |||
addClickListener(event -> nextMatchday.ifPresent(matchday -> regularNavigation.getMatchdaySelect().setValue(matchday))); | |||
} | |||
private Optional<Matchday> getNextMatchday() { | |||
int index = MatchdayButtonUtils.getMatchdayIndex(regularNavigation); | |||
if (index >= 0 && index < regularNavigation.getMatchdayList().size() - 1) | |||
return Optional.ofNullable(regularNavigation.getMatchdayList().get(index + 1)); | |||
return Optional.empty(); | |||
} | |||
} |
@ -0,0 +1,36 @@ | |||
package app.navigation.regular.components.button; | |||
import app.data.entity.Matchday; | |||
import app.navigation.regular.RegularNavigation; | |||
import com.vaadin.flow.component.button.Button; | |||
import com.vaadin.flow.component.icon.VaadinIcon; | |||
import java.util.Optional; | |||
public class PrevMatchdayButton extends Button { | |||
private final RegularNavigation regularNavigation; | |||
public PrevMatchdayButton(RegularNavigation regularNavigation) { | |||
this.regularNavigation = regularNavigation; | |||
if (!regularNavigation.matchdayEnabled()) | |||
throw new IllegalStateException("Cannot instantiate PrevMatchdayButton when Matchdays are not enabled!"); | |||
setIcon(VaadinIcon.ARROW_LEFT.create()); | |||
regularNavigation.addRunnableToBeRunAfterSelection(this::configure); | |||
} | |||
private void configure() { | |||
Optional<Matchday> prevMatchday = getPrevMatchday(); | |||
setEnabled(prevMatchday.isPresent()); | |||
addClickListener(event -> prevMatchday.ifPresent(matchday -> regularNavigation.getMatchdaySelect().setValue(matchday))); | |||
} | |||
private Optional<Matchday> getPrevMatchday() { | |||
int index = MatchdayButtonUtils.getMatchdayIndex(regularNavigation); | |||
if (index > 0) return Optional.ofNullable(regularNavigation.getMatchdayList().get(index - 1)); | |||
return Optional.empty(); | |||
} | |||
} |