Kachel-Header: Titel, Einklapp- und Schließen-Icon auf eine Zeile gestellt, Plus-Kachel klappt mit ein wenn alle Kacheln eingeklappt sind
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
package de.assecutor.votianlt.pages.base.ui.component;
|
||||
|
||||
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.checkbox.Checkbox;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
@@ -31,6 +33,10 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
void onDelete(DeliveryStationTile tile);
|
||||
}
|
||||
|
||||
public interface CollapseListener {
|
||||
void onCollapseChanged(boolean collapsed);
|
||||
}
|
||||
|
||||
private final int stationNumber;
|
||||
|
||||
private final ComboBox<String> company;
|
||||
@@ -48,6 +54,12 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
|
||||
private ChangeListener changeListener;
|
||||
private DeleteListener deleteListener;
|
||||
private CollapseListener collapseListener;
|
||||
|
||||
private boolean collapsed = false;
|
||||
private Button collapseButton;
|
||||
private VerticalLayout collapsedContent;
|
||||
private List<Component> expandedOnlyComponents;
|
||||
|
||||
public DeliveryStationTile(int stationNumber, boolean removable, List<Customer> customers,
|
||||
TranslationHelper translationHelper) {
|
||||
@@ -62,15 +74,14 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
||||
getStyle().set("background-color", "var(--lumo-base-color)");
|
||||
|
||||
// Header
|
||||
// Header with title, collapse button and delete button on one line
|
||||
title = new H3(translationHelper.getTranslation("addjob.station.delivery", stationNumber));
|
||||
title.getStyle().set("margin", "0");
|
||||
title.getStyle().set("margin", "0").set("flex-grow", "1");
|
||||
|
||||
HorizontalLayout titleLayout = new HorizontalLayout();
|
||||
titleLayout.setWidthFull();
|
||||
titleLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
||||
titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||
titleLayout.add(title);
|
||||
collapseButton = new Button(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
|
||||
collapseButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
|
||||
collapseButton.getStyle().set("cursor", "pointer");
|
||||
collapseButton.addClickListener(e -> toggleCollapse());
|
||||
|
||||
Button deleteButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
deleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
@@ -83,7 +94,11 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
} else {
|
||||
deleteButton.getStyle().set("visibility", "hidden");
|
||||
}
|
||||
titleLayout.add(deleteButton);
|
||||
|
||||
HorizontalLayout titleLayout = new HorizontalLayout();
|
||||
titleLayout.setWidthFull();
|
||||
titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||
titleLayout.add(title, collapseButton, deleteButton);
|
||||
|
||||
add(titleLayout);
|
||||
|
||||
@@ -172,6 +187,19 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
|
||||
// Register change listeners on all fields
|
||||
setupChangeListeners();
|
||||
|
||||
// Store references to expanded-mode components (excluding titleLayout which stays visible)
|
||||
expandedOnlyComponents = getChildren().filter(c -> c != titleLayout).toList();
|
||||
|
||||
getStyle().set("transition", "width 0.3s ease, min-width 0.3s ease");
|
||||
|
||||
// Collapsed content (initially hidden)
|
||||
collapsedContent = new VerticalLayout();
|
||||
collapsedContent.setPadding(false);
|
||||
collapsedContent.setSpacing(false);
|
||||
collapsedContent.getStyle().set("gap", "var(--lumo-space-xs)");
|
||||
collapsedContent.setVisible(false);
|
||||
add(collapsedContent);
|
||||
}
|
||||
|
||||
private void setupChangeListeners() {
|
||||
@@ -372,6 +400,14 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
this.deleteListener = listener;
|
||||
}
|
||||
|
||||
public void setCollapseListener(CollapseListener listener) {
|
||||
this.collapseListener = listener;
|
||||
}
|
||||
|
||||
public boolean isCollapsed() {
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user wants to save this address as a customer.
|
||||
*/
|
||||
@@ -403,6 +439,51 @@ public class DeliveryStationTile extends VerticalLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleCollapse() {
|
||||
collapsed = !collapsed;
|
||||
if (collapsed) {
|
||||
updateCollapsedContent();
|
||||
expandedOnlyComponents.forEach(c -> c.setVisible(false));
|
||||
collapsedContent.setVisible(true);
|
||||
setWidth("25%");
|
||||
getStyle().set("min-width", "150px");
|
||||
collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_RIGHT));
|
||||
} else {
|
||||
expandedOnlyComponents.forEach(c -> c.setVisible(true));
|
||||
collapsedContent.setVisible(false);
|
||||
setWidth("40%");
|
||||
getStyle().set("min-width", "300px");
|
||||
collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
|
||||
}
|
||||
if (collapseListener != null) {
|
||||
collapseListener.onCollapseChanged(collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCollapsedContent() {
|
||||
collapsedContent.removeAll();
|
||||
|
||||
addCollapsedLine(company.getValue());
|
||||
|
||||
String name = (getValueOrEmpty(firstName) + " " + getValueOrEmpty(lastName)).trim();
|
||||
addCollapsedLine(name);
|
||||
|
||||
String streetLine = (getValueOrEmpty(street) + " " + getValueOrEmpty(houseNumber)).trim();
|
||||
addCollapsedLine(streetLine);
|
||||
|
||||
String zipCityLine = (getValueOrEmpty(zip) + " " + getValueOrEmpty(city)).trim();
|
||||
addCollapsedLine(zipCityLine);
|
||||
}
|
||||
|
||||
private void addCollapsedLine(String text) {
|
||||
if (text != null && !text.trim().isEmpty()) {
|
||||
Span span = new Span(text);
|
||||
span.getStyle().set("font-size", "var(--lumo-font-size-xs)").set("word-break", "break-word")
|
||||
.set("color", "var(--lumo-secondary-text-color)");
|
||||
collapsedContent.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface for accessing translations from the parent view.
|
||||
*/
|
||||
|
||||
@@ -162,6 +162,10 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
private ComboBox<TaskTemplate> templateComboBox;
|
||||
private TextArea remarkArea;
|
||||
private VerticalLayout pickupSection;
|
||||
private boolean pickupCollapsed = false;
|
||||
private Button pickupCollapseButton;
|
||||
private VerticalLayout pickupCollapsedContent;
|
||||
private List<Component> pickupExpandedOnlyComponents;
|
||||
|
||||
private final Binder<Job> binder = new Binder<>(Job.class);
|
||||
|
||||
@@ -531,6 +535,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
updateTabLabels();
|
||||
});
|
||||
tile.setDeleteListener(this::removeDeliveryStationTile);
|
||||
tile.setCollapseListener(collapsed -> updateAddStationButtonSize());
|
||||
|
||||
deliveryStationTiles.add(tile);
|
||||
|
||||
@@ -571,6 +576,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
resetRouteInformation();
|
||||
triggerValidation();
|
||||
updateTabLabels();
|
||||
updateAddStationButtonSize();
|
||||
});
|
||||
dialog.open();
|
||||
}
|
||||
@@ -1013,19 +1019,22 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
section.getStyle().set("background-color", "var(--lumo-base-color)");
|
||||
|
||||
H3 title = new H3(getTranslation("addjob.section.pickup"));
|
||||
title.getStyle().set("margin", "0");
|
||||
title.getStyle().set("margin", "0").set("flex-grow", "1");
|
||||
|
||||
HorizontalLayout titleLayout = new HorizontalLayout();
|
||||
titleLayout.setWidthFull();
|
||||
titleLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
||||
titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||
titleLayout.add(title);
|
||||
pickupCollapseButton = new Button(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
|
||||
pickupCollapseButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
|
||||
pickupCollapseButton.getStyle().set("cursor", "pointer");
|
||||
pickupCollapseButton.addClickListener(e -> togglePickupCollapse());
|
||||
|
||||
// Invisible placeholder button to match DeliveryStationTile header height
|
||||
Button placeholder = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
placeholder.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||
placeholder.getStyle().set("visibility", "hidden");
|
||||
titleLayout.add(placeholder);
|
||||
|
||||
HorizontalLayout titleLayout = new HorizontalLayout();
|
||||
titleLayout.setWidthFull();
|
||||
titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||
titleLayout.add(title, pickupCollapseButton, placeholder);
|
||||
|
||||
// Alle einzelnen Controls auf volle Breite setzen
|
||||
pickupCompany.setWidthFull();
|
||||
@@ -1064,9 +1073,81 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
|
||||
section.add(savePickupAddress);
|
||||
|
||||
// Store references to expanded-mode components (excluding titleLayout which stays visible)
|
||||
pickupExpandedOnlyComponents = section.getChildren().filter(c -> c != titleLayout).toList();
|
||||
|
||||
section.getStyle().set("transition", "width 0.3s ease, min-width 0.3s ease");
|
||||
|
||||
// Collapsed content (initially hidden)
|
||||
pickupCollapsedContent = new VerticalLayout();
|
||||
pickupCollapsedContent.setPadding(false);
|
||||
pickupCollapsedContent.setSpacing(false);
|
||||
pickupCollapsedContent.getStyle().set("gap", "var(--lumo-space-xs)");
|
||||
pickupCollapsedContent.setVisible(false);
|
||||
section.add(pickupCollapsedContent);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
private void togglePickupCollapse() {
|
||||
pickupCollapsed = !pickupCollapsed;
|
||||
if (pickupCollapsed) {
|
||||
updatePickupCollapsedContent();
|
||||
pickupExpandedOnlyComponents.forEach(c -> c.setVisible(false));
|
||||
pickupCollapsedContent.setVisible(true);
|
||||
pickupSection.setWidth("25%");
|
||||
pickupSection.getStyle().set("min-width", "150px");
|
||||
pickupCollapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_RIGHT));
|
||||
} else {
|
||||
pickupExpandedOnlyComponents.forEach(c -> c.setVisible(true));
|
||||
pickupCollapsedContent.setVisible(false);
|
||||
pickupSection.setWidth("40%");
|
||||
pickupSection.getStyle().set("min-width", "300px");
|
||||
pickupCollapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
|
||||
}
|
||||
updateAddStationButtonSize();
|
||||
}
|
||||
|
||||
private void updateAddStationButtonSize() {
|
||||
boolean allCollapsed = pickupCollapsed
|
||||
&& deliveryStationTiles.stream().allMatch(DeliveryStationTile::isCollapsed);
|
||||
if (allCollapsed) {
|
||||
addStationButton.getStyle().set("width", "25%");
|
||||
addStationButton.getStyle().set("min-width", "150px");
|
||||
} else {
|
||||
addStationButton.getStyle().set("width", "40%");
|
||||
addStationButton.getStyle().set("min-width", "300px");
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePickupCollapsedContent() {
|
||||
pickupCollapsedContent.removeAll();
|
||||
|
||||
addPickupCollapsedLine(pickupCompany.getValue());
|
||||
|
||||
String name = (safeValue(pickupFirstName) + " " + safeValue(pickupLastName)).trim();
|
||||
addPickupCollapsedLine(name);
|
||||
|
||||
String streetLine = (safeValue(pickupStreet) + " " + safeValue(pickupHouseNumber)).trim();
|
||||
addPickupCollapsedLine(streetLine);
|
||||
|
||||
String zipCityLine = (safeValue(pickupZip) + " " + safeValue(pickupCity)).trim();
|
||||
addPickupCollapsedLine(zipCityLine);
|
||||
}
|
||||
|
||||
private void addPickupCollapsedLine(String text) {
|
||||
if (text != null && !text.trim().isEmpty()) {
|
||||
Span span = new Span(text);
|
||||
span.getStyle().set("font-size", "var(--lumo-font-size-xs)").set("word-break", "break-word")
|
||||
.set("color", "var(--lumo-secondary-text-color)");
|
||||
pickupCollapsedContent.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
private String safeValue(TextField field) {
|
||||
return field.getValue() != null ? field.getValue().trim() : "";
|
||||
}
|
||||
|
||||
// createDeliverySection() removed - delivery stations are now handled by
|
||||
// DeliveryStationTile
|
||||
|
||||
|
||||
Reference in New Issue
Block a user