/* FinPulse shared components + formatters. Exports to window. */
// ---- formatters ----
const fmt = {
price: (n) => n.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
pct: (n) => (n > 0 ? '+' : '') + n.toFixed(2) + '%',
signed: (n) => (n > 0 ? '+' : '') + n.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
mcap: (cr) => {
if (cr >= 100000) return '₹' + (cr / 100000).toFixed(2) + 'L Cr';
if (cr >= 1000) return '₹' + (cr / 1000).toFixed(1) + 'K Cr';
return '₹' + cr.toFixed(0) + ' Cr';
},
usMcap: (m) => {
if (!m || m <= 0) return '—';
if (m >= 1e6) return '$' + (m / 1e6).toFixed(2) + 'T';
if (m >= 1e3) return '$' + (m / 1e3).toFixed(1) + 'B';
return '$' + m.toFixed(0) + 'M';
},
stockMcap: (s) => ((s && s.live && s.cur === 'USD') ? fmt.usMcap(s.mcap) : fmt.mcap(s.mcap)),
stockPrice: (s) => {
const c = s && s.live ? window.FinnhubMappers.curSym(s.cur) : '$';
return c + fmt.price(s.price);
},
vol: (lakh) => lakh.toFixed(1) + 'L',
idx: (n) => n.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
};
// ---- change chip ----
function ChangeChip({ value, sm, showArrow = true }) {
const up = value >= 0;
return (
{showArrow && {up ? '▲' : '▼'}}
{fmt.pct(value)}
);
}
// ---- stock avatar ----
function StockAvatar({ stock, size = 36 }) {
return (
{stock.initials}
);
}
// ---- index tile ----
function IndexTile({ ix, selected, onClick }) {
const up = ix.chg >= 0;
const sym = ix.cur === 'INR' ? '₹' : (ix.cur === 'USD' ? '$' : '');
const Tag = onClick ? 'button' : 'div';
return (
{ix.label}
{sym}{fmt.idx(ix.value)}
{fmt.pct(ix.chg)}
);
}
// ---- stock quote card ----
function StockQuoteCard({ stock, onOpen }) {
return (
onOpen && onOpen(stock)} style={{ cursor: onOpen ? 'pointer' : 'default' }}>
{stock.name}
{stock.ticker}
);
}
// ---- metric card ----
function MetricCard({ label, value, sub, color = 'var(--accent)' }) {
return (
{label}
{value}
{sub &&
{sub}
}
);
}
// ---- category pill ----
function CatPill({ cat, color }) {
return {cat};
}
// ---- news image: real photo when available, striped placeholder otherwise ----
function NewsImg({ seed, className, h, src }) {
const [failed, setFailed] = useState(false);
if (src && !failed) {
return
setFailed(true)} />;
}
const hue = (seed * 47) % 360;
return (
news photo
);
}
function newsOpenProps(item, onOpen) {
if (!onOpen) return {};
return {
role: 'button',
tabIndex: 0,
onClick: () => onOpen(item),
onKeyDown: (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onOpen(item); } },
style: { cursor: 'pointer' },
};
}
// ---- featured news ----
function NewsFeatured({ item, onOpen }) {
return (
{item.source}
{item.headline}
{item.summary &&
{(window.HtmlText ? window.HtmlText.stripHtml(item.summary) : item.summary)}
}
{item.author}·{item.time}·{item.read} read
{item.sentiment === 'bull' ? 'Bullish' : 'Bearish'}
);
}
// ---- standard news card ----
function NewsStandard({ item, onOpen }) {
return (
{item.source}
{item.headline}
{item.time}
);
}
// ---- compact news row ----
function NewsCompact({ items, onOpen }) {
return (
{items.map((n) => (
{n.headline}
{n.time}
))}
);
}
// ---- empty state ----
function EmptyState({ icon, title, sub, action }) {
return (
{title}
{sub}
{action &&
{action}
}
);
}
Object.assign(window, { fmt, ChangeChip, StockAvatar, IndexTile, StockQuoteCard, MetricCard, CatPill, NewsImg, NewsFeatured, NewsStandard, NewsCompact, EmptyState });