@razorpay/blade
Version:
The Design System that powers Razorpay
1,222 lines (1,185 loc) • 42.6 kB
JavaScript
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _taggedTemplateLiteral from '@babel/runtime/helpers/taggedTemplateLiteral';
import { memo, useMemo, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import dayjs from 'dayjs';
import { formatNumber } from '@razorpay/i18nify-js';
import ReactMarkdown from 'react-markdown';
import { createGlobalStyle } from 'styled-components';
import { useGenUIAnimation, useGenUIAction } from './GenUIContext.web.js';
import { ComponentRenderer } from './GenUISchemaRenderer.web.js';
import { createRehypeAnimate } from './rehypeAnimate.js';
import '../Box/index.js';
import '../Typography/index.js';
import '../Skeleton/index.js';
import '../Card/index.js';
import '../Badge/index.js';
import '../Divider/index.js';
import '../Charts/index.js';
import '../Table/index.js';
import '../Link/index.js';
import '../Icons/index.js';
import '../InfoGroup/index.js';
import '../Button/index.js';
import '../Button/IconButton/index.js';
import '../Amount/index.js';
import '../Indicator/index.js';
import '../Alert/index.js';
import '../Tooltip/index.js';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { Box } from '../Box/Box.js';
import { Text } from '../Typography/Text/Text.js';
import { Skeleton } from '../Skeleton/Skeleton.js';
import { Heading } from '../Typography/Heading/Heading.js';
import Link from '../Link/Link/Link.js';
import DotIcon from '../Icons/DotIcon/DotIcon.js';
import { Divider } from '../Divider/Divider.js';
import { ChartBarWrapper, ChartBar } from '../Charts/BarChart/BarChart.web.js';
import { ChartCartesianGrid, ChartXAxis, ChartYAxis, ChartLegend, ChartTooltip } from '../Charts/CommonChartComponents/CommonChartComponents.web.js';
import { ChartLineWrapper, ChartLine } from '../Charts/LineChart/LineChart.web.js';
import { ChartAreaWrapper, ChartArea } from '../Charts/AreaChart/AreaChart.web.js';
import { ChartDonutWrapper, ChartDonut } from '../Charts/DonutChart/DonutChart.web.js';
import { Tooltip } from '../Tooltip/Tooltip.web.js';
import { TooltipInteractiveWrapper } from '../Tooltip/TooltipInteractiveWrapper.web.js';
import { IconButton } from '../Button/IconButton/IconButton.js';
import CopyIcon from '../Icons/CopyIcon/CopyIcon.js';
import { Badge } from '../Badge/Badge.js';
import { Indicator } from '../Indicator/Indicator.js';
import { Amount } from '../Amount/Amount.js';
import CheckIcon from '../Icons/CheckIcon/CheckIcon.js';
import CloseIcon from '../Icons/CloseIcon/CloseIcon.js';
import EditIcon from '../Icons/EditIcon/EditIcon.js';
import TrashIcon from '../Icons/TrashIcon/TrashIcon.js';
import DownloadIcon from '../Icons/DownloadIcon/DownloadIcon.js';
import EyeIcon from '../Icons/EyeIcon/EyeIcon.js';
import Button from '../Button/Button/Button.js';
import { Table } from '../Table/Table.web.js';
import { TableHeader, TableHeaderRow, TableHeaderCell } from '../Table/TableHeader.web.js';
import { TableBody, TableRow, TableCell } from '../Table/TableBody.web.js';
import { Card, CardBody } from '../Card/Card.js';
import { CardHeader, CardHeaderLeading } from '../Card/CardHeader.js';
import { CardFooter, CardFooterLeading } from '../Card/CardFooter.js';
import { InfoGroup, InfoItem, InfoItemKey, InfoItemValue } from '../InfoGroup/InfoGroup.web.js';
import InfoIcon from '../Icons/InfoIcon/InfoIcon.js';
import AlertTriangleIcon from '../Icons/AlertTriangleIcon/AlertTriangleIcon.js';
import CheckCircleIcon from '../Icons/CheckCircleIcon/CheckCircleIcon.js';
import { Alert } from '../Alert/Alert.js';
var _templateObject;
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var TextAnimationStyles = /*#__PURE__*/createGlobalStyle(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n @keyframes animate-fadeIn {\n from {\n opacity: 0;\n color: #48D08C;\n filter: blur(2px);\n }\n to {\n opacity: 1;\n color: inherit;\n filter: blur(0px);\n }\n }\n\n [data-animate-word] {\n display: inline-block;\n animation: animate-fadeIn\n var(--animate-duration, 400ms)\n var(--animate-easing, ease) both;\n }\n"])));
/**
* Built-in component types supported by GenUI
*/
var ComponentType = {
TEXT: 'TEXT',
CHART: 'CHART',
TABLE: 'TABLE',
CARD: 'CARD',
BADGE: 'BADGE',
SPACER: 'SPACER',
DIVIDER: 'DIVIDER',
STACK: 'STACK',
GRID: 'GRID',
INFO_GROUP: 'INFO_GROUP',
BUTTON: 'BUTTON',
LINK: 'LINK',
ALERT: 'ALERT',
AMOUNT: 'AMOUNT'
};
/**
* Valid feedback colors used across GenUI components (Badge, Indicator, Alert)
* Used to validate streaming JSON where color values might be partial/incomplete
*/
var FeedbackColors = ['information', 'negative', 'neutral', 'notice', 'positive', 'primary'];
var getValidFeedbackColor = function getValidFeedbackColor(color) {
var fallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'neutral';
if (!color) return fallback;
if (FeedbackColors.includes(color)) {
return color;
}
return fallback;
};
// Table cell component types (using component pattern)
// Table row action schema type
// Event payload dispatched when table row actions are triggered
/**
* Union type of all built-in UI components
*/
/**
* Generic UI component type - can be extended with custom components
*/
var ComponentErrorFallback = function ComponentErrorFallback() {
return /*#__PURE__*/jsx(Box, {
textAlign: "center",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
height: "100%",
width: "100%",
children: /*#__PURE__*/jsx(Text, {
size: "medium",
weight: "semibold",
children: "Error rendering component"
})
});
};
var ChartSkeletonLoader = function ChartSkeletonLoader(_ref) {
var title = _ref.title,
isTiny = _ref.isTiny,
_ref$chartHeight = _ref.chartHeight,
chartHeight = _ref$chartHeight === void 0 ? '350px' : _ref$chartHeight;
return /*#__PURE__*/jsxs(Box, {
width: "100%",
display: "flex",
flexDirection: "column",
gap: "spacing.3",
marginY: "spacing.3",
children: [title && !isTiny ? /*#__PURE__*/jsx(Skeleton, {
width: "200px",
height: "24px",
borderRadius: "medium",
marginBottom: "spacing.3"
}) : null, /*#__PURE__*/jsxs(Box, {
width: "100%",
height: chartHeight,
display: "flex",
flexDirection: "column",
gap: "spacing.3",
children: [/*#__PURE__*/jsxs(Box, {
display: "flex",
flexDirection: "row",
gap: "spacing.3",
height: "100%",
children: [/*#__PURE__*/jsx(Skeleton, {
width: "40px",
height: "100%",
borderRadius: "medium"
}), /*#__PURE__*/jsx(Skeleton, {
width: "100%",
height: "100%",
borderRadius: "medium"
})]
}), /*#__PURE__*/jsx(Skeleton, {
width: "100%",
height: "40px",
borderRadius: "medium"
})]
})]
});
};
/**
* Stable components object for ReactMarkdown to prevent re-renders during streaming.
* Defined outside the component to maintain referential equality.
*/
var markdownComponents = {
h1: function h1(_ref2) {
var children = _ref2.children;
return /*#__PURE__*/jsx(Heading, {
marginBottom: "spacing.4",
size: "large",
children: children
});
},
h2: function h2(_ref3) {
var children = _ref3.children;
return /*#__PURE__*/jsx(Heading, {
marginBottom: "spacing.3",
size: "medium",
children: children
});
},
h3: function h3(_ref4) {
var children = _ref4.children;
return /*#__PURE__*/jsx(Heading, {
marginBottom: "spacing.3",
size: "small",
children: children
});
},
h4: function h4(_ref5) {
var children = _ref5.children;
return /*#__PURE__*/jsx(Heading, {
marginBottom: "spacing.3",
size: "small",
children: children
});
},
b: function b(_ref6) {
var children = _ref6.children;
return /*#__PURE__*/jsx(Text, {
size: "medium",
weight: "semibold",
color: "surface.text.gray.subtle",
children: children
});
},
a: function a(_ref7) {
var href = _ref7.href,
children = _ref7.children;
return /*#__PURE__*/jsx(Link, {
href: href,
size: "medium",
variant: "anchor",
color: "primary",
target: "_blank",
rel: "noopener noreferrer",
children: children
});
},
li: function li(_ref8) {
var children = _ref8.children;
return /*#__PURE__*/jsxs(Text, {
marginY: "spacing.3",
size: "medium",
children: [/*#__PURE__*/jsx(DotIcon, {
marginBottom: "1px",
size: "xsmall"
}), " ", children]
});
},
ol: function ol(_ref9) {
var children = _ref9.children;
return /*#__PURE__*/jsx(Box, {
display: "flex",
flexDirection: "column",
marginLeft: "spacing.4",
children: children
});
},
ul: function ul(_ref0) {
var children = _ref0.children;
return /*#__PURE__*/jsx(Box, {
display: "flex",
flexDirection: "column",
marginLeft: "spacing.4",
children: children
});
},
hr: function hr() {
return /*#__PURE__*/jsx(Divider, {
marginY: "spacing.4"
});
},
i: function i(_ref1) {
var children = _ref1.children;
return /*#__PURE__*/jsx(Text, {
size: "medium",
variant: "caption",
color: "surface.text.gray.subtle",
children: children
});
},
p: function p(_ref10) {
var children = _ref10.children;
return /*#__PURE__*/jsx(Text, {
marginY: "spacing.2",
size: "medium",
color: "surface.text.gray.subtle",
children: children
});
}
};
var RenderTextComponent = /*#__PURE__*/memo(function (_ref11) {
var content = _ref11.content;
var _useGenUIAnimation = useGenUIAnimation(),
isAnimating = _useGenUIAnimation.isAnimating,
animateOptions = _useGenUIAnimation.animateOptions;
var rehypePlugins = useMemo(function () {
if (!isAnimating) return [];
return [createRehypeAnimate(animateOptions)];
}, [isAnimating, animateOptions]);
if (!content) return null;
return /*#__PURE__*/jsxs(Fragment, {
children: [isAnimating ? /*#__PURE__*/jsx(TextAnimationStyles, {}) : null, /*#__PURE__*/jsx(ReactMarkdown, {
skipHtml: true,
rehypePlugins: rehypePlugins,
components: markdownComponents,
children: content
})]
});
});
var RenderChartComponent = /*#__PURE__*/memo(function (_ref12) {
var chartType = _ref12.chartType,
title = _ref12.title,
xAxis = _ref12.xAxis,
data = _ref12.data,
_ref12$unit = _ref12.unit,
unit = _ref12$unit === void 0 ? '' : _ref12$unit,
_ref12$variant = _ref12.variant,
variant = _ref12$variant === void 0 ? 'full' : _ref12$variant,
valueFormatter = _ref12.valueFormatter;
var isTiny = variant === 'tiny';
var chartHeight = isTiny ? '100px' : '350px';
var minHeight = isTiny ? '100px' : '350px';
var isLoading = !chartType || !xAxis || !data || data.length === 0 || !valueFormatter || !valueFormatter.type;
// Handle incomplete data during streaming
if (isLoading) {
return /*#__PURE__*/jsx(ChartSkeletonLoader, {
title: title,
isTiny: isTiny,
chartHeight: chartHeight
});
}
// Filter out empty objects and incomplete data from streaming
var validData = data.filter(function (item) {
if (!item || Object.keys(item).length === 0) return false;
// Check if xAxis key exists and has a valid value
if (!item[xAxis] || item[xAxis] === '') return false;
// Check if at least one numeric value exists
var hasNumericValue = Object.keys(item).some(function (key) {
return key !== xAxis && typeof item[key] === 'number' && !isNaN(Number(item[key]));
});
return hasNumericValue;
});
if (validData.length === 0) {
return /*#__PURE__*/jsx(ChartSkeletonLoader, {
title: title,
isTiny: isTiny,
chartHeight: chartHeight
});
}
// Auto-detect all numeric keys except xAxis for plotting
var firstItem = validData[0];
var keysToPlot = Object.keys(firstItem).filter(function (key) {
return key !== xAxis && typeof firstItem[key] === 'number';
});
if (keysToPlot.length === 0) {
return /*#__PURE__*/jsx(ChartSkeletonLoader, {
title: title,
isTiny: isTiny,
chartHeight: chartHeight
});
}
var tickFormatter = function tickFormatter(value) {
var numValue = typeof value === 'string' ? parseFloat(value) : value;
if (isNaN(numValue)) {
return value.toString();
}
// Use valueFormatter if provided, otherwise fall back to unit
if (valueFormatter) {
var type = valueFormatter.type,
formatterCurrency = valueFormatter.currency,
formatterSuffix = valueFormatter.suffix;
var buildPrefixSuffixString = function buildPrefixSuffixString(v) {
return "".concat(v).concat(formatterSuffix !== null && formatterSuffix !== void 0 ? formatterSuffix : '');
};
try {
switch (type) {
case 'currency':
{
var formatted = formatNumber(numValue, {
currency: formatterCurrency || 'INR',
intlOptions: {
notation: 'compact',
maximumFractionDigits: 1
}
});
return buildPrefixSuffixString(formatted);
}
case 'percentage':
{
var _formatted = formatNumber(numValue / 100, {
intlOptions: {
style: 'percent',
minimumFractionDigits: 0,
maximumFractionDigits: 2
}
});
if (formatterSuffix === '%') {
return _formatted;
} else {
return buildPrefixSuffixString(_formatted);
}
}
case 'number':
{
var numberFormatted = formatNumber(numValue, {
intlOptions: {
minimumFractionDigits: 0,
maximumFractionDigits: 2
}
});
return buildPrefixSuffixString(numberFormatted);
}
case 'string':
{
return buildPrefixSuffixString(numValue.toString());
}
default:
return numValue.toString();
}
} catch (error) {
console.error('[GenUI Chart]: Error formatting value:', error);
return numValue.toString();
}
}
// Fallback to legacy unit formatting
return "".concat(numValue.toString()).concat(unit);
};
// TODO: Refactor into smaller components
return /*#__PURE__*/jsx(ErrorBoundary, {
FallbackComponent: ComponentErrorFallback,
children: /*#__PURE__*/jsxs(Box, {
width: "100%",
display: "flex",
flexDirection: "column",
gap: "spacing.3",
marginY: "spacing.3",
minHeight: chartHeight,
children: [title && !isTiny ? /*#__PURE__*/jsx(Text, {
size: "large",
weight: "semibold",
color: "surface.text.gray.subtle",
marginBottom: "spacing.3",
children: title
}) : null, /*#__PURE__*/jsx(Box, {
width: "100%",
height: chartHeight,
minHeight: minHeight,
children: chartType === 'bar' ? /*#__PURE__*/jsxs(ChartBarWrapper, {
width: "100%",
height: chartHeight,
data: validData,
colorTheme: "categorical",
children: [!isTiny ? /*#__PURE__*/jsx(ChartCartesianGrid, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartXAxis, {
dataKey: xAxis
}) : null, !isTiny ? /*#__PURE__*/jsx(ChartYAxis, {
tickFormatter: tickFormatter
}) : null, keysToPlot.map(function (key) {
return /*#__PURE__*/jsx(ChartBar, {
dataKey: key,
name: key
}, key);
}), !isTiny ? /*#__PURE__*/jsx(ChartLegend, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartTooltip, {}) : null]
}) : chartType === 'line' ? /*#__PURE__*/jsxs(ChartLineWrapper, {
width: "100%",
height: chartHeight,
minHeight: minHeight,
data: validData,
colorTheme: "categorical",
children: [!isTiny ? /*#__PURE__*/jsx(ChartCartesianGrid, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartXAxis, {
dataKey: xAxis
}) : null, !isTiny ? /*#__PURE__*/jsx(ChartYAxis, {
tickFormatter: tickFormatter
}) : null, keysToPlot.map(function (key) {
return /*#__PURE__*/jsx(ChartLine, {
dataKey: key,
name: key
}, key);
}), !isTiny ? /*#__PURE__*/jsx(ChartLegend, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartTooltip, {}) : null]
}) : chartType === 'area' ? /*#__PURE__*/jsxs(ChartAreaWrapper, {
width: "100%",
height: chartHeight,
minHeight: minHeight,
data: validData,
colorTheme: "categorical",
children: [!isTiny ? /*#__PURE__*/jsx(ChartCartesianGrid, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartXAxis, {
dataKey: xAxis
}) : null, !isTiny ? /*#__PURE__*/jsx(ChartYAxis, {
tickFormatter: tickFormatter
}) : null, keysToPlot.map(function (key) {
return /*#__PURE__*/jsx(ChartArea, {
dataKey: key,
name: key,
type: "monotone"
}, key);
}), !isTiny ? /*#__PURE__*/jsx(ChartLegend, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartTooltip, {}) : null]
}) : chartType === 'pie' ? /*#__PURE__*/jsxs(ChartDonutWrapper, {
width: "100%",
height: chartHeight,
minHeight: minHeight,
children: [/*#__PURE__*/jsx(ChartDonut, {
dataKey: keysToPlot[0],
nameKey: xAxis,
data: validData,
radius: "medium",
type: "circle"
}), !isTiny ? /*#__PURE__*/jsx(ChartLegend, {}) : null, !isTiny ? /*#__PURE__*/jsx(ChartTooltip, {}) : null]
}) : null
})]
})
});
},
// Custom comparison - only re-render if data length or key fields change
function (prevProps, nextProps) {
var _prevProps$data, _nextProps$data, _prevProps$data2, _nextProps$data2;
return prevProps.chartType === nextProps.chartType && prevProps.title === nextProps.title && prevProps.xAxis === nextProps.xAxis && prevProps.variant === nextProps.variant && ((_prevProps$data = prevProps.data) === null || _prevProps$data === void 0 ? void 0 : _prevProps$data.length) === ((_nextProps$data = nextProps.data) === null || _nextProps$data === void 0 ? void 0 : _nextProps$data.length) && JSON.stringify((_prevProps$data2 = prevProps.data) === null || _prevProps$data2 === void 0 ? void 0 : _prevProps$data2[prevProps.data.length - 1]) === JSON.stringify((_nextProps$data2 = nextProps.data) === null || _nextProps$data2 === void 0 ? void 0 : _nextProps$data2[nextProps.data.length - 1]) && JSON.stringify(prevProps.valueFormatter) === JSON.stringify(nextProps.valueFormatter);
});
// Primitive copy button — just the icon + tooltip, no text label.
// Compose this inside any cell that needs copy behaviour.
var Copyable = function Copyable(_ref13) {
var value = _ref13.value;
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
copied = _useState2[0],
setCopied = _useState2[1];
var handleCopy = function handleCopy() {
void navigator.clipboard.writeText(value !== null && value !== void 0 ? value : '');
setCopied(true);
setTimeout(function () {
return setCopied(false);
}, 2000);
};
return /*#__PURE__*/jsx(Tooltip, {
content: copied ? 'Copied!' : 'Copy',
placement: "top",
children: /*#__PURE__*/jsx(TooltipInteractiveWrapper, {
children: /*#__PURE__*/jsx(IconButton, {
icon: CopyIcon,
size: "medium",
emphasis: "intense",
accessibilityLabel: "Copy ".concat(value),
onClick: handleCopy
})
})
});
};
// Text + copy icon
var CopyableText = function CopyableText(_ref14) {
var value = _ref14.value;
return /*#__PURE__*/jsxs(Box, {
display: "flex",
alignItems: "center",
gap: "spacing.2",
children: [/*#__PURE__*/jsx(Text, {
size: "medium",
children: value !== null && value !== void 0 ? value : '-'
}), /*#__PURE__*/jsx(Copyable, {
value: value
})]
});
};
// Table cell link renderer - fires action for consumer to handle
var TableCellLinkRenderer = function TableCellLinkRenderer(_ref15) {
var _cell$text;
var cell = _ref15.cell;
var onActionClick = useGenUIAction();
var linkNode = cell !== null && cell !== void 0 && cell.action ? /*#__PURE__*/jsx(Link, {
variant: "button",
onClick: function onClick() {
if (onActionClick && cell.action) {
onActionClick(cell.action);
}
},
children: (_cell$text = cell.text) !== null && _cell$text !== void 0 ? _cell$text : ''
}) : /*#__PURE__*/jsx(Text, {
size: "medium",
children: cell === null || cell === void 0 ? void 0 : cell.text
});
if (cell !== null && cell !== void 0 && cell.copyable && cell !== null && cell !== void 0 && cell.text) {
return /*#__PURE__*/jsxs(Box, {
display: "flex",
alignItems: "center",
gap: "spacing.2",
children: [linkNode, /*#__PURE__*/jsx(Copyable, {
value: cell.text
})]
});
}
return linkNode;
};
// Helper to render a single table cell based on its component type
var RenderTableCellContent = function RenderTableCellContent(_ref16) {
var cell = _ref16.cell;
if (!cell) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
// Handle incomplete cell object during streaming
if (!cell.component) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
// Handle component-based cell types
switch (cell.component) {
case 'TEXT':
{
var _cell$value;
if (cell.copyable && cell.value) {
return /*#__PURE__*/jsx(CopyableText, {
value: cell.value
});
}
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: (_cell$value = cell.value) !== null && _cell$value !== void 0 ? _cell$value : '-'
});
}
case 'AMOUNT':
{
var currency = cell.currency || 'INR';
// Ensure value is a valid number (handle streaming where value might be string/undefined)
var numValue = typeof cell.value === 'string' ? parseFloat(cell.value) : cell.value;
if (typeof numValue !== 'number' || isNaN(numValue)) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
return /*#__PURE__*/jsx(Amount, {
value: numValue,
currency: currency,
size: "medium"
});
}
case 'INDICATOR':
{
// Handle streaming where value might be incomplete
if (!cell.value) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
var indicatorColor = getValidFeedbackColor(cell.color, 'neutral');
return /*#__PURE__*/jsxs(Box, {
display: "flex",
alignItems: "center",
gap: "spacing.3",
children: [/*#__PURE__*/jsx(Indicator, {
accessibilityLabel: cell.value,
color: indicatorColor
}), /*#__PURE__*/jsx(Text, {
size: "medium",
children: cell.value
})]
});
}
case 'BADGE':
{
var badgeColor = getValidFeedbackColor(cell.color, 'neutral');
return /*#__PURE__*/jsx(Badge, {
color: badgeColor,
emphasis: "subtle",
children: cell.value || '-'
});
}
case 'DATE':
{
// Handle streaming where value might be incomplete
if (!cell.value) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
// Parse the date value using dayjs
var dateValue = dayjs(cell.value);
// Check if date is valid
if (!dateValue.isValid()) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: cell.value
});
}
// Use custom dateFormat if provided, otherwise use default
// Format tokens: https://day.js.org/docs/en/display/format
var defaultFormat = 'DD MMM YYYY, HH:mm';
var formatted = dateValue.format(cell.dateFormat || defaultFormat);
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: formatted
});
}
case 'LINK':
{
// Handle streaming where text might be incomplete
if (!cell.text) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
return /*#__PURE__*/jsx(TableCellLinkRenderer, {
cell: cell
});
}
default:
// Handle unknown component types during streaming
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: String(cell.value || '-')
});
}
};
// Icon mapping for row actions
var rowActionIconMap = {
check: CheckIcon,
close: CloseIcon,
edit: EditIcon,
"delete": TrashIcon,
download: DownloadIcon,
view: EyeIcon,
copy: CopyIcon
};
// Separate component for table row hover actions to avoid render issues
var TableRowHoverActions = function TableRowHoverActions(_ref17) {
var rowActions = _ref17.rowActions,
rowIndex = _ref17.rowIndex,
rowData = _ref17.rowData;
var onActionClick = useGenUIAction();
var createActionEvent = function createActionEvent(eventName) {
return {
type: 'TABLE_ROW_ACTION',
eventName: eventName,
data: {
rowIndex: rowIndex,
rowData: rowData
}
};
};
return /*#__PURE__*/jsx(Fragment, {
children: rowActions.map(function (rowAction, index) {
// Guard: Skip incomplete button actions during streaming
if (rowAction.type === 'BUTTON') {
if (!rowAction.text) return null;
return /*#__PURE__*/jsx(Button, {
variant: "tertiary",
size: "xsmall",
onClick: function onClick() {
var _rowAction$action;
if ((_rowAction$action = rowAction.action) !== null && _rowAction$action !== void 0 && _rowAction$action.eventName && onActionClick) {
onActionClick(createActionEvent(rowAction.action.eventName));
}
},
children: rowAction.text
}, index);
}
// Guard: Skip incomplete icon button actions during streaming
if (rowAction.type === 'ICON_BUTTON') {
var IconComponent = rowAction.icon ? rowActionIconMap[rowAction.icon] : null;
if (!IconComponent || !rowAction.accessibilityLabel) return null;
return /*#__PURE__*/jsx(IconButton, {
icon: IconComponent,
size: "medium",
emphasis: "intense",
accessibilityLabel: rowAction.accessibilityLabel,
onClick: function onClick() {
var _rowAction$action2;
if ((_rowAction$action2 = rowAction.action) !== null && _rowAction$action2 !== void 0 && _rowAction$action2.eventName && onActionClick) {
onActionClick(createActionEvent(rowAction.action.eventName));
}
}
}, index);
}
return null;
})
});
};
// Produces column widths using minmax(max-content, 1fr) so columns:
// 1. Never shrink below their content width (no text wrapping)
// 2. Expand proportionally to fill available width when space permits
var calculateColumnWidths = function calculateColumnWidths(headers, _rows) {
return headers.map(function () {
return 'minmax(max-content, 1fr)';
});
};
var RenderTableComponent = /*#__PURE__*/memo(function (_ref18) {
var headers = _ref18.headers,
rows = _ref18.rows,
rowActions = _ref18.rowActions;
var columnWidths = useMemo(function () {
return headers && rows ? calculateColumnWidths(headers, rows) : [];
}, [headers, rows]);
if (!headers || !rows || headers.length === 0 || rows.length === 0) {
return null;
}
// Transform rows into table data format with id
var tableData = {
nodes: rows.map(function (row, index) {
return {
id: "row-".concat(index),
cells: row
};
})
};
return /*#__PURE__*/jsx(Box, {
display: "flex",
flexDirection: "column",
gap: "spacing.3",
children: /*#__PURE__*/jsx(Table, {
data: tableData,
backgroundColor: "transparent",
rowDensity: "compact",
gridTemplateColumns: columnWidths.join(' '),
children: function children(data) {
return /*#__PURE__*/jsxs(Fragment, {
children: [/*#__PURE__*/jsx(TableHeader, {
children: /*#__PURE__*/jsx(TableHeaderRow, {
children: headers.map(function (header, index) {
return /*#__PURE__*/jsx(TableHeaderCell, {
children: header
}, index);
})
})
}), /*#__PURE__*/jsx(TableBody, {
children: data.map(function (item, rowIndex) {
return /*#__PURE__*/jsx(TableRow, {
item: item
// eslint-disable-next-line @typescript-eslint/no-empty-function
,
onHover: rowActions && rowActions.length > 0 ? function () {} : undefined,
hoverActions: rowActions && rowActions.length > 0 ? /*#__PURE__*/jsx(TableRowHoverActions, {
rowActions: rowActions,
rowIndex: rowIndex,
rowData: item.cells
}) : undefined,
children: item.cells.map(function (cell, cellIndex) {
return /*#__PURE__*/jsx(TableCell, {
children: /*#__PURE__*/jsx(RenderTableCellContent, {
cell: cell
})
}, cellIndex);
})
}, rowIndex);
})
})]
});
}
})
});
});
var RenderCardComponent = /*#__PURE__*/memo(function (_ref19) {
var title = _ref19.title,
description = _ref19.description,
footer = _ref19.footer,
children = _ref19.children;
var hasHeader = title || description;
return /*#__PURE__*/jsx(Box, {
height: "100%",
display: "flex",
flexDirection: "column",
gap: "spacing.3",
children: /*#__PURE__*/jsxs(Card, {
width: "100%",
height: "100%",
padding: "spacing.7",
children: [hasHeader ? /*#__PURE__*/jsx(CardHeader, {
children: /*#__PURE__*/jsx(CardHeaderLeading, {
title: title || '',
subtitle: description || ''
})
}) : null, children && children.length > 0 ? /*#__PURE__*/jsx(CardBody, {
height: "100%",
children: /*#__PURE__*/jsx(Box, {
display: "flex",
flexDirection: "column",
gap: "spacing.5",
children: children.map(function (child, index) {
return /*#__PURE__*/jsx(GenUIComponentRenderer, {
component: child,
index: index
}, index);
})
})
}) : null, footer ? /*#__PURE__*/jsx(CardFooter, {
showDivider: true,
children: /*#__PURE__*/jsx(CardFooterLeading, {
subtitle: footer
})
}) : null]
})
});
});
var RenderBadgeComponent = /*#__PURE__*/memo(function (_ref20) {
var text = _ref20.text,
color = _ref20.color;
if (!text) return null;
return /*#__PURE__*/jsx(Badge, {
color: color || 'neutral',
emphasis: "subtle",
children: text
});
});
var RenderSpacerComponent = /*#__PURE__*/memo(function (_ref21) {
var size = _ref21.size;
if (!size) return null;
var sizeMap = {
small: 'spacing.2',
medium: 'spacing.4',
large: 'spacing.5'
};
return /*#__PURE__*/jsx(Box, {
height: sizeMap[size],
width: sizeMap[size]
});
});
var RenderDividerComponent = /*#__PURE__*/memo(function (_ref22) {
var _ref22$orientation = _ref22.orientation,
orientation = _ref22$orientation === void 0 ? 'horizontal' : _ref22$orientation;
return /*#__PURE__*/jsx(Box, {
display: "flex",
paddingY: "spacing.4",
children: /*#__PURE__*/jsx(Divider, _objectSpread({
orientation: orientation
}, orientation === 'vertical' ? {
height: '24px'
} : {}))
});
});
var RenderStackComponent = /*#__PURE__*/memo(function (_ref23) {
var _ref23$direction = _ref23.direction,
direction = _ref23$direction === void 0 ? 'vertical' : _ref23$direction,
gap = _ref23.gap,
children = _ref23.children;
if (!children || children.length === 0) {
return null;
}
var sizeMap = {
small: 'spacing.2',
medium: 'spacing.4',
large: 'spacing.5'
};
return /*#__PURE__*/jsx(Box, {
display: "flex",
width: "100%",
paddingY: "spacing.3",
flexDirection: direction === 'vertical' ? 'column' : 'row',
gap: sizeMap[gap !== null && gap !== void 0 ? gap : 'small'],
children: children.map(function (child, index) {
return /*#__PURE__*/jsx(GenUIComponentRenderer, {
component: child,
index: index
}, index);
})
});
});
var RenderGridComponent = /*#__PURE__*/memo(function (_ref24) {
var columns = _ref24.columns,
gap = _ref24.gap,
children = _ref24.children;
if (!columns || !children || children.length === 0) {
return null;
}
var sizeMap = {
small: 'spacing.2',
medium: 'spacing.4',
large: 'spacing.5'
};
return /*#__PURE__*/jsx(Box, {
display: "grid",
width: "100%",
marginY: "spacing.3",
gridTemplateColumns: "repeat(".concat(columns, ", 1fr)"),
gap: sizeMap[gap !== null && gap !== void 0 ? gap : 'small'],
children: children.map(function (child, index) {
return /*#__PURE__*/jsx(GenUIComponentRenderer, {
component: child,
index: index
}, index);
})
});
});
var RenderInfoGroupComponent = /*#__PURE__*/memo(function (_ref25) {
var items = _ref25.items;
if (!items || items.length === 0) {
return null;
}
// Filter out invalid items during streaming
// Filters out: null, undefined, and empty string children
var validItems = items.filter(function (item) {
var _item$value, _item$key;
var children = (_item$value = item.value) === null || _item$value === void 0 ? void 0 : _item$value.children;
if (!((_item$key = item.key) !== null && _item$key !== void 0 && _item$key.children) || children == null) return false;
return typeof children !== 'string' || children !== '';
});
if (validItems.length === 0) {
return null;
}
return /*#__PURE__*/jsx(InfoGroup, {
marginY: "spacing.3",
itemOrientation: "horizontal",
size: "medium",
valueAlign: "left",
children: validItems.map(function (item, index) {
var _item$key4, _item$key5;
var value = item.value;
var isString = typeof value.children === 'string';
if (isString) {
var _item$key2, _item$key3;
return /*#__PURE__*/jsxs(InfoItem, {
children: [/*#__PURE__*/jsx(InfoItemKey, {
helpText: item === null || item === void 0 || (_item$key2 = item.key) === null || _item$key2 === void 0 ? void 0 : _item$key2.helpText,
children: item === null || item === void 0 || (_item$key3 = item.key) === null || _item$key3 === void 0 ? void 0 : _item$key3.children
}), /*#__PURE__*/jsx(InfoItemValue, {
helpText: value.helpText,
children: value.children
})]
}, index);
}
return /*#__PURE__*/jsxs(InfoItem, {
children: [/*#__PURE__*/jsx(InfoItemKey, {
helpText: item === null || item === void 0 || (_item$key4 = item.key) === null || _item$key4 === void 0 ? void 0 : _item$key4.helpText,
children: item === null || item === void 0 || (_item$key5 = item.key) === null || _item$key5 === void 0 ? void 0 : _item$key5.children
}), /*#__PURE__*/jsx(InfoItemValue, {
helpText: value.helpText,
children: /*#__PURE__*/jsx(GenUIComponentRenderer, {
component: value.children,
index: index
})
})]
}, index);
})
});
});
var RenderButtonComponent = /*#__PURE__*/memo(function (_ref26) {
var text = _ref26.text,
action = _ref26.action;
var onActionClick = useGenUIAction();
if (!text) return null;
var handleClick = function handleClick() {
if (action && onActionClick) {
onActionClick(action);
}
};
return /*#__PURE__*/jsx(Button, {
marginTop: "spacing.2",
variant: "tertiary",
onClick: handleClick,
children: text
});
});
var RenderLinkComponent = /*#__PURE__*/memo(function (_ref27) {
var text = _ref27.text,
action = _ref27.action;
var onActionClick = useGenUIAction();
if (!text) return null;
if (!action) {
return /*#__PURE__*/jsx(Box, {
marginBottom: "spacing.2",
children: /*#__PURE__*/jsx(Text, {
size: "medium",
children: text
})
});
}
return /*#__PURE__*/jsx(Box, {
marginBottom: "spacing.2",
children: /*#__PURE__*/jsx(Link, {
variant: "button",
onClick: function onClick() {
if (onActionClick) {
onActionClick(action);
}
},
children: text
})
});
});
var RenderAlertComponent = /*#__PURE__*/memo(function (_ref28) {
var _actions$primary, _actions$secondary;
var title = _ref28.title,
description = _ref28.description,
_ref28$color = _ref28.color,
color = _ref28$color === void 0 ? 'neutral' : _ref28$color,
actions = _ref28.actions;
var onActionClick = useGenUIAction();
if (!title && !description) return null;
// Validate color during streaming - show placeholder for invalid colors
var validColor = getValidFeedbackColor(color);
if (validColor !== color) {
return /*#__PURE__*/jsx(Box, {
marginY: "spacing.3",
width: "100%"
});
}
var iconMap = {
information: InfoIcon,
negative: AlertTriangleIcon,
neutral: InfoIcon,
notice: InfoIcon,
positive: CheckCircleIcon
};
var Icon = iconMap[validColor];
return /*#__PURE__*/jsx(Alert, {
emphasis: "subtle",
title: title,
icon: Icon,
description: description,
color: validColor,
isDismissible: false,
marginY: "spacing.4",
actions: {
primary: actions !== null && actions !== void 0 && (_actions$primary = actions.primary) !== null && _actions$primary !== void 0 && _actions$primary.text ? {
text: actions.primary.text,
onClick: function onClick() {
var _actions$primary2;
if ((_actions$primary2 = actions.primary) !== null && _actions$primary2 !== void 0 && _actions$primary2.action && onActionClick) {
onActionClick(actions.primary.action);
}
}
} : undefined,
secondary: actions !== null && actions !== void 0 && (_actions$secondary = actions.secondary) !== null && _actions$secondary !== void 0 && _actions$secondary.text ? {
text: actions.secondary.text,
onClick: function onClick() {
var _actions$secondary2;
if ((_actions$secondary2 = actions.secondary) !== null && _actions$secondary2 !== void 0 && _actions$secondary2.action && onActionClick) {
onActionClick(actions.secondary.action);
}
}
} : undefined
}
});
});
var RenderAmountComponent = /*#__PURE__*/memo(function (_ref29) {
var value = _ref29.value,
currency = _ref29.currency;
var resolvedCurrency = currency || 'INR';
var numValue = typeof value === 'string' ? parseFloat(value) : value;
if (typeof numValue !== 'number' || isNaN(numValue)) {
return /*#__PURE__*/jsx(Text, {
size: "medium",
children: "-"
});
}
return /*#__PURE__*/jsx(Amount, {
value: numValue,
currency: resolvedCurrency
});
});
// Alias for internal use in built-in renderers
var GenUIComponentRenderer = ComponentRenderer;
/**
* Default registry of built-in components
*/
var createBuiltInRegistry = function createBuiltInRegistry() {
var _ref30;
return _ref30 = {}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_ref30, ComponentType.TEXT, {
renderer: RenderTextComponent
}), ComponentType.CHART, {
renderer: RenderChartComponent
}), ComponentType.TABLE, {
renderer: RenderTableComponent
}), ComponentType.CARD, {
renderer: RenderCardComponent
}), ComponentType.BADGE, {
renderer: RenderBadgeComponent
}), ComponentType.SPACER, {
renderer: RenderSpacerComponent
}), ComponentType.DIVIDER, {
renderer: RenderDividerComponent
}), ComponentType.STACK, {
renderer: RenderStackComponent
}), ComponentType.GRID, {
renderer: RenderGridComponent
}), ComponentType.INFO_GROUP, {
renderer: RenderInfoGroupComponent
}), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_ref30, ComponentType.BUTTON, {
renderer: RenderButtonComponent
}), ComponentType.LINK, {
renderer: RenderLinkComponent
}), ComponentType.ALERT, {
renderer: RenderAlertComponent
}), ComponentType.AMOUNT, {
renderer: RenderAmountComponent
});
};
export { ComponentType, createBuiltInRegistry };
//# sourceMappingURL=GenUIComponents.web.js.map