diff --git a/pom.xml b/pom.xml
index 64cc853..b20126d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
+ * 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..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(); @@ -94,8 +106,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(); @@ -125,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 extends Component> clazz, String label) { if (!clazz.isAnnotationPresent(Route.class)) { @@ -143,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; @@ -201,11 +215,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; @@ -216,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) { @@ -235,17 +249,17 @@ private static SourceUrlResolver getResolver() { private Optional+ * 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 +431,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