CSSInject add-on

Hi all,

I just took a 15 min break from regular work and packaged the CSSInject component as an proper add-on. It’s the same component I’ve used in the Chameleon Theme color editor to dynamically change the colors as you use the color pickers.

So here it is, for your viewing pleasure:

CSSInject add-on

Disclaimer: not tested (at all, yet), so please report any issues here (or in the Directory comments, whatever).

Hello!

How to use this addon with a custom component, in particular DragAndDropWrapper?
After a call to addComponent, I get an exception:

java.lang.UnsupportedOperationException at com.vaadin.ui.CustomComponent.addComponent(CustomComponent.java:208) :blink:

CustomComponent doesn’t support addComponent, hence the exception. That’s just plain Vaadin stuff, not related to CSSInject.

You use the CSSInject as any other “non-visible” component: place it in some layout. It doesn’t really matter where, but I prefer the main layout, as early in the component hierarchy as possible, so the styles are updated before any other component rendering is done (they migth affect size calculations for example).

If I am guessing your need from your post, you wish to alter the DragAndDropWrapper’s style in some way. You then need to write custom CSS that will be injected to the client using CSSInject, that will target the wrapper element, e.g.

String cssText = ".v-ddwrapper { background-color: red; }";
cssInject.setValue(cssText);

CSSInject does not provide any helpers for targeting individual components, it’s just plain CSS that will be added to the window you add the CSSInject component to.

Hi Jouni

Thank you very much for this tiny usefull Widget. While testing I found a bug which can be corrected with following patch:


