From d529d9867ff86f32b9d679fb532a24938e7adaff Mon Sep 17 00:00:00 2001
From: Erik Lumme <erik@vaadin.com>
Date: Wed, 13 Sep 2017 09:43:00 +0300
Subject: Migrate DynamicallyInjectingCSS

---
 .../articles/DynamicallyInjectingCSS.asciidoc      | 307 +++++++++++++++++++++
 1 file changed, 307 insertions(+)
 create mode 100644 documentation/articles/DynamicallyInjectingCSS.asciidoc

(limited to 'documentation/articles/DynamicallyInjectingCSS.asciidoc')

diff --git a/documentation/articles/DynamicallyInjectingCSS.asciidoc b/documentation/articles/DynamicallyInjectingCSS.asciidoc
new file mode 100644
index 0000000000..d379ec6c4d
--- /dev/null
+++ b/documentation/articles/DynamicallyInjectingCSS.asciidoc
@@ -0,0 +1,307 @@
+[[dynamically-injecting-css]]
+Dynamically injecting CSS
+-------------------------
+
+In most cases you will style your components using SASS or CSS and
+create a theme for the application which you include with the `@Theme`
+annotation. This is always the preferred way of theming your
+application. But in some cases this is not enough. Sometimes you will
+want your user to be able to change some property on-the-fly without
+providing pre-made class names. To do this you can use CSS style
+injection. In this example I am going to show you how you can use CSS
+injection to create an editor which you can use to modify text visually
+with, a WYSIWYG of sorts. Here is an image of the final component I am
+going to create:
+
+image:img/theme-editor.png[Theme editor]
+
+First lets start by defining the UI of the editor component, it looks
+like this:
+
+[source,java]
+....
+private Component createEditor(String text) {
+  Panel editor = new Panel("Text Editor");
+  editor.setWidth("580px");
+  VerticalLayout panelContent = new VerticalLayout();
+  panelContent.setSpacing(true);
+  panelContent.setMargin(new MarginInfo(true, false, false, false));
+  editor.setContent(panelContent);
+  // Create the toolbar
+  HorizontalLayout toolbar = new HorizontalLayout();
+  toolbar.setSpacing(true);
+  toolbar.setMargin(new MarginInfo(false, false, false, true));
+  // Create the font family selector
+  toolbar.addComponent(createFontSelect());
+  // Create the font size selector
+  toolbar.addComponent(createFontSizeSelect());
+  // Create the text color selector
+  toolbar.addComponent(createTextColorSelect());
+  // Create the background color selector
+  toolbar.addComponent(createBackgroundColorSelect());
+  panelContent.addComponent(toolbar);
+  panelContent.setComponentAlignment(toolbar, Alignment.MIDDLE_LEFT);
+  // Spacer between toolbar and text
+  panelContent.addComponent(new Label("<hr />", ContentMode.HTML));
+  // The text to edit
+  TextArea textLabel = new TextArea(null, text);
+  textLabel.setWidth("100%");
+  textLabel.setHeight("200px");
+  // IMPORTANT: We are here setting the style name of the label, we are going to use this in our injected styles to target the label
+  textLabel.setStyleName("text-label");
+  panelContent.addComponent(textLabel);
+  return editor;
+}
+....
+
+Basically the editor component is a Panel with a text area and some
+buttons which you can use to modify the text area text with. The
+important thing here is that we give the text area a style name
+"text-label". With this style name we will be able to inject CSS styles
+targeted at that text area and modify colors and fonts of it. Lets next
+take a look at how the controls in the toolbar is implemented. They are
+all pretty similar but lets first take a look at how the Font selector
+was made:
+
+[source,java]
+....
+private Component createFontSelect() {
+  final ComboBox select = new ComboBox(null,
+      Arrays.asList("Arial", "Helvetica", "Verdana", "Courier", "Times", "sans-serif"));
+  select.setValue("Arial");
+  select.setWidth("200px");
+  select.setInputPrompt("Font");
+  select.setDescription("Font");
+  select.setImmediate(true);
+  select.setNullSelectionAllowed(false);
+  select.setNewItemsAllowed(false);
+  select.addValueChangeListener(new ValueChangeListener() {
+    @Override
+    public void valueChange( ValueChangeEvent event ) {
+      // Get the new font family
+      String fontFamily = select.getValue().toString();
+      // Get the stylesheet of the page
+      Styles styles = Page.getCurrent().getStyles();
+      // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
+      styles.add(".v-app .v-textarea.text-label { font-family:" + fontFamily + "; }");
+    }
+  });
+  return select;
+}
+....
+
+The important part here is what is inside the `ValueChangeListener`. Once
+we get the value from the ComboBox we are ready to inject it to the page
+so the user can visually see what have changed. To do this we fetch the
+StyleSheet for the current Page by calling `Page.getCurrent()`. Once we
+have the current Page we can get its StyleSheet by calling
+`Page.getstyleSheet()`. Once we got the StyleSheet we are free to inject
+any CSS string into the page by using `StyleSheet.inject(String css)`. As
+you see here we use the style name we gave to the TextArea as the
+selector and apply the font-family attribute to change the font family
+to the one the user has selected. For the sake of clarity, lets look at
+how another one, the text color selector, was implemented:
+
+[source,java]
+....
+private Component createTextColorSelect( ) {
+  // Colorpicker for changing text color
+  ColorPicker textColor = new ColorPicker("Color", Color.BLACK);
+  textColor.setWidth("110px");
+  textColor.setCaption("Color");
+  textColor.addColorChangeListener(new ColorChangeListener() {
+    @Override
+    public void colorChanged( ColorChangeEvent event ) {
+      // Get the new text color
+      Color color = event.getColor();
+      // Get the stylesheet of the page
+      Styles styles = Page.getCurrent().getStyles();
+      // inject the new color as a style
+      styles.add(".v-app .v-textarea.text-label { color:" + color.getCSS() + "; }");
+    }
+  });
+  return textColor;
+}
+....
+
+Again, the important part in this method is in the `ColorChangeListener`.
+Basically here we do the exactly same thing as we did with the font
+family except here we are dealing with a color. To change the color of
+the text we just simply apply the 'color' attribute for text area. The
+`ColorPicker.Color` even provides us with a convenient method of directly
+converting the received Color object into a CSS color. And finally, for
+completeness, here is the full example code which will produce the demo
+application in the picture above for you to try out:
+
+[source,java]
+....
+/**
+ * Imports and package definition omitted
+ */
+public class CSSInjectWithColorpicker extends UI {
+  @Override
+  protected void init( VaadinRequest request ) { // Create a text editor
+    Component editor =
+        createEditor("Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+            + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+            + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+            + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+            + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."
+            + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+            + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+            + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+            + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+            + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."
+            + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+            + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+            + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+            + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+            + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."
+            + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+            + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+            + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+            + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+            + "quam, ac urna eros est cras id cras, eleifend eu mattis nec.");
+    VerticalLayout content = new VerticalLayout(editor);
+    content.setMargin(true);
+    setContent(content);
+  }
+
+  /**
+   * Creates a text editor for visually editing text
+   *
+   * @param text The text editor
+   * @return
+   */
+  private Component createEditor( String text ) {
+    Panel editor = new Panel("Text Editor");
+    editor.setWidth("580px");
+    VerticalLayout panelContent = new VerticalLayout();
+    panelContent.setSpacing(true);
+    panelContent.setMargin(new MarginInfo(true, false, false, false));
+    editor.setContent(panelContent);
+    // Create the toolbar
+    HorizontalLayout toolbar = new HorizontalLayout();
+    toolbar.setSpacing(true);
+    toolbar.setMargin(new MarginInfo(false, false, false, true));
+    // Create the font family selector
+    toolbar.addComponent(createFontSelect());
+    // Create the font size selector
+    toolbar.addComponent(createFontSizeSelect());
+    // Create the text color selector
+    toolbar.addComponent(createTextColorSelect());
+    // Create the background color selector
+    toolbar.addComponent(createBackgroundColorSelect());
+    panelContent.addComponent(toolbar);
+    panelContent.setComponentAlignment(toolbar, Alignment.MIDDLE_LEFT);
+    // Spacer between toolbar and text
+    panelContent.addComponent(new Label("<hr />", ContentMode.HTML));
+    // The text to edit
+    TextArea textLabel = new TextArea(null, text);
+    textLabel.setWidth("100%");
+    textLabel.setHeight("200px");
+    // IMPORTANT: We are here setting the style name of the label, we are going to use this in our injected styles to
+    // target the label
+    textLabel.setStyleName("text-label");
+    panelContent.addComponent(textLabel);
+    return editor;
+  }
+
+  /**
+   * Creates a background color select dialog
+   */
+  private Component createBackgroundColorSelect( ) {
+    ColorPicker bgColor = new ColorPicker("Background", Color.WHITE);
+    bgColor.setWidth("110px");
+    bgColor.setCaption("Background");
+    bgColor.addColorChangeListener(new ColorChangeListener() {
+      @Override
+      public void colorChanged( ColorChangeEvent event ) {
+        // Get the new background color
+        Color color = event.getColor();
+        // Get the stylesheet of the page
+        Styles styles = Page.getCurrent().getStyles();
+        // inject the new background color
+        styles.add(".v-app .v-textarea.text-label { background-color:" + color.getCSS() + "; }");
+      }
+    });
+    return bgColor;
+  }
+
+  /**
+   * Create a text color selection dialog
+   */
+  private Component createTextColorSelect( ) {
+    // Colorpicker for changing text color
+    ColorPicker textColor = new ColorPicker("Color", Color.BLACK);
+    textColor.setWidth("110px");
+    textColor.setCaption("Color");
+    textColor.addColorChangeListener(new ColorChangeListener() {
+
+      @Override
+      public void colorChanged( ColorChangeEvent event ) {
+        // Get the new text color
+        Color color = event.getColor();
+        // Get the stylesheet of the page
+        Styles styles = Page.getCurrent().getStyles();
+        // inject the new color as a style
+        styles.add(".v-app .v-textarea.text-label { color:" + color.getCSS() + "; }");
+      }
+    });
+    return textColor;
+  }
+
+  /**
+   * Creates a font family selection dialog
+   */
+  private Component createFontSelect( ) {
+    final ComboBox select =
+        new ComboBox(null, Arrays.asList("Arial", "Helvetica", "Verdana", "Courier", "Times", "sans-serif"));
+    select.setValue("Arial");
+    select.setWidth("200px");
+    select.setInputPrompt("Font");
+    select.setDescription("Font");
+    select.setImmediate(true);
+    select.setNullSelectionAllowed(false);
+    select.setNewItemsAllowed(false);
+    select.addValueChangeListener(new ValueChangeListener() {
+      @Override
+      public void valueChange( ValueChangeEvent event ) {
+        // Get the new font family
+        String fontFamily = select.getValue().toString();
+        // Get the stylesheet of the page
+        Styles styles = Page.getCurrent().getStyles();
+        // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
+        styles.add(".v-app .v-textarea.text-label { font-family:" + fontFamily + "; }");
+      }
+    });
+    return select;
+  }
+
+  /**
+   * Creates a font size selection control
+   */
+  private Component createFontSizeSelect( ) {
+    final ComboBox select = new ComboBox(null, Arrays.asList(8, 9, 10, 12, 14, 16, 20, 25, 30, 40, 50));
+    select.setWidth("100px");
+    select.setValue(12);
+    select.setInputPrompt("Font size");
+    select.setDescription("Font size");
+    select.setImmediate(true);
+    select.setNullSelectionAllowed(false);
+    select.setNewItemsAllowed(false);
+    select.addValueChangeListener(new ValueChangeListener() {
+      @Override
+      public void valueChange( ValueChangeEvent event ) {
+        // Get the new font size
+        Integer fontSize = (Integer) select.getValue();
+        // Get the stylesheet of the page
+        Styles styles = Page.getCurrent().getStyles();
+        // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
+        styles.add(".v-app .v-textarea.text-label { font-size:" + String.valueOf(fontSize) + "px; }");
+      }
+    });
+    return select;
+  }
+}
+....
-- 
cgit v1.2.3

