|
|
|
@ -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<ColorOptions> 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<ColorOptions> 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<String> themeSelector = createThemeSelector(); |
|
|
|
ComboBox<ColorOptions> primaryColor = createColorCombo( |
|
|
|
"Color primario", |
|
|
|
"--lumo-primary-color", |
|
|
|
primaryColorOptions |
|
|
|
); |
|
|
|
|
|
|
|
// Selector de colores de texto primarios |
|
|
|
ComboBox<ColorOptions> primaryTextColorSelector = createPrimaryTextColorComboBox(); |
|
|
|
primaryTextColorSelector.setWidth("250px"); |
|
|
|
|
|
|
|
// Selector del color primario |
|
|
|
ComboBox<ColorOptions> primaryColorSelector = createPrimaryColorComboBox(); |
|
|
|
primaryColorSelector.setWidth("250px"); |
|
|
|
|
|
|
|
HorizontalLayout layout1 = new HorizontalLayout(themeSelector); |
|
|
|
layout1.setWidthFull(); |
|
|
|
ComboBox<ColorOptions> 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<String> createThemeSelector() { |
|
|
|
RadioButtonGroup<String> 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<ColorOptions> createPrimaryColorComboBox() { |
|
|
|
ComboBox<ColorOptions> 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<ColorOptions> createColorCombo( |
|
|
|
String label, |
|
|
|
String cssVariable, |
|
|
|
List<ColorOptions> options |
|
|
|
) { |
|
|
|
ComboBox<ColorOptions> 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<ColorOptions> createPrimaryTextColorComboBox() { |
|
|
|
ComboBox<ColorOptions> 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<Div, ColorOptions> 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<String> createThemeSelector() { |
|
|
|
RadioButtonGroup<String> 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()); |
|
|
|
} |
|
|
|
} |