PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / metric-cards / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/metric-cards/index.js

320 lines 27.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 var el = wp.element.createElement;
3 var Fragment = wp.element.Fragment;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var __ = wp.i18n.__;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelBody = wp.components.PanelBody;
10 var RangeControl = wp.components.RangeControl;
11 var ToggleControl = wp.components.ToggleControl;
12 var TextControl = wp.components.TextControl;
13 var SelectControl = wp.components.SelectControl;
14 var Button = wp.components.Button;
15
16 var _tc, _tv;
17 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
18 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
19
20 var CARD_STYLE_OPTIONS = [
21 { label: 'Shadow', value: 'shadow' },
22 { label: 'Glass', value: 'glass' },
23 { label: 'Flat', value: 'flat' },
24 ];
25 var TREND_OPTIONS = [
26 { label: 'Up (positive)', value: 'up' },
27 { label: 'Down (negative)', value: 'down' },
28 { label: 'Neutral', value: 'neutral' },
29 ];
30 var COLUMN_OPTIONS = [
31 { label: '2 columns', value: '2' },
32 { label: '3 columns', value: '3' },
33 { label: '4 columns', value: '4' },
34 ];
35
36 var ICON_PATHS = {
37 chart: 'M18 20V10M12 20V4M6 20v-6',
38 users: 'M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M9 11a4 4 0 100-8 4 4 0 000 8zM23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75',
39 shield: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z',
40 clock: 'M12 2a10 10 0 100 20A10 10 0 0012 2zM12 6v6l4 2',
41 bolt: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z',
42 heart: 'M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z',
43 star: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z',
44 dollar: 'M12 1v22M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6',
45 };
46 var ICON_OPTIONS = [
47 { label: 'Bar chart', value: 'chart' },
48 { label: 'Users', value: 'users' },
49 { label: 'Shield', value: 'shield' },
50 { label: 'Clock', value: 'clock' },
51 { label: 'Lightning', value: 'bolt' },
52 { label: 'Heart', value: 'heart' },
53 { label: 'Star', value: 'star' },
54 { label: 'Dollar', value: 'dollar' },
55 ];
56
57 function makeId() { return 'm' + Math.random().toString(36).substr(2, 6); }
58
59 /* Build SVG polyline from comma-separated numbers */
60 function buildSparklinePath(dataStr, width, height) {
61 var nums = dataStr.split(',').map(function (s) { return parseFloat(s.trim()); }).filter(function (n) { return !isNaN(n); });
62 if (nums.length < 2) return '';
63 var min = Math.min.apply(null, nums);
64 var max = Math.max.apply(null, nums);
65 var range = max - min || 1;
66 var pad = 2;
67 var w = width - pad * 2;
68 var h = height - pad * 2;
69 var points = nums.map(function (n, i) {
70 var x = pad + (i / (nums.length - 1)) * w;
71 var y = pad + h - ((n - min) / range) * h;
72 return x.toFixed(1) + ',' + y.toFixed(1);
73 });
74 return points.join(' ');
75 }
76
77 function SparklineSVG(props) {
78 var dataStr = props.dataStr || '';
79 var width = props.width || 80;
80 var height = props.height || 32;
81 var color = props.color || '#6c3fb5';
82 var points = buildSparklinePath(dataStr, width, height);
83 if (!points) return null;
84 return el('svg', { width: width, height: height, viewBox: '0 0 ' + width + ' ' + height, style: { display: 'block', overflow: 'visible', marginTop: '8px' } },
85 el('polyline', { points: points, fill: 'none', stroke: color, strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }),
86 el('polyline', { points: points + ' ' + (width - 2) + ',' + height + ' 2,' + height, fill: color, fillOpacity: 0.12, stroke: 'none' })
87 );
88 }
89
90 function TrendArrow(props) {
91 var trend = props.trend;
92 var color = props.color;
93 if (trend === 'up') {
94 return el('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: 2.5, strokeLinecap: 'round', strokeLinejoin: 'round', style: { display: 'inline', verticalAlign: 'middle', marginRight: '2px' } },
95 el('polyline', { points: '18 15 12 9 6 15' }));
96 }
97 if (trend === 'down') {
98 return el('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: 2.5, strokeLinecap: 'round', strokeLinejoin: 'round', style: { display: 'inline', verticalAlign: 'middle', marginRight: '2px' } },
99 el('polyline', { points: '6 9 12 15 18 9' }));
100 }
101 return null;
102 }
103
104 function MetricCard(props) {
105 var metric = props.metric;
106 var a = props.a;
107 var trendColor = metric.trend === 'up' ? (a.upColor || '#16a34a') : metric.trend === 'down' ? (a.downColor || '#dc2626') : '#6b7280';
108 var cardBg = metric.bgColor || a.globalCardBg;
109 var textColor = metric.textColor || a.globalTextColor;
110 var accent = metric.accentColor || '#6c3fb5';
111 var boxShadow = a.cardStyle === 'shadow' ? '0 4px 24px rgba(0,0,0,0.10)' : 'none';
112 var border = a.cardStyle === 'bordered' ? '1.5px solid rgba(108,63,181,0.15)' : 'none';
113 var backdropFilter = a.cardStyle === 'glass' ? 'blur(12px)' : undefined;
114 var iconPath = ICON_PATHS[metric.icon] || ICON_PATHS.chart;
115
116 return el('div', { className: 'bkbg-metric-card', style: { background: cardBg, color: textColor, borderRadius: a.cardRadius + 'px', padding: a.cardPadding + 'px', boxShadow: boxShadow, border: border, backdropFilter: backdropFilter, position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column' } },
117 a.showIcon && el('div', { style: { position: 'absolute', top: a.cardPadding + 'px', right: a.cardPadding + 'px', color: accent, opacity: 0.6 } },
118 el('svg', { viewBox: '0 0 24 24', width: a.iconSize, height: a.iconSize, fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
119 el('path', { d: iconPath }))
120 ),
121 el('p', { className: 'bkbg-mtc-label', style: { margin: '0 0 4px', color: a.labelColor || undefined, opacity: 0.75 } }, metric.label),
122 el('p', { className: 'bkbg-metric-value', style: { margin: '0' } }, metric.value),
123 a.showTrend && el('div', { className: 'bkbg-mtc-trend', style: { display: 'flex', alignItems: 'center', gap: '4px', marginTop: '6px', color: trendColor } },
124 el(TrendArrow, { trend: metric.trend, color: trendColor }),
125 el('span', null, metric.trendValue),
126 a.showTrendPeriod && metric.trendPeriod && el('span', { style: { opacity: 0.6, fontWeight: 400 } }, metric.trendPeriod)
127 ),
128 a.showSparkline && metric.sparkline && el(SparklineSVG, { dataStr: metric.sparkline, width: 80, height: a.sparklineHeight, color: accent })
129 );
130 }
131
132 registerBlockType('blockenberg/metric-cards', {
133 deprecated: [{
134 attributes: JSON.parse(JSON.stringify(wp.blocks.getBlockType ? {} : {})),
135 save: function (props) {
136 var a = props.attributes;
137 var blockProps = wp.blockEditor.useBlockProps.save({ className: 'bkbg-metric-cards-wrap' });
138 var cols = parseInt(a.columns, 10) || 3;
139
140 return el('div', Object.assign({}, blockProps, { style: { paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px', backgroundColor: a.bgColor || undefined } }),
141 el('div', { className: 'bkbg-metric-cards', style: { display: 'grid', gridTemplateColumns: 'repeat(' + cols + ', 1fr)', gap: a.gap + 'px' } },
142 a.metrics.map(function (metric) {
143 var trendColor = metric.trend === 'up' ? (a.upColor || '#16a34a') : metric.trend === 'down' ? (a.downColor || '#dc2626') : '#6b7280';
144 var cardBg = metric.bgColor || a.globalCardBg;
145 var textColor = metric.textColor || a.globalTextColor;
146 var accent = metric.accentColor || '#6c3fb5';
147 var boxShadow = a.cardStyle === 'shadow' ? '0 4px 24px rgba(0,0,0,0.10)' : 'none';
148 var backdropFilter = a.cardStyle === 'glass' ? 'blur(12px)' : undefined;
149 var iconPath = ICON_PATHS[metric.icon] || ICON_PATHS.chart;
150
151 return el('div', { key: metric.id, className: 'bkbg-metric-card', 'data-trend': metric.trend, 'data-value': metric.value, style: { background: cardBg, color: textColor, borderRadius: a.cardRadius + 'px', padding: a.cardPadding + 'px', boxShadow: boxShadow, backdropFilter: backdropFilter, position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column' } },
152 a.showIcon && el('div', { style: { position: 'absolute', top: a.cardPadding + 'px', right: a.cardPadding + 'px', color: accent, opacity: 0.6 } },
153 el('svg', { viewBox: '0 0 24 24', width: a.iconSize, height: a.iconSize, fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': 'true' },
154 el('path', { d: iconPath }))
155 ),
156 el('p', { style: { margin: '0 0 4px', fontSize: a.labelSize + 'px', fontWeight: (a.labelFontWeight || 600), lineHeight: (a.lineHeight || 1.3), color: a.labelColor || undefined, opacity: 0.75 } }, metric.label),
157 el('p', { className: 'bkbg-metric-value', style: { margin: '0', fontSize: a.valueSize + 'px', fontWeight: a.valueFontWeight, lineHeight: 1.1 } }, metric.value),
158 a.showTrend && el('div', { style: { display: 'flex', alignItems: 'center', gap: '4px', marginTop: '6px', fontSize: a.trendSize + 'px', color: trendColor, fontWeight: 600 } },
159 metric.trend !== 'neutral' && el('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: trendColor, strokeWidth: 2.5, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': 'true' },
160 el('polyline', { points: metric.trend === 'up' ? '18 15 12 9 6 15' : '6 9 12 15 18 9' })),
161 el('span', null, metric.trendValue),
162 a.showTrendPeriod && metric.trendPeriod && el('span', { style: { opacity: 0.6, fontWeight: 400 } }, metric.trendPeriod)
163 ),
164 a.showSparkline && metric.sparkline && (function () {
165 var points = buildSparklinePath(metric.sparkline, 80, a.sparklineHeight);
166 return points ? el('svg', { width: 80, height: a.sparklineHeight, viewBox: '0 0 80 ' + a.sparklineHeight, style: { display: 'block', overflow: 'visible', marginTop: '8px' } },
167 el('polyline', { points: points, fill: 'none', stroke: accent, strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }),
168 el('polyline', { points: points + ' 78,' + a.sparklineHeight + ' 2,' + a.sparklineHeight, fill: accent, fillOpacity: 0.12, stroke: 'none' })
169 ) : null;
170 })()
171 );
172 })
173 )
174 );
175 }
176 }],
177 edit: function (props) {
178 var attributes = props.attributes;
179 var setAttributes = props.setAttributes;
180 var metrics = attributes.metrics;
181 var blockProps = useBlockProps((function () {
182 var _tvf = getTypoCssVars();
183 var s = { paddingTop: attributes.paddingTop + 'px', paddingBottom: attributes.paddingBottom + 'px', backgroundColor: attributes.bgColor || undefined };
184 Object.assign(s, _tvf(attributes.valueTypo, '--bkbg-mtc-vl-'));
185 Object.assign(s, _tvf(attributes.labelTypo, '--bkbg-mtc-lb-'));
186 Object.assign(s, _tvf(attributes.trendTypo, '--bkbg-mtc-tr-'));
187 return { style: s };
188 })());
189
190 function setMetric(id, patch) { setAttributes({ metrics: metrics.map(function (m) { return m.id === id ? Object.assign({}, m, patch) : m; }) }); }
191 function addMetric() {
192 setAttributes({ metrics: metrics.concat([{ id: makeId(), label: 'New Metric', value: '0', trend: 'up', trendValue: '+0%', trendPeriod: 'vs last month', icon: 'chart', sparkline: '10,20,15,30,25,40,35', accentColor: '#6c3fb5', bgColor: '', textColor: '' }]) });
193 }
194 function removeMetric(id) { if (metrics.length <= 1) return; setAttributes({ metrics: metrics.filter(function (m) { return m.id !== id; }) }); }
195
196 var cols = parseInt(attributes.columns, 10) || 3;
197
198 return el(Fragment, null,
199 el(InspectorControls, null,
200 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
201 el(SelectControl, { label: __('Columns', 'blockenberg'), value: attributes.columns, options: COLUMN_OPTIONS, onChange: function (v) { setAttributes({ columns: v }); } }),
202 el(SelectControl, { label: __('Card style', 'blockenberg'), value: attributes.cardStyle, options: CARD_STYLE_OPTIONS, onChange: function (v) { setAttributes({ cardStyle: v }); } }),
203 el(RangeControl, { label: __('Gap (px)', 'blockenberg'), value: attributes.gap, min: 8, max: 60, onChange: function (v) { setAttributes({ gap: v }); } }),
204 el(RangeControl, { label: __('Card radius (px)', 'blockenberg'), value: attributes.cardRadius, min: 0, max: 32, onChange: function (v) { setAttributes({ cardRadius: v }); } }),
205 el(RangeControl, { label: __('Card padding (px)', 'blockenberg'), value: attributes.cardPadding, min: 8, max: 64, onChange: function (v) { setAttributes({ cardPadding: v }); } })
206 ),
207 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
208 el(ToggleControl, { label: __('Show icon', 'blockenberg'), checked: attributes.showIcon, onChange: function (v) { setAttributes({ showIcon: v }); }, __nextHasNoMarginBottom: true }),
209 el(RangeControl, { label: __('Icon size (px)', 'blockenberg'), value: attributes.iconSize, min: 16, max: 48, onChange: function (v) { setAttributes({ iconSize: v }); } }),
210 el(ToggleControl, { label: __('Show sparkline', 'blockenberg'), checked: attributes.showSparkline, onChange: function (v) { setAttributes({ showSparkline: v }); }, __nextHasNoMarginBottom: true }),
211 el(RangeControl, { label: __('Sparkline height (px)', 'blockenberg'), value: attributes.sparklineHeight, min: 16, max: 80, onChange: function (v) { setAttributes({ sparklineHeight: v }); } }),
212 el(ToggleControl, { label: __('Show trend', 'blockenberg'), checked: attributes.showTrend, onChange: function (v) { setAttributes({ showTrend: v }); }, __nextHasNoMarginBottom: true }),
213 el(ToggleControl, { label: __('Show trend period', 'blockenberg'), checked: attributes.showTrendPeriod, onChange: function (v) { setAttributes({ showTrendPeriod: v }); }, __nextHasNoMarginBottom: true })
214 ),
215 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
216 el(getTypoControl(), { label: __('Value', 'blockenberg'), value: attributes.valueTypo, onChange: function (v) { setAttributes({ valueTypo: v }); } }),
217 el(getTypoControl(), { label: __('Label', 'blockenberg'), value: attributes.labelTypo, onChange: function (v) { setAttributes({ labelTypo: v }); } }),
218 el(getTypoControl(), { label: __('Trend', 'blockenberg'), value: attributes.trendTypo, onChange: function (v) { setAttributes({ trendTypo: v }); } })
219 ),
220 el(PanelColorSettings, {
221 title: __('Colors', 'blockenberg'), initialOpen: false,
222 colorSettings: [
223 { value: attributes.upColor, onChange: function (v) { setAttributes({ upColor: v || '' }); }, label: __('Trend up', 'blockenberg') },
224 { value: attributes.downColor, onChange: function (v) { setAttributes({ downColor: v || '' }); }, label: __('Trend down', 'blockenberg') },
225 { value: attributes.globalCardBg, onChange: function (v) { setAttributes({ globalCardBg: v || '' }); }, label: __('Card background', 'blockenberg') },
226 { value: attributes.globalTextColor,onChange: function (v) { setAttributes({ globalTextColor: v || '' }); }, label: __('Card text', 'blockenberg') },
227 { value: attributes.labelColor, onChange: function (v) { setAttributes({ labelColor: v || '' }); }, label: __('Label color', 'blockenberg') },
228 { value: attributes.bgColor, onChange: function (v) { setAttributes({ bgColor: v || '' }); }, label: __('Section background', 'blockenberg') },
229 ]
230 }),
231 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
232 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: attributes.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
233 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: attributes.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
234 ),
235 el(PanelBody, { title: __('Metrics', 'blockenberg'), initialOpen: false },
236 metrics.map(function (metric, idx) {
237 return el(PanelBody, { key: metric.id, title: (metric.label || 'Metric') + ' #' + (idx + 1), initialOpen: false },
238 el(TextControl, { label: __('Label', 'blockenberg'), value: metric.label, onChange: function (v) { setMetric(metric.id, { label: v }); } }),
239 el(TextControl, { label: __('Value', 'blockenberg'), value: metric.value, onChange: function (v) { setMetric(metric.id, { value: v }); } }),
240 el(SelectControl, { label: __('Trend direction', 'blockenberg'), value: metric.trend, options: TREND_OPTIONS, onChange: function (v) { setMetric(metric.id, { trend: v }); } }),
241 el(TextControl, { label: __('Trend value (e.g. +12%)', 'blockenberg'), value: metric.trendValue, onChange: function (v) { setMetric(metric.id, { trendValue: v }); } }),
242 el(TextControl, { label: __('Trend period (e.g. vs last month)', 'blockenberg'), value: metric.trendPeriod, onChange: function (v) { setMetric(metric.id, { trendPeriod: v }); } }),
243 el(SelectControl, { label: __('Icon', 'blockenberg'), value: metric.icon, options: ICON_OPTIONS, onChange: function (v) { setMetric(metric.id, { icon: v }); } }),
244 el(TextControl, { label: __('Sparkline data (comma-separated numbers)', 'blockenberg'), value: metric.sparkline, onChange: function (v) { setMetric(metric.id, { sparkline: v }); } }),
245 el(PanelColorSettings, {
246 title: __('Card Colors', 'blockenberg'), initialOpen: false,
247 colorSettings: [
248 { value: metric.accentColor, onChange: function (v) { setMetric(metric.id, { accentColor: v || '' }); }, label: __('Accent / Sparkline', 'blockenberg') },
249 { value: metric.bgColor, onChange: function (v) { setMetric(metric.id, { bgColor: v || '' }); }, label: __('Background', 'blockenberg') },
250 { value: metric.textColor, onChange: function (v) { setMetric(metric.id, { textColor: v || '' }); }, label: __('Text', 'blockenberg') },
251 ]
252 }),
253 metrics.length > 1 && el(Button, { onClick: function () { removeMetric(metric.id); }, variant: 'tertiary', size: 'compact', isDestructive: true }, __('Remove metric', 'blockenberg'))
254 );
255 }),
256 el(Button, { onClick: addMetric, variant: 'primary', style: { marginTop: '8px' } }, __('+ Add Metric', 'blockenberg'))
257 )
258 ),
259
260 el('div', blockProps,
261 el('div', { className: 'bkbg-metric-cards', style: { display: 'grid', gridTemplateColumns: 'repeat(' + cols + ', 1fr)', gap: attributes.gap + 'px' } },
262 metrics.map(function (metric) {
263 return el(MetricCard, { key: metric.id, metric: metric, a: attributes });
264 })
265 )
266 )
267 );
268 },
269
270 save: function (props) {
271 var a = props.attributes;
272 var blockProps = wp.blockEditor.useBlockProps.save((function () {
273 var _tvf = getTypoCssVars();
274 var s = { paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px', backgroundColor: a.bgColor || undefined };
275 Object.assign(s, _tvf(a.valueTypo, '--bkbg-mtc-vl-'));
276 Object.assign(s, _tvf(a.labelTypo, '--bkbg-mtc-lb-'));
277 Object.assign(s, _tvf(a.trendTypo, '--bkbg-mtc-tr-'));
278 return { className: 'bkbg-metric-cards-wrap', style: s };
279 })());
280 var cols = parseInt(a.columns, 10) || 3;
281
282 return el('div', blockProps,
283 el('div', { className: 'bkbg-metric-cards', style: { display: 'grid', gridTemplateColumns: 'repeat(' + cols + ', 1fr)', gap: a.gap + 'px' } },
284 a.metrics.map(function (metric) {
285 var trendColor = metric.trend === 'up' ? (a.upColor || '#16a34a') : metric.trend === 'down' ? (a.downColor || '#dc2626') : '#6b7280';
286 var cardBg = metric.bgColor || a.globalCardBg;
287 var textColor = metric.textColor || a.globalTextColor;
288 var accent = metric.accentColor || '#6c3fb5';
289 var boxShadow = a.cardStyle === 'shadow' ? '0 4px 24px rgba(0,0,0,0.10)' : 'none';
290 var backdropFilter = a.cardStyle === 'glass' ? 'blur(12px)' : undefined;
291 var iconPath = ICON_PATHS[metric.icon] || ICON_PATHS.chart;
292
293 return el('div', { key: metric.id, className: 'bkbg-metric-card', 'data-trend': metric.trend, 'data-value': metric.value, style: { background: cardBg, color: textColor, borderRadius: a.cardRadius + 'px', padding: a.cardPadding + 'px', boxShadow: boxShadow, backdropFilter: backdropFilter, position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column' } },
294 a.showIcon && el('div', { style: { position: 'absolute', top: a.cardPadding + 'px', right: a.cardPadding + 'px', color: accent, opacity: 0.6 } },
295 el('svg', { viewBox: '0 0 24 24', width: a.iconSize, height: a.iconSize, fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': 'true' },
296 el('path', { d: iconPath }))
297 ),
298 el('p', { className: 'bkbg-mtc-label', style: { margin: '0 0 4px', color: a.labelColor || undefined, opacity: 0.75 } }, metric.label),
299 el('p', { className: 'bkbg-metric-value', style: { margin: '0' } }, metric.value),
300 a.showTrend && el('div', { className: 'bkbg-mtc-trend', style: { display: 'flex', alignItems: 'center', gap: '4px', marginTop: '6px', color: trendColor } },
301 metric.trend !== 'neutral' && el('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: trendColor, strokeWidth: 2.5, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': 'true' },
302 el('polyline', { points: metric.trend === 'up' ? '18 15 12 9 6 15' : '6 9 12 15 18 9' })),
303 el('span', null, metric.trendValue),
304 a.showTrendPeriod && metric.trendPeriod && el('span', { style: { opacity: 0.6, fontWeight: 400 } }, metric.trendPeriod)
305 ),
306 a.showSparkline && metric.sparkline && (function () {
307 var points = buildSparklinePath(metric.sparkline, 80, a.sparklineHeight);
308 return points ? el('svg', { width: 80, height: a.sparklineHeight, viewBox: '0 0 80 ' + a.sparklineHeight, style: { display: 'block', overflow: 'visible', marginTop: '8px' } },
309 el('polyline', { points: points, fill: 'none', stroke: accent, strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }),
310 el('polyline', { points: points + ' 78,' + a.sparklineHeight + ' 2,' + a.sparklineHeight, fill: accent, fillOpacity: 0.12, stroke: 'none' })
311 ) : null;
312 })()
313 );
314 })
315 )
316 );
317 }
318 });
319 }());
320