@@ -8,6 +8,7 @@
 import com.vaadin.terminal.gwt.client.UIDL;
 
 public class VCSSInject extends Widget implements Paintable {
+  private UIDL m_uidl = null;
 
   public VCSSInject() {
     setElement(DOM.createSpan());
@@ -17,8 +18,11 @@
   }
 
   @Override
-  public void updateFromUIDL(final UIDL uidl, final ApplicationConnection client) {
-    setStyles(uidl.getStringAttribute("styles"), uidl.getId());
+  public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+    m_uidl = uidl;
+    if (m_uidl != null) {
+      setStyles(m_uidl.getStringAttribute("styles"), m_uidl.getId());
+    }
   }
 
   private native void setStyles(String value, String id)
@@ -46,10 +50,12 @@
   @Override
   public void onDetach() {
     super.onDetach();
-    removeStyleElement();
+    if (m_uidl != null) {
+      removeStyleElement(m_uidl.getId());
+    }
   }
 
-  private native void removeStyleElement()
+  private native void removeStyleElement(String id)
   /*-{
       var el;
       var elid = "vstyles-" + id;

Without this patch following Exception happens when removing the CSSInject-Component: JavaScriptException: (ReferenceError): id is not defined.

Greetings
Stephan

Hi Stephan,

Thanks for the comments and the patch. I’ll update the add-on as soon as I find some Community Friday time.

Hi,

I now updated the add-on, with a slightly modified patch. It’s uploaded to the Directory.

Hi Jouni,

Great idea for a widget. I’m having a problem getting it running. Since I am a Vaadin newbie, maybe I am missing something. I included the 0.1 JAR file in the library path, and got it integrated into my code the way I want. When I run my app, at the very top of the UI in the browser a message is output:



Widgetset does not contain implementation for org.vaadin.cssinject.CSSInject. Check its @ClientWidget mapping, widgetsets GWT module description file and re-compile your widgetset. In case you have downloaded a vaadin add-on package, you might want to refer to add-on instructions. Unrendered UIDL:
org.vaadin.cssinject.CSSInject(NO CLIENT IMPLEMENTATION FOUND)

Not sure what this means. Please comment?

Thanks,

Matt

Matt, did you remember to recompile your widgetset?

http://vaadin.com/book/-/page/gwt.eclipse.html#gwt.eclipse.compiling

Well, that’s the problem. I cannot tell if this CSS add-on is pre-compiled or requires it beforehand. It is not clear. I looked through the build-widgetset.xml script for Ant, and it would appear that the cssinject-*.jar was in fact generated by the Ant script already. Am I supposed to recompile an add-on, or is it missing something?

-Matt

Once the JAR is in your classpath, simply running the Eclipse widgetset compiler or the Ant compiler should be enough. Be sure to enable verbose logging so you see what’s going on during the compilation. Are there any errors during the compilation?

Do you think it would make sense to have dynamic CSS file loading also as a part of this widget?

I’ve implemented this kind of mechanism in the
AboutBox
to switch the whole CSS themes on-the-fly. The server-side API could be something very simple like:

css.loadFile(String id, Resource cssLink)

and here is an sample JSNI implementation:

private native void loadCss(String url, String media, String id) /*-{

     var links = $doc.getElementsByTagName("link");
     var es = null;
     if (links.length > 0) {
         for (var i = 0; i<links.length; i++) {
             if (links[i]
["id"]
 == id) {
                if (links[i]
["href"]
 == url) {
                    return;
                } else {
                    es = links[i]
;
                    break;
                }
            }
         }
     }

     if (es == null) {
         es = $doc.createElement("link");
     }
     es.setAttribute("id", id);
     es.setAttribute("rel", "stylesheet");
     es.setAttribute("type", "text/css");
     es.setAttribute("media", media);
     es.setAttribute("href", url);

     var head = $doc.getElementsByTagName("head")[0]
;
     head.appendChild(es);
 }-*/;

If the server-side API is based on Resource, one could also generate the CSS data on the fly in the application.

Sami: sounds like a good idea. Since the code is now
available in GitHub
, I urge you to make a patch an send me a pull request. I’d be happy to include it.

If not, then I ask you to at least create a new enhancement issue in GitHub so the feature will be documented. Thanks!

Hey everyone!

I made a new version with the Resource functionality. So you can now do this:

CSSInject css = new CSSInject();
css.addStyleSheet(new ThemeResource("views/login-view.css"));

You can also use ClassResource and all the other possibilities that the Resource interface allows you to do.

With this you can now package your server-side-only add-on component’s styles more easily, I would argue.

It also makes it handy to include some view-specific styles in your application, so you’re not forcing the user to download all the CSS at init, but only when it is needed. But be careful, since the CSS might affect the layout calculations, and I’m not totally sure how the order of execution goes when adding new stylesheets on the fly during JavaScript execution (i.e. which gets done first: the loading of CSS or the rest of the JS execution).

Available in the Directory as v.1.0. Enjoy!

How would i use CSSInject to style a single table row, without needing to call refreshRowCache() or refreshRenderedCells().
Actually the problem is getting a handle on the table row and adding style to it.

I’m having a very weird problem here, probably not specific to this addon.
If I try to get part of my class string from XML, CSS inject fails.

This works:

        	cssText = ".myclass { background-color: #ff0000; }";

This also works:

                String v1 = "myclass";
        	cssText = "." + v1 + " { background-color: #ff0000; }";

This does not work:

                String v1 = xmldata.getAttributes().getNamedItem("id").getNodeValue().toString();
        	cssText = "." + v1 + " { background-color: #ff0000; }";

Printing cssText to the console always gives the same result:

.myclass { background-color: #ff0000; }

What could I be missing?

Looks like a great add-on, but how do I prevent that it has a visual size? I added it like this:

	Window mainWindow = new Window("My Application");
	mainWindow.setSizeFull();
	mainWindow.getContent().setSizeFull();
	
	CSSInject css = new CSSInject();
	css.setValue(".custom-style { color: rgb(100, 200, 300); }");
	mainWindow.getContent().addComponent(css);

Now it occupies half the screen (the other half is for the next component I add). Perhaps this is not really a CSSInject question/issue, but I’m a bit confused.

Thanks

Jan

Nobody seems to have replied, so: there is an implicit VerticalLayout in your window and the CSSInject gets expand ratio 1 by default, which means extra space is divided evenly between its slot in the layout and other components.

((VerticalLayout) mainWindow.getContent()).setExpandRatio(css, 0); or something like that should help. You could also explicitly create the main layout and do mainWindow.setContent(…) to avoid the cast.

Perfect thanks, exactly what I needed.

I already knew about the expand ratio but I didn’t think about this way.

BTW: I love Vaadin and I find it’s a breeze to use: The only thing I struggle with a bit is the layout stuff. On the other hand it’s also difficult in most other frameworks like Swing, SWT etc.

Jan

What am I doing wrong?
Widgetset does not contain implementation for org.vaadin.cssinject.CSSInject. Check its component connector’s @Connect mapping, widgetsets GWT module description file and re-compile your widgetset. In case you have downloaded a vaadin add-on package, you might want to refer to add-on instructions.

Did you:

  • add a custom client-side widgetset to your project
  • If you have one already, did you properly compile it.
  • If you did that and added the add-on afterwards you have to recompile the widgetset.

Btw. CSSInject is, as far as i know, already in Vaadin 7 in case you’re it.