From d0c1a76d4ec4e9845a82fcd31facd29d0d9e532e Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:14:44 -0300 Subject: [PATCH 1/3] build: set version to 5.2.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 64cc853..b20126d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.flowingcode.vaadin.addons.demo commons-demo - 5.1.1-SNAPSHOT + 5.2.0-SNAPSHOT Commons Demo Common classes for add-ons demo From 710b1e862edde2cba96ccb7f8e7af34378222000 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:38:07 -0300 Subject: [PATCH 2/3] feat: deprecate get/applyThemeAttribute in favor of get/setColorScheme --- .../vaadin/addons/demo/ColorScheme.java | 59 +++++++++++++++ .../vaadin/addons/demo/TabbedDemo.java | 74 +++++++++++++++---- 2 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java b/src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java new file mode 100644 index 0000000..b8d67c6 --- /dev/null +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/ColorScheme.java @@ -0,0 +1,59 @@ +/*- + * #%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 lombok.Getter; +/* + * Copyright 2000-2025 Vaadin Ltd. + * + * 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. + */ +import lombok.RequiredArgsConstructor; + +/** + * Enumeration of supported color scheme values. + *

+ * These values correspond to the CSS color-scheme property values and control how the browser + * renders UI elements and how the application responds to system color scheme preferences. + */ +@RequiredArgsConstructor +public enum ColorScheme { + /** + * Light color scheme. The application will use a light theme regardless of system preferences. + */ + LIGHT("light"), + + /** + * Dark color scheme. The application will use a dark theme regardless of system preferences. + */ + DARK("dark"); + + @Getter + private final String value; + +} 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 311bd32..97ce88e 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java @@ -94,8 +94,7 @@ public TabbedDemo() { themeCB.addClassName("smallcheckbox"); themeCB.addValueChangeListener(cb -> { boolean useDarkTheme = themeCB.getValue(); - String theme = useDarkTheme ? "dark" : ""; - applyThemeAttribute(getElement(), theme); + setColorScheme(this, useDarkTheme ? ColorScheme.DARK : ColorScheme.LIGHT); }); footer = new HorizontalLayout(); footer.setWidthFull(); @@ -201,11 +200,11 @@ public void showRouterLayoutContent(HasElement content) { demo.getElement().getStyle().set("height", "100%"); setupDemoHelperButton(content.getClass()); } - + updateFooterButtonsVisibility(); getElement().insertChild(1, content.getElement()); - applyThemeAttribute(getElement(), getThemeAttribute()); + setColorScheme(this, getColorScheme()); } private static SourceUrlResolver resolver = null; @@ -241,11 +240,11 @@ private Optional createSourceCodeTab(Class annotatedClass, Dem SourceCodeTab.SourceCodeTabBuilder builder = SourceCodeTab.builder(); builder.url(url); - + if (!annotation.caption().equals(DemoSource.DEFAULT_VALUE)) { builder.caption(annotation.caption()); } - + if (!annotation.language().equals(DemoSource.DEFAULT_VALUE)) { builder.language(annotation.language()); } @@ -255,10 +254,10 @@ private Optional createSourceCodeTab(Class annotatedClass, Dem } builder.sourcePosition(annotation.sourcePosition()); - + return Optional.of(builder.build()); } - + public static String lookupGithubBranch(Class clazz) { GithubBranch branch = clazz.getAnnotation(GithubBranch.class); if (branch == null) { @@ -323,16 +322,62 @@ public void setOrientation(Orientation orientation) { orientationCB.setValue(Orientation.HORIZONTAL.equals(orientation)); } - private static final String THEME_NAME = TabbedDemo.class.getName() + "#THEME_NAME"; - + /** + * Returns the theme attribute value. + *

+ * The "theme attribute" is either an empty string (light) or "dark". + *

+ * + * @deprecated Use {@link #getColorScheme()} + * @return the theme attribute value + */ + @Deprecated public static String getThemeAttribute() { - return (String) Optional.ofNullable(VaadinSession.getCurrent().getAttribute(THEME_NAME)) - .orElse(""); + ColorScheme scheme = getColorScheme(); + return scheme == ColorScheme.LIGHT ? "" : scheme.getValue(); } + /** + * Returns the current color scheme. + * + * @return the current color scheme + */ + public static ColorScheme getColorScheme() { + return Optional.ofNullable(VaadinSession.getCurrent().getAttribute(ColorScheme.class)) + .orElse(ColorScheme.LIGHT); + } + + /** + * Applies the theme attribute to the given element. + *

+ * The "theme attribute" is either an empty string (light) or "dark". + *

+ * + * @param element the element to apply the theme to + * @param theme the theme attribute value + * @deprecated Use {@link #setColorScheme(Component, ColorScheme)} + */ + @Deprecated public static void applyThemeAttribute(Element element, String theme) { - VaadinSession.getCurrent().setAttribute(THEME_NAME, theme); + Component c = element.getComponent().get(); + if (theme.isEmpty()) { + setColorScheme(c, ColorScheme.LIGHT); + } else if (theme.equals("dark")) { + setColorScheme(c, ColorScheme.DARK); + } + } + + /** + * Sets the color scheme for the given component. + * + * @param component the component to apply the color scheme to + * @param colorScheme the color scheme to apply + */ + public static void setColorScheme(Component component, @NonNull ColorScheme colorScheme) { + VaadinSession.getCurrent().setAttribute(ColorScheme.class, colorScheme); + String theme = colorScheme.getValue(); + Element element = component.getElement(); String script; if (element.getTag().equalsIgnoreCase("iframe")) { script = "let e = this.contentWindow.document.documentElement;"; @@ -347,8 +392,7 @@ public static void applyThemeAttribute(Element element, String theme) { element.executeJs(script, theme); - Component c = element.getComponent().get(); - collectThemeChangeObservers(c).forEach(observer -> observer.onThemeChange(theme)); + collectThemeChangeObservers(component).forEach(observer -> observer.onThemeChange(theme)); } private static Stream collectThemeChangeObservers(Component c) { From 5e84c0e231a1dc3090b61fd9d14071079989a78d Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:55:31 -0300 Subject: [PATCH 3/3] docs: improve TabbedDemo javadoc --- .../vaadin/addons/demo/TabbedDemo.java | 57 +++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) 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 97ce88e..1deeae7 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/TabbedDemo.java @@ -48,6 +48,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * A layout for displaying a tabbed demo with source code integration. + *

+ * This layout consists of a set of tabs for navigating between different demos, + * a content area for displaying the current demo, and a footer with controls + * for + * toggling the source code visibility, orientation, and theme. + *

+ */ @StyleSheet("context://frontend/styles/commons-demo/shared-styles.css") @SuppressWarnings("serial") public class TabbedDemo extends VerticalLayout implements RouterLayout { @@ -67,6 +76,9 @@ public class TabbedDemo extends VerticalLayout implements RouterLayout { private Button helperButton; private DemoHelperViewer demoHelperViewer; + /** + * Constructs a new TabbedDemo instance. + */ public TabbedDemo() { demoHelperViewer = new DialogDemoHelperViewer(); @@ -124,6 +136,7 @@ public TabbedDemo() { * * @param clazz the class of routed demo view component * @param label the demo name (tab label) + * @throws IllegalArgumentException if the class is not annotated with {@link Route} */ public void addDemo(Class clazz, String label) { if (!clazz.isAnnotationPresent(Route.class)) { @@ -142,6 +155,8 @@ private void updateVisibility() { /** * Sets the autovisibility mode. When autovisibility is enabled, the tabs component is hidden * unless it contains two or more tabs. + * + * @param autoVisibility {@code true} to enable autovisibility, {@code false} to disable it */ public void setAutoVisibility(boolean autoVisibility) { this.autoVisibility = autoVisibility; @@ -215,7 +230,7 @@ public void showRouterLayoutContent(HasElement content) { * * @param resolver The {@code SourceUrlResolver} to be used. Must not be {@code null}. * @throws IllegalStateException if a resolver has already been set. - * @throws NullPointerException if the provided {@code resolver} is {@code null}. + * @throws NullPointerException if the provided {@code resolver} is {@code null}. */ public static void configureSourceUrlResolver(@NonNull SourceUrlResolver resolver) { if (TabbedDemo.resolver != null) { @@ -234,7 +249,7 @@ private static SourceUrlResolver getResolver() { private Optional createSourceCodeTab(Class annotatedClass, DemoSource annotation) { String url = getResolver().resolveURL(this, annotatedClass, annotation).orElse(null); - if (url==null) { + if (url == null) { return Optional.empty(); } @@ -258,11 +273,17 @@ private Optional createSourceCodeTab(Class annotatedClass, Dem return Optional.of(builder.build()); } + /** + * Looks up the GitHub branch name associated with the given TabbedDemo class. + * + * @param clazz the TabbedDemo class to inspect + * @return the GitHub branch name, or "master" if the annotation is not found + */ public static String lookupGithubBranch(Class clazz) { GithubBranch branch = clazz.getAnnotation(GithubBranch.class); if (branch == null) { Package pkg = clazz.getPackage(); - if (pkg!=null) { + if (pkg != null) { branch = pkg.getAnnotation(GithubBranch.class); } } @@ -286,11 +307,19 @@ private void updateSplitterPosition() { } } + /** + * Sets the visibility of the source code. + * + * @param visible {@code true} to make the source code visible, {@code false} otherwise + */ public void setSourceVisible(boolean visible) { codeCB.setValue(visible); codePositionCB.setVisible(visible); } + /** + * Toggles the position of the source code relative to the demo content. + */ public void toggleSourcePosition() { if (currentLayout != null) { currentLayout.toggleSourcePosition(); @@ -309,10 +338,20 @@ private void toggleSplitterOrientation() { setOrientation(splitOrientation); } + /** + * Returns the current orientation of the split layout. + * + * @return the current orientation + */ public Orientation getOrientation() { return currentLayout.getOrientation(); } + /** + * Sets the orientation of the split layout. + * + * @param orientation the new orientation + */ public void setOrientation(Orientation orientation) { splitOrientation = orientation; if (currentLayout != null) { @@ -409,6 +448,11 @@ private void updateFooterButtonsVisibility() { codePositionCB.setVisible(hasSourceCode); } + /** + * Adds a listener for {@link TabbedDemoSourceEvent}. + * + * @param listener the listener to add + */ public void addTabbedDemoSourceListener(ComponentEventListener listener) { ComponentUtil.addListener(this, TabbedDemoSourceEvent.class, listener); listener.onComponentEvent(new TabbedDemoSourceEvent(this, currentLayout != null)); @@ -437,6 +481,11 @@ private void adjustSplitOrientation(boolean portraitOrientation) { setOrientation(splitOrientation); } + /** + * Sets the {@link DemoHelperViewer} to be used for displaying demo helpers. + * + * @param demoHelperViewer the viewer to set + */ public void setDemoHelperViewer(DemoHelperViewer demoHelperViewer) { this.demoHelperViewer = Objects.requireNonNull(demoHelperViewer, "Demo helper viewer cannot be null"); @@ -455,7 +504,7 @@ private void setupDemoHelperButton(Class helperClass) { .addClickListener(e -> demoHelperViewer.show(demoHelperRenderer.helperContent())); add(helperButton); } catch (Exception e) { - logger.error("Error creating an instance",e); + logger.error("Error creating an instance", e); } } }