diff --git a/src/main/frontend/themes/sistema-mantenimiento/components/vaadin-text-field.css b/src/main/frontend/themes/sistema-mantenimiento/components/vaadin-text-field.css new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/mx/gob/jumapacelaya/ui/ConfiguracionView.java b/src/main/java/mx/gob/jumapacelaya/ui/ConfiguracionView.java index 32d2e02..8506f17 100644 --- a/src/main/java/mx/gob/jumapacelaya/ui/ConfiguracionView.java +++ b/src/main/java/mx/gob/jumapacelaya/ui/ConfiguracionView.java @@ -20,192 +20,168 @@ import java.util.List; @Route(value = "configuracion", layout = MainLayout.class) public class ConfiguracionView extends VerticalLayout { - private static final String THEME_SESSION_KEY = "userTheme"; private static final String THEME_LOCALSTORAGE_KEY = "appThemePreference"; + private boolean initializing = true; + private final List primaryTextColorOptions = List.of( - new ColorOptions("Color 1", "#A02142"), - new ColorOptions("Color 2", "#691B31"), - new ColorOptions("Color 3", "#BC955B"), - new ColorOptions("Color 4", "#DDC9A3"), - new ColorOptions("Color 5", "#6F7271") + new ColorOptions("Big Dip O´Ruby", "#A02142"), + new ColorOptions("Claret", "#691B31"), + new ColorOptions("Aztec Gold", "#BC955B"), + new ColorOptions("Lion", "#DDC9A3"), + new ColorOptions("Nickel", "#6F7271") ); private final List primaryColorOptions = List.of( - new ColorOptions("Color 1", "#A02142"), - new ColorOptions("Color 2", "#691B31"), - new ColorOptions("Color 3", "#BC955B"), - new ColorOptions("Color 4", "#DDC9A3"), - new ColorOptions("Color 5", "#6F7271") + new ColorOptions("Big Dip O´Ruby", "#A02142"), + new ColorOptions("Claret", "#691B31"), + new ColorOptions("Aztec Gold", "#BC955B"), + new ColorOptions("Lion", "#DDC9A3"), + new ColorOptions("Nickel", "#6F7271") ); - public ConfiguracionView() { setSpacing(true); + add(new H2("⚙\uFE0F Configuración del Sistema")); - loadCustomVariablesOnStartup(); + loadCssVariablesFromLocalStorage(); - // Selector de modo claro u obscuro RadioButtonGroup themeSelector = createThemeSelector(); + ComboBox primaryColor = createColorCombo( + "Color primario", + "--lumo-primary-color", + primaryColorOptions + ); - // Selector de colores de texto primarios - ComboBox primaryTextColorSelector = createPrimaryTextColorComboBox(); - primaryTextColorSelector.setWidth("250px"); - - // Selector del color primario - ComboBox primaryColorSelector = createPrimaryColorComboBox(); - primaryColorSelector.setWidth("250px"); - - HorizontalLayout layout1 = new HorizontalLayout(themeSelector); - layout1.setWidthFull(); + ComboBox primaryTextColor = createColorCombo( + "Color de textos", + "--lumo-primary-text-color", + primaryTextColorOptions + ); - HorizontalLayout layout2 = new HorizontalLayout(primaryColorSelector, primaryTextColorSelector); - layout2.setWidthFull(); + HorizontalLayout row1 = new HorizontalLayout(themeSelector); + HorizontalLayout row2 = new HorizontalLayout(primaryColor, primaryTextColor); - VerticalLayout layoutPadre = new VerticalLayout(layout1, layout2); + VerticalLayout layoutPadre = new VerticalLayout(row1, row2); layoutPadre.getStyle() - .set("border-radius", "10px") - .set("box-shadow", "0 4px 8px rgba(0, 0, 0, 0.2)"); + .set("border-radius", "10px") + .set("box-shadow", "0 4px 8px rgba(0, 0, 0, 0.2)"); + add(layoutPadre); + + UI.getCurrent().getPage().executeJs("return true;") + .then(Boolean.class, ok -> initializing = false); } - private void applyTheme(String themeAttribute) { - String js = """ - if ('%s' === 'dark') { - document.documentElement.setAttribute('theme', 'dark'); - localStorage.setItem('%s', 'dark'); - } else { - document.documentElement.removeAttribute('theme'); - localStorage.setItem('%s', 'light'); - } - """.formatted( - themeAttribute, - THEME_LOCALSTORAGE_KEY, - THEME_LOCALSTORAGE_KEY - ); + /* -------------------- THEME -------------------- */ - UI.getCurrent().getPage().executeJs(js); + private RadioButtonGroup createThemeSelector() { + RadioButtonGroup selector = new RadioButtonGroup<>("Modo de interfaz"); + selector.setItems("Claro", "Oscuro"); - VaadinSession.getCurrent().setAttribute( - THEME_SESSION_KEY, - themeAttribute.isEmpty() ? "light" : "dark" + UI.getCurrent().getPage().executeJs( + "return localStorage.getItem($0)", THEME_LOCALSTORAGE_KEY + ).then(String.class, theme -> + selector.setValue("dark".equals(theme) ? "Oscuro" : "Claro") ); - } - - private ComboBox createPrimaryColorComboBox() { - ComboBox selector = new ComboBox<>("Color Primario"); - selector.setItems(primaryColorOptions); - selector.setClearButtonVisible(false); - selector.setAllowCustomValue(false); - final String cssVariable = "--lumo-primary-color"; - - UI.getCurrent().getPage().executeJs(String.format("return localStorage.getItem('config:%s');", cssVariable)) - .then(String.class, savedHex -> { - ColorOptions savedOption = primaryTextColorOptions.stream() - .filter(opt -> opt.getHexValue().equalsIgnoreCase(savedHex)) - .findFirst() - .orElse(primaryTextColorOptions.get(0)); + selector.addValueChangeListener(e -> { + if (initializing) return; + + boolean dark = "Oscuro".equals(e.getValue()); + UI.getCurrent().getPage().executeJs( + dark + ? "document.documentElement.setAttribute('theme','dark')" + : "document.documentElement.removeAttribute('theme')" + ); + UI.getCurrent().getPage().executeJs( + "localStorage.setItem($0,$1)", + THEME_LOCALSTORAGE_KEY, + dark ? "dark" : "light" + ); + }); - selector.setValue(savedOption); - }); + return selector; + } - selector.addValueChangeListener(event -> { - ColorOptions selectedOption = event.getValue(); - if (selectedOption != null) { - applyCssVariable(cssVariable, selectedOption.getHexValue()); - } + /* -------------------- COLORS -------------------- */ + + private ComboBox createColorCombo( + String label, + String cssVariable, + List options + ) { + ComboBox combo = new ComboBox<>(label); + combo.setItems(options); + combo.setWidth("250px"); + combo.setAllowCustomValue(false); + + UI.getCurrent().getPage().executeJs( + "return localStorage.getItem($0)", + "config:" + cssVariable + ).then(String.class, saved -> { + options.stream() + .filter(o -> o.getHexValue().equalsIgnoreCase(saved)) + .findFirst() + .ifPresent(combo::setValue); }); - selector.setRenderer(new ComponentRenderer<>(colorOptions -> { - Div div = new Div(); - div.setText(colorOptions.getName()); - div.getStyle().set("display", "flex") - .set("align-items", "center"); - - Div colorSwatch = new Div(); - colorSwatch.getStyle().set("background-color", colorOptions.getHexValue()) - .set("width", "16px") - .set("height", "16px") - .set("border-radius", "50%") - .set("margin-right", "8px") - .set("border", "1px solid var(--lumo-border-color)"); + combo.addValueChangeListener(e -> { + if (initializing || e.getValue() == null) return; - div.addComponentAsFirst(colorSwatch); - return div; - })); + applyCssVariable(cssVariable, e.getValue().getHexValue()); + }); - return selector; + combo.setRenderer(colorRenderer()); + return combo; } - private ComboBox createPrimaryTextColorComboBox() { - ComboBox selector = new ComboBox<>("Color de los textos"); - selector.setItems(primaryTextColorOptions); - selector.setClearButtonVisible(false); - selector.setAllowCustomValue(false); - - final String cssVariable = "--lumo-primary-text-color"; - - UI.getCurrent().getPage().executeJs(String.format("return localStorage.getItem('config:%s');", cssVariable)) - .then(String.class, savedHex -> { - ColorOptions savedOption = primaryTextColorOptions.stream() - .filter(opt -> opt.getHexValue().equalsIgnoreCase(savedHex)) - .findFirst() - .orElse(primaryTextColorOptions.get(0)); + private void applyCssVariable(String variable, String value) { + UI.getCurrent().getPage().executeJs( + """ + document.documentElement.style.setProperty($0,$1); + localStorage.setItem($2,$1); + """, + variable, + value, + "config:" + variable + ); + } - selector.setValue(savedOption); + private void loadCssVariablesFromLocalStorage() { + UI.getCurrent().getPage().executeJs( + """ + ['--lumo-primary-color','--lumo-primary-text-color'].forEach(v=>{ + const val = localStorage.getItem('config:'+v); + if(val) document.documentElement.style.setProperty(v,val); }); + """ + ); + } - selector.addValueChangeListener(event -> { - ColorOptions selectorOption = event.getValue(); - if (selectorOption != null) { - applyCssVariable(cssVariable, selectorOption.getHexValue()); - } - }); + /* -------------------- RENDERER -------------------- */ - selector.setRenderer(new ComponentRenderer<>(colorOptions -> { - Div div = new Div(); - div.setText(colorOptions.getName()); - div.getStyle().set("display", "flex") - .set("align-items", "center"); + private ComponentRenderer colorRenderer() { + return new ComponentRenderer<>(opt -> { + Div wrapper = new Div(); + wrapper.getStyle().set("display","flex").set("align-items","center"); - Div colorSwatch = new Div(); - colorSwatch.getStyle().set("background-color", colorOptions.getHexValue()) - .set("width","15px") - .set("height","15px") + Div dot = new Div(); + dot.getStyle() + .set("width","14px") + .set("height","14px") .set("border-radius","50%") - .set("margin-right","10px") + .set("margin-right","8px") + .set("background-color", opt.getHexValue()) .set("border","1px solid var(--lumo-border-color)"); - div.addComponentAsFirst(colorSwatch); - return div; - })); - - return selector; - } - - private RadioButtonGroup createThemeSelector() { - RadioButtonGroup themeSelector = new RadioButtonGroup<>("Modo de interfaz (Tema)"); - themeSelector.setItems("Claro (predeterminado)", "Oscuro"); - - String currentTheme = (String) VaadinSession.getCurrent().getAttribute(THEME_SESSION_KEY); - themeSelector.setValue( - "dark".equals(currentTheme) - ? "Oscuro" - : "Claro (predeterminado)" - ); - - themeSelector.addValueChangeListener(e -> { - String selection = e.getValue(); - String themeAttribute = selection.contains("Oscuro") ? "dark" : ""; - - applyTheme(themeAttribute); + wrapper.add(dot); + wrapper.add(opt.getName()); + return wrapper; }); - - return themeSelector; } - + /* -------------------- MODEL -------------------- */ public static class ColorOptions { private final String name; @@ -220,46 +196,8 @@ public class ConfiguracionView extends VerticalLayout { public String getHexValue() { return hexValue; } @Override - public String toString() { return name; } - } - - private void applyCssVariable(String variableName, String colorValue) { - String jsSetVar = String.format( - "document.documentElement.style.setProperty('%s', '%s');", - variableName, - colorValue - ); - - String jsSetStorage = String.format( - "localStorage.setItem('config:%s', '%s');", - variableName, - colorValue - ); - - UI.getCurrent().getPage().executeJs(jsSetVar, jsSetStorage); - VaadinSession.getCurrent().setAttribute(variableName, colorValue); - } - - private void loadCustomVariablesOnStartup() { - final String[] customVariables = { - "--lumo-primary-text-color", - "--lumo-primary-color" - }; - - StringBuilder jsLoadScript = new StringBuilder(); - - for (String cssVariable : customVariables) { - String storageKey = "config:" + cssVariable; - - jsLoadScript.append(String.format( - "const saved_%1$s = localStorage.getItem('%2$s');" + - "if (saved_%1$s) { document.documentElement.style.setProperty('%3$s', saved_%1$s); }", - cssVariable.replace("-", "_"), - storageKey, - cssVariable - )); + public String toString() { + return name; } - - UI.getCurrent().getPage().executeJs(jsLoadScript.toString()); } }