package org.rapidoid.gui; import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; import org.rapidoid.collection.Coll; import org.rapidoid.gui.base.AbstractWidget; import org.rapidoid.html.Tag; import org.rapidoid.html.tag.TdTag; import org.rapidoid.lambda.Lmbd; import org.rapidoid.lambda.Mapper; import org.rapidoid.model.Item; import org.rapidoid.model.Items; import org.rapidoid.model.Models; import org.rapidoid.model.Property; import org.rapidoid.u.U; import org.rapidoid.util.Msc; import org.rapidoid.var.Var; import org.rapidoid.wrap.BoolWrap; import java.util.Iterator; import java.util.List; /* * #%L * rapidoid-gui * %% * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ @Authors("Nikolche Mihajlovski") @Since("2.0.0") public class Grid extends AbstractWidget<Grid> { private volatile Iterable<?> items; private volatile String orderBy; private volatile int pageSize = 10; private volatile String[] columns = {}; private volatile Object[] headers = {}; private volatile String rowCmd; private volatile String highlightRegex; private volatile Mapper<Object, String> toUri; @Override protected Object render() { Pager pager = noPager(); boolean paging = pageSize > 0; Iterable<?> rows; BoolWrap isLastPage = new BoolWrap(); if (paging) { String pageParam = "_p" + seq("pager"); pager = GUI.pager(pageParam).min(1); Integer size = Coll.getSizeOrNull(items); if (size != null) { int pages = (int) Math.ceil(size / (double) pageSize); pager.max(pages); } rows = Msc.getPage(items, pager.pageNumber(), pageSize, size, isLastPage); } else { rows = items; } return renderGridPage(pager, rows, isLastPage.value); } private Object renderGridPage(Pager pager, Iterable<?> rows, boolean isLastPage) { Iterator<?> it = rows.iterator(); boolean hasData = it.hasNext(); if (pager != null && (isLastPage || !hasData)) { pager.max(pager.pageNumber()); } if (!hasData) { return U.list(noDataAvailable(), pager); // no data } Class<?> type = it.next().getClass(); Items itemsModel; if (rows instanceof Items) { itemsModel = (Items) rows; } else { itemsModel = Models.beanItems(type, U.array(rows)); } final List<Property> props = itemsModel.properties(columns); boolean ordered = !U.isEmpty(orderBy); Var<String> order = null; String currentOrder = orderBy; if (ordered) { order = GUI.var("_o" + seq("order"), orderBy); currentOrder = order.get(); itemsModel = itemsModel.orderedBy(currentOrder); } Tag header = tableHeader(props, order); Tag body = tableBody(props, itemsModel); return fullTable(header, body, pager); } protected Tag noDataAvailable() { return GUI.NOTHING; } protected Pager noPager() { return null; } protected Tag fullTable(Tag header, Tag body, Pager pager) { return GUI.row(GUI.table_(GUI.thead(header), body), pager); } protected Tag tableBody(final List<Property> props, Items pageOrAll) { Tag body = GUI.tbody(); for (Item item : pageOrAll) { Tag row = itemRow(props, item); body = body.append(row); } return body; } protected Tag tableHeader(final List<Property> props, Var<String> order) { Tag header = tr(); for (int i = 0; i < props.size(); i++) { Property prop = props.get(i); Tag sortIcon = null; Object caption = U.notEmpty(headers) && headers.length > i ? headers[i] : null; caption = U.or(caption, prop.caption()); Object sort; if (order != null) { String currentOrder = order.get(); if (currentOrder.equals(prop.name())) { sortIcon = GUI.fa("sort-amount-asc"); } if (currentOrder.equals("-" + prop.name())) { sortIcon = GUI.fa("sort-amount-desc"); } sort = GUI.a_void(caption, " ", sortIcon).cmd("_sort", order, prop.name()); } else { sort = caption; } header = header.append(th(sort)); } return header; } protected Tag itemRow(List<Property> properties, Item item) { Tag row = tr(); for (Property prop : properties) { Object value = prop.get(item); value = U.or(value, ""); row = row.append(cell(GUI.display(value))); } if (rowCmd != null) { row = row.cmd(rowCmd, item.value()); row = row.class_("pointer"); } else { String js = onClickScript(item); if (U.notEmpty(js)) { row = row.onclick(js); row = row.class_("pointer"); } } return row; } protected String onClickScript(Item item) { String uri; if (toUri != null) { uri = Lmbd.eval(toUri, item.value()); } else { uri = GUI.uriFor(item.value()); if (U.notEmpty(uri)) { uri = Msc.uri(uri, "view"); } } return U.notEmpty(uri) ? U.frmt("Rapidoid.goAt('%s');", uri) : null; } protected TdTag cell(Object value) { if (U.notEmpty(highlightRegex)) { String s = String.valueOf(value); value = GUI.highlight(s, highlightRegex); } return td(value); } public Iterable<?> items() { return items; } public Grid items(Iterable<?> items) { U.must(!(items instanceof Items)); this.items = items; return this; } public String orderBy() { return orderBy; } public Grid orderBy(String orderBy) { this.orderBy = orderBy; return this; } public int pageSize() { return pageSize; } public Grid pageSize(int pageSize) { this.pageSize = pageSize; return this; } public String[] columns() { return columns; } public Grid columns(String... columns) { this.columns = columns; return this; } public Grid columns(Iterable<String> columns) { return columns(U.arrayOf(String.class, columns)); } public String rowCmd() { return rowCmd; } public Grid rowCmd(String rowCmd) { this.rowCmd = rowCmd; return this; } public Mapper<Object, String> toUri() { return toUri; } @SuppressWarnings("unchecked") public <T> Grid toUri(Mapper<T, String> toUri) { this.toUri = (Mapper<Object, String>) toUri; return this; } public Object[] headers() { return headers; } public Grid headers(Object... headers) { this.headers = headers; return this; } public Grid headers(Iterable<?> headers) { return headers(U.array(headers)); } public String highlightRegex() { return highlightRegex; } public Grid highlightRegex(String highlightRegex) { this.highlightRegex = highlightRegex; return this; } }