| 105 | |
| 106 | /** Label/value bar list of optionally-pickable rows. */ |
| 107 | export function BarList<Row extends Record<string, unknown>>({ |
| 108 | rows, |
| 109 | keyName, |
| 110 | onPick, |
| 111 | rowKey, |
| 112 | titleFor, |
| 113 | className, |
| 114 | proportional, |
| 115 | valueName = "value", |
| 116 | emptyText, |
| 117 | }: { |
| 118 | rows: Array<Row>; |
| 119 | keyName: keyof Row & string; |
| 120 | onPick?: (row: Row) => void; |
| 121 | rowKey?: (row: Row) => string; |
| 122 | titleFor?: (row: Row) => string; |
| 123 | className?: string; |
| 124 | proportional?: boolean; |
| 125 | valueName?: string; |
| 126 | emptyText?: string; |
| 127 | }) { |
| 128 | if (!rows.length) { |
| 129 | return emptyText ? <EmptyState>{emptyText}</EmptyState> : <div className={cn("tdp-bar-list", className)} />; |
| 130 | } |
| 131 | const total = proportional |
| 132 | ? rows.reduce((acc, row) => acc + (Number(row[valueName]) || 0), 0) || 1 |
| 133 | : 0; |
| 134 | return ( |
| 135 | <div className={cn("tdp-bar-list", className)}> |
| 136 | {rows.map((row) => { |
| 137 | const label = String(row[keyName] ?? ""); |
| 138 | const key = rowKey?.(row) ?? String(row[keyName]); |
| 139 | const title = titleFor?.(row); |
| 140 | const value = "value" in row ? row.value : undefined; |
| 141 | const meta = "meta" in row ? row.meta : undefined; |
| 142 | const color = "color" in row ? row.color : undefined; |
| 143 | const inner = proportional ? ( |
| 144 | <> |
| 145 | <div className="tdp-bar-head"> |
| 146 | <span className="tdp-bar-label">{label}</span> |
| 147 | {value !== undefined && <span className="tdp-bar-value">{String(value)}</span>} |
| 148 | </div> |
| 149 | <div className="tdp-bar-track" aria-hidden="true"> |
| 150 | <span |
| 151 | className="tdp-bar-fill" |
| 152 | style={{ |
| 153 | width: |
| 154 | Math.max(2, Math.round(((Number(row[valueName]) || 0) / total) * 100)) + |
| 155 | "%", |
| 156 | }} |
| 157 | /> |
| 158 | </div> |
| 159 | </> |
| 160 | ) : ( |
| 161 | <> |
| 162 | {color !== undefined && ( |
| 163 | <span className="tdp-bar-dot" style={{ background: String(color) }} /> |
| 164 | )} |