001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.console.views; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022 023import org.apache.juneau.commons.bean.*; 024import org.apache.juneau.console.widgets.*; 025 026/** 027 * A named cell-renderer reference in the {@code VIEW_META} wire contract. 028 * 029 * <p> 030 * Serializes to the canonical object form <c>{"id":"tag","meta":{"field":"status"}}</c>; the optional 031 * {@link #meta} and {@link #popover} members are omitted from the wire when unset. The client 032 * {@code juneau-renders.js} registry looks up {@link #id} and invokes the matching renderer, passing the 033 * {@link #meta} map through as per-column context (and painting {@link #popover}, when present, from row data 034 * already on the client). Typed factories exist for the renderers that take structured metadata: 035 * {@link #progress(int)} and {@link #pill(String)}. Timestamp renderers ({@code ts-zulu}, {@code datetime}) 036 * honor {@code meta.popup}: {@code off} disables the local/California hover popup; {@code ts-zulu} defaults 037 * the popup on, {@code datetime} defaults it off. 038 * 039 * <p> 040 * A compact string sugar is supported via {@link #parse(String)}: everything after the <b>first</b> colon becomes 041 * <c>meta.field</c> — e.g. <c>"tag:status"</c> → <c>{id:"tag", meta:{field:"status"}}</c> and 042 * <c>"date"</c> → <c>{id:"date"}</c>. 043 * 044 * <h5 class='section'>See Also:</h5> 045 * <ul> 046 * <li class='jc'>{@link ViewTable} 047 * </ul> 048 * 049 * @since 10.0.0 050 */ 051@BeanType(properties="id,meta,popover") 052@SuppressWarnings({ 053 "java:S1845" // Fluent-builder setters intentionally mirror field names (Juneau DSL convention). 054}) 055public class Render { 056 057 /** The renderer id (registry key in {@code juneau-renders.js}). */ 058 public String id; 059 060 /** Optional per-column renderer metadata. Omitted from the wire when absent. */ 061 public Map<String,Object> meta; 062 063 /** 064 * Creates a renderer reference with the specified id and no metadata. 065 * 066 * @param id The renderer id. Must not be <jk>null</jk>. 067 * @return A new {@link Render}. 068 */ 069 public static Render of(String id) { 070 var r = new Render(); 071 r.id = id; 072 return r; 073 } 074 075 /** 076 * Parses a render-id string into a {@link Render}, applying the <c>"id:field"</c> string sugar. 077 * 078 * <p> 079 * Everything after the <b>first</b> colon becomes <c>meta.field</c>; a bare id (no colon) yields a 080 * metadata-free renderer. 081 * 082 * @param s The render-id string. Must not be <jk>null</jk> or blank. 083 * @return A new {@link Render}. 084 */ 085 public static Render parse(String s) { 086 if (ib(s)) 087 throw iaex("Render id must not be null or blank."); 088 var i = s.indexOf(':'); 089 if (i < 0) 090 return of(s); 091 return of(s.substring(0, i)).meta("field", s.substring(i + 1)); 092 } 093 094 /** 095 * Adds a metadata key/value pair to this renderer reference. 096 * 097 * @param key The metadata key. Must not be <jk>null</jk>. 098 * @param value The metadata value. Serialized as given, so a {@link Number} or {@link Boolean} stays a JSON 099 * number or boolean (for example {@code meta("places", 2)}). 100 * @return This object. 101 */ 102 public Render meta(String key, Object value) { 103 if (meta == null) 104 meta = m(); 105 meta.put(key, value); 106 return this; 107 } 108 109 /** 110 * Optional per-cell popover, omitted from the wire when unset. 111 * 112 * <p> 113 * The runtime appends a sibling trigger next to the rendered cell and paints the popover from row data 114 * already on the client. Never assigned {@code innerHTML}. 115 */ 116 public CellPopover popover; 117 118 /** 119 * Sets the optional per-cell popover. 120 * 121 * @param value The popover definition. Can be <jk>null</jk> to unset. 122 * @return This object. 123 */ 124 public Render popover(CellPopover value) { 125 popover = value; 126 return this; 127 } 128 129 /** 130 * Creates a {@code progress} renderer with the given maximum (denominator). 131 * 132 * <p> 133 * Equivalent to {@code Render.of("progress").meta("max", String.valueOf(max))}. Chain 134 * {@link #meta(String,Object)} for {@code label} / {@code field}. 135 * 136 * @param max The ratio denominator. Serialized as the string {@code meta.max}. 137 * @return A new {@link Render}. 138 */ 139 public static Render progress(int max) { 140 return of("progress").meta("max", String.valueOf(max)); 141 } 142 143 /** 144 * Creates a {@code progress} renderer with maximum and optional warn/exceeds thresholds. 145 * 146 * <p> 147 * {@code warn} and {@code exceeds} are omitted from {@code meta} when <jk>null</jk>. A boxed {@code 0} is a 148 * real threshold ({@code "0"}), not omitted. 149 * 150 * @param max The ratio denominator. 151 * @param warn Warn threshold in the same units as the cell value. Can be <jk>null</jk>. 152 * @param exceeds Exceeds threshold in the same units as the cell value. Can be <jk>null</jk>. 153 * @return A new {@link Render}. 154 */ 155 public static Render progress(int max, Integer warn, Integer exceeds) { 156 var r = progress(max); 157 if (warn != null) 158 r.meta("warn", String.valueOf(warn)); 159 if (exceeds != null) 160 r.meta("exceeds", String.valueOf(exceeds)); 161 return r; 162 } 163 164 /** 165 * Creates a {@code pill} renderer with the leading dot on and the tone inherited from the chip theme. 166 * 167 * <p> 168 * Equivalent to {@code Render.of("pill")}. The dot color follows the {@code .tag.<field>.<value>} theme 169 * ({@code currentColor}) until an explicit {@code tone} is chosen. Chain {@link #meta(String,Object)} for 170 * {@code field}, {@code dot} ({@code on}|{@code off}) and {@code action} (a {@code rowActions} id). 171 * 172 * <p> 173 * {@code action} is optional and only meaningful on a table column: a pill named on a field-grid fill 174 * sink is unconditionally display-only and rejects an {@code action} outright. 175 * 176 * @return A new {@link Render}. 177 */ 178 public static Render pill() { 179 return of("pill"); 180 } 181 182 /** 183 * Creates a {@code pill} renderer with an explicit dot tone. 184 * 185 * <p> 186 * {@code tone} is one of the five status tones 187 * {@code info}|{@code success}|{@code warning}|{@code error}|{@code neutral} (see 188 * {@link StatusTone}). A <jk>null</jk> or blank {@code tone} is omitted 189 * from {@code meta} (identical to {@link #pill()}); {@code neutral} is emitted verbatim and, like an absent tone, 190 * leaves the dot inheriting {@code currentColor}. The tone is not validated server-side: the client 191 * (<c>NS._render.pillTones</c>) ignores an off-palette token, so the dot gets no tone class. Prefer 192 * {@link #pill(StatusTone)} to stay on the palette. 193 * 194 * <p> 195 * Not to be confused with {@link #progress(int, Integer, Integer)}'s {@code warn}/{@code exceeds} meta, which are 196 * numeric thresholds rather than tone names and share no vocabulary with this palette. 197 * 198 * @param tone The explicit dot tone. Can be <jk>null</jk> or blank to inherit. 199 * @return A new {@link Render}. 200 */ 201 public static Render pill(String tone) { 202 var r = pill(); 203 if (inb(tone)) 204 r.meta("tone", tone); 205 return r; 206 } 207 208 /** 209 * Creates a {@code pill} renderer with an explicit dot tone taken from the {@link StatusTone} palette. 210 * 211 * <p> 212 * Equivalent to <c>pill(tone == <jk>null</jk> ? <jk>null</jk> : tone.wire())</c>; a <jk>null</jk> tone is 213 * omitted from {@code meta}, identical to {@link #pill()}. 214 * 215 * <h5 class='section'>Example:</h5> 216 * <p class='bjava'> 217 * <jc>// Renders as {id:"pill",meta:{tone:"warning"}}.</jc> 218 * Render <jv>r</jv> = Render.<jsm>pill</jsm>(StatusTone.<jsf>WARNING</jsf>); 219 * </p> 220 * 221 * @param tone The explicit dot tone. Can be <jk>null</jk> to inherit. 222 * @return A new {@link Render}. 223 */ 224 public static Render pill(StatusTone tone) { 225 return pill(tone == null ? null : tone.wire()); 226 } 227}