Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,48 +28,41 @@ public MessageListAttachments() {

MessageList list = new MessageList();



Instant yesterday = LocalDateTime.now().minusDays(1)
.toInstant(ZoneOffset.UTC);
Instant fiftyMinsAgo = LocalDateTime.now().minusMinutes(50)
.toInstant(ZoneOffset.UTC);

// tag::snippet[]
MessageListItem message1 = new MessageListItem(
"Here are the documents for the project.",
yesterday, "Matt Mambo");
"Here are the documents for the project.", yesterday,
"Matt Mambo");
message1.setUserColorIndex(1);

message1.addAttachment(new MessageListItem.Attachment(
"project-proposal.pdf",
"https://example.com/files/proposal.pdf",
"application/pdf"));
"https://example.com/files/proposal.pdf", "application/pdf"));
// end::snippet[]
message1.addAttachment(new MessageListItem.Attachment(
"budget-overview.xlsx",
"https://example.com/files/budget.xlsx",
"budget-overview.xlsx", "https://example.com/files/budget.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));

MessageListItem message2 = new MessageListItem(
"Thanks! Here's a photo from the offsite.",
fiftyMinsAgo, "Linsey Listy");
"Thanks! Here's a photo from the offsite.", fiftyMinsAgo,
"Linsey Listy");
message2.setUserColorIndex(2);

String imageDataUrl = toThumbnailDataUrl(
getClass().getResourceAsStream("/images/reindeer.jpg"));
message2.addAttachment(new MessageListItem.Attachment(
"landscape.jpg",
imageDataUrl,
"image/jpeg"));
message2.addAttachment(new MessageListItem.Attachment("landscape.jpg",
imageDataUrl, "image/jpeg"));

// tag::snippet[]
list.setItems(Arrays.asList(message1, message2));

var status = new Span("Click an attachment to see its name here.");
list.addAttachmentClickListener(event -> {
status.setText("Clicked: "
+ event.getAttachment().name());
status.setText("Clicked: " + event.getAttachment().name());
});
// end::snippet[]

Expand All @@ -84,8 +77,7 @@ private static String toThumbnailDataUrl(InputStream imageStream) {
var originalWidth = originalImage.getWidth();
var originalHeight = originalImage.getHeight();

var scale = Math.min(
(double) THUMBNAIL_MAX_SIZE / originalWidth,
var scale = Math.min((double) THUMBNAIL_MAX_SIZE / originalWidth,
(double) THUMBNAIL_MAX_SIZE / originalHeight);
var scaledWidth = (int) (originalWidth * scale);
var scaledHeight = (int) (originalHeight * scale);
Expand All @@ -95,15 +87,13 @@ private static String toThumbnailDataUrl(InputStream imageStream) {
var g2d = scaledImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight,
null);
g2d.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose();

var outputStream = new ByteArrayOutputStream();
ImageIO.write(scaledImage, "jpg", outputStream);
return "data:image/jpeg;base64,"
+ Base64.getEncoder()
.encodeToString(outputStream.toByteArray());
return "data:image/jpeg;base64," + Base64.getEncoder()
.encodeToString(outputStream.toByteArray());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.popover.Popover;
import com.vaadin.flow.theme.lumo.LumoIcon;
import com.vaadin.flow.dom.Element;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public Notification show() {
notification.close();
});

HorizontalLayout layout = new HorizontalLayout(new Text("5 tasks deleted"));
HorizontalLayout layout = new HorizontalLayout(
new Text("5 tasks deleted"));
layout.addToEnd(undoButton, new CloseButton());
layout.setAlignItems(Alignment.CENTER);
layout.setMinWidth("300px");
Expand Down
100 changes: 70 additions & 30 deletions src/main/java/com/vaadin/demo/flow/signals/ConditionalVisibility.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,52 @@ static class VisaApplicationData {
private String previousEmployer;

// getters and setters omitted for brevity
public Boolean getNeedsVisa() { return needsVisa; }
public void setNeedsVisa(Boolean needsVisa) { this.needsVisa = needsVisa; }
public VisaType getVisaType() { return visaType; }
public void setVisaType(VisaType visaType) { this.visaType = visaType; }
public String getCurrentVisaStatus() { return currentVisaStatus; }
public void setCurrentVisaStatus(String currentVisaStatus) { this.currentVisaStatus = currentVisaStatus; }
public Boolean getHasH1BPreviously() { return hasH1BPreviously; }
public void setHasH1BPreviously(Boolean hasH1BPreviously) { this.hasH1BPreviously = hasH1BPreviously; }
public String getPreviousEmployer() { return previousEmployer; }
public void setPreviousEmployer(String previousEmployer) { this.previousEmployer = previousEmployer; }
public Boolean getNeedsVisa() {
return needsVisa;
}

public void setNeedsVisa(Boolean needsVisa) {
this.needsVisa = needsVisa;
}

public VisaType getVisaType() {
return visaType;
}

public void setVisaType(VisaType visaType) {
this.visaType = visaType;
}

public String getCurrentVisaStatus() {
return currentVisaStatus;
}

public void setCurrentVisaStatus(String currentVisaStatus) {
this.currentVisaStatus = currentVisaStatus;
}

public Boolean getHasH1BPreviously() {
return hasH1BPreviously;
}

public void setHasH1BPreviously(Boolean hasH1BPreviously) {
this.hasH1BPreviously = hasH1BPreviously;
}

public String getPreviousEmployer() {
return previousEmployer;
}

public void setPreviousEmployer(String previousEmployer) {
this.previousEmployer = previousEmployer;
}
}

public ConditionalVisibility() {
// tag::setup[]
// Create binder and data bean
Binder<VisaApplicationData> binder = new Binder<>(VisaApplicationData.class);
Binder<VisaApplicationData> binder = new Binder<>(
VisaApplicationData.class);
VisaApplicationData data = new VisaApplicationData();
binder.setBean(data);

Expand All @@ -50,25 +80,29 @@ public ConditionalVisibility() {

// tag::level0[]
// Base question: needs visa sponsorship
Checkbox needsVisaCheckbox = new Checkbox("Do you require visa sponsorship?");
binder.forField(needsVisaCheckbox)
.bind(VisaApplicationData::getNeedsVisa, VisaApplicationData::setNeedsVisa);
Checkbox needsVisaCheckbox = new Checkbox(
"Do you require visa sponsorship?");
binder.forField(needsVisaCheckbox).bind(
VisaApplicationData::getNeedsVisa,
VisaApplicationData::setNeedsVisa);
needsVisaCheckbox.bindValue(needsVisaSignal, needsVisaSignal::set);
// end::level0[]

// tag::level1[]
// Level 1: Visa-related fields (shown when needsVisa is true)
VerticalLayout visaSection = new VerticalLayout();

ComboBox<VisaType> visaTypeSelect = new ComboBox<>("Visa Type", VisaType.values());
ComboBox<VisaType> visaTypeSelect = new ComboBox<>("Visa Type",
VisaType.values());
visaTypeSelect.setValue(VisaType.H1B);
binder.forField(visaTypeSelect)
.bind(VisaApplicationData::getVisaType, VisaApplicationData::setVisaType);
binder.forField(visaTypeSelect).bind(VisaApplicationData::getVisaType,
VisaApplicationData::setVisaType);
visaTypeSelect.bindValue(visaTypeSignal, visaTypeSignal::set);

TextField currentVisaStatus = new TextField("Current Visa Status");
binder.forField(currentVisaStatus)
.bind(VisaApplicationData::getCurrentVisaStatus, VisaApplicationData::setCurrentVisaStatus);
binder.forField(currentVisaStatus).bind(
VisaApplicationData::getCurrentVisaStatus,
VisaApplicationData::setCurrentVisaStatus);

visaSection.add(visaTypeSelect, currentVisaStatus);
visaSection.bindVisible(needsVisaSignal);
Expand All @@ -78,34 +112,40 @@ public ConditionalVisibility() {
// Level 2: H1-B specific fields (shown when visa type is H1B)
VerticalLayout h1bSection = new VerticalLayout();

Checkbox hasH1BPreviouslyCheckbox = new Checkbox("Have you held an H1-B visa before?");
binder.forField(hasH1BPreviouslyCheckbox)
.bind(VisaApplicationData::getHasH1BPreviously, VisaApplicationData::setHasH1BPreviously);
hasH1BPreviouslyCheckbox.bindValue(hasH1BPreviouslySignal, hasH1BPreviouslySignal::set);
Checkbox hasH1BPreviouslyCheckbox = new Checkbox(
"Have you held an H1-B visa before?");
binder.forField(hasH1BPreviouslyCheckbox).bind(
VisaApplicationData::getHasH1BPreviously,
VisaApplicationData::setHasH1BPreviously);
hasH1BPreviouslyCheckbox.bindValue(hasH1BPreviouslySignal,
hasH1BPreviouslySignal::set);

TextField h1bSpecialtyOccupation = new TextField("Specialty Occupation");
TextField h1bSpecialtyOccupation = new TextField(
"Specialty Occupation");
// Binder binding omitted for brevity

h1bSection.add(hasH1BPreviouslyCheckbox, h1bSpecialtyOccupation);
h1bSection.bindVisible(() -> needsVisaSignal.get()
&& visaTypeSignal.get() == VisaType.H1B);
&& visaTypeSignal.get() == VisaType.H1B);
// end::level2[]

// tag::level3[]
// Level 3: Previous H1-B details (shown when has H1-B previously)
VerticalLayout previousH1BSection = new VerticalLayout();

TextField previousEmployer = new TextField("Previous Employer");
binder.forField(previousEmployer)
.bind(VisaApplicationData::getPreviousEmployer, VisaApplicationData::setPreviousEmployer);
binder.forField(previousEmployer).bind(
VisaApplicationData::getPreviousEmployer,
VisaApplicationData::setPreviousEmployer);

TextField previousPetitionNumber = new TextField("Previous Petition Number");
TextField previousPetitionNumber = new TextField(
"Previous Petition Number");
// Additional fields omitted for brevity

previousH1BSection.add(previousEmployer, previousPetitionNumber);
previousH1BSection.bindVisible(() -> needsVisaSignal.get()
&& visaTypeSignal.get() == VisaType.H1B
&& hasH1BPreviouslySignal.get());
&& visaTypeSignal.get() == VisaType.H1B
&& hasH1BPreviouslySignal.get());
// end::level3[]

add(needsVisaCheckbox, visaSection, h1bSection, previousH1BSection);
Expand Down
38 changes: 23 additions & 15 deletions src/main/java/com/vaadin/demo/flow/signals/ShoppingCartSignals.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,29 @@
@Route("shopping-cart-with-signals")
public class ShoppingCartSignals extends VerticalLayout {

record Product(String id, String name, BigDecimal price) {}
record Product(String id, String name, BigDecimal price) {
}

record CartItem(Product product, int quantity) {
CartItem withQuantity(int newQuantity) {
return new CartItem(this.product, newQuantity);
}
}
record DiscountCode(String code, BigDecimal percentage) {}
enum ShippingOption { STANDARD, EXPRESS, OVERNIGHT }

record DiscountCode(String code, BigDecimal percentage) {
}

enum ShippingOption {
STANDARD, EXPRESS, OVERNIGHT
}

public ShoppingCartSignals() {
// tag::signals[]
// Create signals for cart state
ListSignal<CartItem> cartItemsSignal = new ListSignal<>();
ValueSignal<String> discountCodeSignal = new ValueSignal<>("");
ValueSignal<ShippingOption> shippingOptionSignal = new ValueSignal<>(ShippingOption.STANDARD);
ValueSignal<ShippingOption> shippingOptionSignal = new ValueSignal<>(
ShippingOption.STANDARD);
// end::signals[]

// tag::computed-subtotal[]
Expand Down Expand Up @@ -77,8 +85,8 @@ public ShoppingCartSignals() {

// tag::computed-total[]
// Computed signal for grand total
Signal<BigDecimal> totalSignal = Signal.computed(() -> subtotalSignal.get()
.subtract(discountSignal.get()).add(shippingSignal.get())
Signal<BigDecimal> totalSignal = Signal.computed(() -> subtotalSignal
.get().subtract(discountSignal.get()).add(shippingSignal.get())
.add(taxSignal.get()).setScale(2, RoundingMode.HALF_UP));
// end::computed-total[]

Expand Down Expand Up @@ -120,10 +128,11 @@ public ShoppingCartSignals() {
// end::discount-field[]

// tag::shipping-select[]
ComboBox<ShippingOption> shippingSelect = new ComboBox<>("Shipping Method",
ShippingOption.values());
ComboBox<ShippingOption> shippingSelect = new ComboBox<>(
"Shipping Method", ShippingOption.values());
shippingSelect.setValue(ShippingOption.STANDARD);
shippingSelect.bindValue(shippingOptionSignal, shippingOptionSignal::set);
shippingSelect.bindValue(shippingOptionSignal,
shippingOptionSignal::set);
// end::shipping-select[]

optionsLayout.add(discountField, shippingSelect);
Expand Down Expand Up @@ -184,8 +193,8 @@ private HorizontalLayout createCartItemRow(ValueSignal<CartItem> itemSignal,
HorizontalLayout row = new HorizontalLayout();

Span nameLabel = new Span(itemSignal.map(item -> item.product().name()
+ " - $" + item.product().price().setScale(2,
RoundingMode.HALF_UP)));
+ " - $"
+ item.product().price().setScale(2, RoundingMode.HALF_UP)));

IntegerField quantityField = new IntegerField();
quantityField.setMin(1);
Expand All @@ -205,10 +214,9 @@ private HorizontalLayout createCartItemRow(ValueSignal<CartItem> itemSignal,
}
});

Span itemTotalLabel = new Span(itemSignal.map(item -> "$"
+ item.product().price()
.multiply(BigDecimal.valueOf(item.quantity()))
.setScale(2, RoundingMode.HALF_UP)));
Span itemTotalLabel = new Span(itemSignal.map(item -> "$" + item
.product().price().multiply(BigDecimal.valueOf(item.quantity()))
.setScale(2, RoundingMode.HALF_UP)));

Button removeButton = new Button("Remove", e -> {
cartItemsSignal.remove(itemSignal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ public GridFilteringExample() {
boolean inStockOnly = inStockOnlySignal.get();

return allProducts.stream()
.filter(p -> category.equals("All")
|| p.category().equals(category))
.filter(p -> searchTerm.isEmpty()
|| p.name().toLowerCase().contains(searchTerm)
|| p.id().toLowerCase().contains(searchTerm))
.filter(p -> !inStockOnly || p.stock() > 0)
.toList();
.filter(p -> category.equals("All")
|| p.category().equals(category))
.filter(p -> searchTerm.isEmpty()
|| p.name().toLowerCase().contains(searchTerm)
|| p.id().toLowerCase().contains(searchTerm))
.filter(p -> !inStockOnly || p.stock() > 0).toList();
});

// Filter UI components
ComboBox<String> categoryFilter = new ComboBox<>("Category", List.of(
"All", "Electronics", "Clothing", "Books", "Home & Garden"));
categoryFilter.bindValue(categoryFilterSignal, categoryFilterSignal::set);
categoryFilter.bindValue(categoryFilterSignal,
categoryFilterSignal::set);

TextField searchField = new TextField("Search");
searchField.setPlaceholder("Search by name or ID");
Expand All @@ -58,7 +58,7 @@ public GridFilteringExample() {
productGrid.setColumns("id", "name", "category", "price", "stock");
Signal.effect(productGrid, () -> {
productGrid.setItems(Objects.requireNonNullElseGet(
filteredProductsSignal.get(), List::of));
filteredProductsSignal.get(), List::of));
});

add(categoryFilter, searchField, inStockCheckbox, productGrid);
Expand All @@ -67,8 +67,7 @@ public GridFilteringExample() {

private List<Product> loadProducts() {
// Returns sample product data
return List.of(
new Product("P001", "Laptop", "Electronics", 999.99, 15),
return List.of(new Product("P001", "Laptop", "Electronics", 999.99, 15),
new Product("P002", "T-Shirt", "Clothing", 19.99, 50),
new Product("P003", "Java Programming Book", "Books", 49.99, 0),
new Product("P004", "Garden Hose", "Home & Garden", 29.99, 30),
Expand Down
Loading