From 25d2874d0d9ec81905d8f0bcaf7dabfb4c749217 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:10:33 -0300 Subject: [PATCH 1/3] refactor: remove FunctionalInterface annotation --- .../flowingcode/vaadin/addons/demo/ThemeChangeObserver.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java b/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java index 9bd3a30..2cc5194 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java @@ -2,7 +2,7 @@ * #%L * Commons Demo * %% - * Copyright (C) 2020 - 2025 Flowing Code + * Copyright (C) 2020 - 2026 Flowing Code * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ * Any attached component implementing this interface will receive an event when a new theme is * applied. */ -@FunctionalInterface public interface ThemeChangeObserver { void onThemeChange(String themeName); From 5ccae6b1f30ac2d3846e4c20c0a2894662fb0bf6 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:20:54 -0300 Subject: [PATCH 2/3] feat: add ThemeChangeEvent --- .../vaadin/addons/demo/TabbedDemo.java | 16 ++++- .../vaadin/addons/demo/ThemeChangeEvent.java | 63 +++++++++++++++++++ .../addons/demo/ThemeChangeObserver.java | 24 ++++++- 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java b/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java index ddf9353..6505f93 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java @@ -139,7 +139,10 @@ public TabbedDemo() { themeSelect.setItems(DynamicTheme.values()); themeSelect.setValue(DynamicTheme.getCurrent()); themeSelect.setWidth("85px"); - themeSelect.addValueChangeListener(ev -> ev.getValue().apply(this)); + themeSelect.addValueChangeListener(ev -> { + ev.getValue().apply(this); + observeThemeChange(this); + }); footer.add(themeSelect); } @@ -466,8 +469,19 @@ public static void setColorScheme(Component component, @NonNull ColorScheme colo element.executeJs(script, theme); collectThemeChangeObservers(component).forEach(observer -> observer.onThemeChange(theme)); + observeThemeChange(component); } + private static void observeThemeChange(Component source) { + DynamicTheme dynamicTheme = null; + if (DynamicTheme.isFeatureSupported()) { + dynamicTheme = DynamicTheme.getCurrent(); + } + ThemeChangeEvent event = new ThemeChangeEvent(source, false, getColorScheme(), dynamicTheme); + collectThemeChangeObservers(source).forEach(observer -> observer.onThemeChange(event)); + } + + private static Stream collectThemeChangeObservers(Component c) { Stream children = c.getChildren().flatMap(child -> collectThemeChangeObservers(child)); diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java b/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java new file mode 100644 index 0000000..3cc9887 --- /dev/null +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeEvent.java @@ -0,0 +1,63 @@ +/*- + * #%L + * Commons Demo + * %% + * Copyright (C) 2020 - 2026 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.flowingcode.vaadin.addons.demo; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.ComponentEvent; +import java.util.Optional; +import lombok.Getter; + +/** + * Event fired when a theme change occurs in the application. It contains information about the new + * {@link ColorScheme} and {@link DynamicTheme}. + */ +@SuppressWarnings("serial") +public class ThemeChangeEvent extends ComponentEvent { + + @Getter + private final ColorScheme colorScheme; + + private final DynamicTheme dynamicTheme; + + /** + * Constructs a new {@code ThemeChangeEvent}. + * + * @param source the source component of the event + * @param fromClient true if the event originated from the client side, false otherwise + * @param colorScheme the new color scheme applied + * @param dynamicTheme the new dynamic theme applied (may be null) + */ + public ThemeChangeEvent(Component source, boolean fromClient, ColorScheme colorScheme, DynamicTheme dynamicTheme) { + super(source, fromClient); + this.colorScheme = colorScheme; + this.dynamicTheme = dynamicTheme; + } + + /** + * Returns the dynamic theme applied, if any. + * + * @return an {@code Optional} containing the dynamic theme, or empty if dynamic theming is not + * initialized. + */ + public Optional getDynamicTheme() { + return Optional.ofNullable(dynamicTheme); + } + +} diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java b/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java index 2cc5194..63fd92a 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/ThemeChangeObserver.java @@ -22,9 +22,31 @@ /** * Any attached component implementing this interface will receive an event when a new theme is * applied. + *

+ * Observers are notified twice to support backward compatibility: + * first via the deprecated + * {@link #onThemeChange(String) onThemeChange(String theme)} and then via the + * {@link #onThemeChange(ThemeChangeEvent) onThemeChange(ThemeChangeEvent)}. + *

*/ public interface ThemeChangeObserver { - void onThemeChange(String themeName); + /** + * Called when a theme change occurs. + * + * @param themeName the name of the new theme + * @deprecated Use {@link #onThemeChange(ThemeChangeEvent)} instead. + */ + @Deprecated(forRemoval = true, since = "5.3.0") + default void onThemeChange(String themeName) { + }; + + /** + * Called when a theme change occurs. + * + * @param event the theme change event + */ + default void onThemeChange(ThemeChangeEvent event) { + } } From 37f64d7f86ec0f08c4804974d6d1418c162f4b85 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:48:27 -0300 Subject: [PATCH 3/3] feat: add support for dynamic theme in iframes --- .../vaadin/addons/demo/DynamicTheme.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java b/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java index 92fdc47..0b21e7a 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java @@ -224,10 +224,19 @@ public void apply(HasElement component) { assertFeatureInitialized(); VaadinSession.getCurrent().setAttribute(DynamicTheme.class, this); + + String document; + if (component.getElement().getTag().equalsIgnoreCase("iframe")) { + document = "this.contentWindow.document"; + } else { + document = "document"; + } + component.getElement().executeJs(""" + const _document = %s; const applyTheme = () => { ["lumo/lumo.css", "aura/aura.css"].forEach(href=> { - let link = document.querySelector(`link[href='${href}']`); + let link = _document.querySelector(`link[href='${href}']`); if (!link) return; if (href === $0) { if (link.rel === 'preload') link.rel = 'stylesheet'; @@ -238,12 +247,12 @@ public void apply(HasElement component) { }); }; - if (document.startViewTransition) { - document.startViewTransition(applyTheme); + if (_document.startViewTransition) { + _document.startViewTransition(applyTheme); } else { applyTheme(); } - """, href); + """.formatted(document), href); } }