I am developping a JSF custom component performing "responsive" multi-columns display.
It uses the "CSS Multi-column Layout Module" ( http://www.w3.org/TR/css3-multicol/ ) (tutorial in French : http://www.alsacreations.com/tuto/lire/1557-les-multicolonnes-en-css3.html ).
I actually have the following solution, which works :
Component class :
@Log4j
@FacesComponent(ResponsiveMultiColumnsTable.SENAT_COMPONENT_TYPE)
@ResourceDependency(library="senat",name="senat-columns.css")
public class ResponsiveMultiColumnsTable extends UIData {
public static final String SENAT_COMPONENT_FAMILY = "fr.senat.faces.components";
public static final String SENAT_COMPONENT_TYPE = SENAT_COMPONENT_FAMILY + ".ResponsiveMultiColumnsTable";
public static final String DEFAULT_RENDERER = SENAT_COMPONENT_FAMILY + ".ResponsiveMultiColumnsPanelRenderer";
private enum PropertyKeys { style, styleClass }
public ResponsiveMultiColumnsTable() {
setRendererType(DEFAULT_RENDERER);
}
@Override
public boolean getRendersChildren()
{
return true;
}
public String getStyle() {
return (String) getStateHelper().eval(PropertyKeys.style);
}
public void setStyle(String param) {
getStateHelper().put(PropertyKeys.style, param);
}
public String getStyleClass() {
return (String) getStateHelper().eval(PropertyKeys.styleClass);
}
public void setStyleClass(String param) {
getStateHelper().put(PropertyKeys.styleClass, param);
}
}
Renderer class :
@FacesRenderer(componentFamily = ResponsiveMultiColumnsTable.COMPONENT_FAMILY, rendererType = ResponsiveMultiColumnsTable.DEFAULT_RENDERER)
public class ResponsiveMultiColumnsTableRenderer extends CoreRenderer {
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
ResponsiveMultiColumnsTable detail = (ResponsiveMultiColumnsTable) component;
ResponseWriter writer = context.getResponseWriter();
String clientId = detail.getClientId(context);
String style = detail.getStyle();
String styleClass = detail.getStyleClass();
writer.startElement("div", detail);
writer.writeAttribute("id", clientId, "id");
String extStyleClass = "senat-details-columns";
if (styleClass != null) {
extStyleClass += " " + styleClass;
}
writer.writeAttribute("class", "senat-details-columns " + extStyleClass, "styleClass");
if ((style != null) && !style.isEmpty()) {
writer.writeAttribute("style", style, "style");
}
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.endElement("div");
}
@Override
public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
ResponsiveMultiColumnsTable grid = (ResponsiveMultiColumnsTable) component;
int rowIndex = grid.getFirst();
int rows = grid.getRows();
int itemsToRender = rows != 0 ? rows : grid.getRowCount();
for(int i = rowIndex ; i < itemsToRender ; i++) {
grid.setRowIndex(i);
if(!grid.isRowAvailable()) {
break;
}
writer.startElement("div", null);
renderChildren(context, grid);
writer.endElement("div");
}
}
}
Component declaration in taglib :
<tag>
<description>
<![CDATA[
*** À compléter ***
]]>
</description>
<tag-name>responsiveMultiColumnsTable</tag-name>
<component>
<component-type>fr.senat.faces.components.ResponsiveMultiColumnsTable</component-type>
</component>
<attribute>
<description>Liste à afficher
</description>
<name>value</name>
<required>true</required>
<type>java.util.List</type>
</attribute>
<attribute>
<description>Nom de la variable d'itération</description>
<name>var</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description>Élément de style à ajouter</description>
<name>style</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description>Classe à ajouter</description>
<name>styleClass</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
</tag>
Stylesheet :
.senat-details-columns {
-webkit-columns: 3;
-moz-columns: 3;
columns: 3;
}
@media (max-width: 1024px) {
.senat-details-columns {
-webkit-columns: 2;
-moz-columns: 2;
columns: 2;
}
}
@media (max-width: 640px) {
.senat-details-columns {
-webkit-columns: 1;
-moz-columns: 1;
columns: 1;
}
}
I would like to be able to indicate data such as which column count to use at which resolution. One way to do that would be to dynamically generate a stylesheet fragment then include it.
The base dynamic stylesheet would look like :
.#{dynamic-style} {
-webkit-columns: #{max-col-count};
-moz-columns: #{max-col-count};
columns: #{max-col-count};
}
@media (max-width: #{max-middle-res}px) {
.#{dynamic-style} {
-webkit-columns: #{middle-col-count};
-moz-columns: #{middle-col-count};
columns: #{middle-col-count};
}
}
@media (max-width: #{max-min-res}px) {
.#{dynamic-style} {
-webkit-columns: #{min-col-count};
-moz-columns: #{middle-col-count};
columns: #{middle-col-count};
}
}
How can I dynamically process it and include it ?
The problem I see is that I can have multiple uses of the same stylesheet with different parameters. If there was only one use, just including EL expressions in the stylesheet could do the job.
Another solution would be to append style just before the component, but it seems a bit ugly to me.