sevatech-library 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -8,21 +8,21 @@ var LinkIcon = require('@mui/icons-material/Link');
8
8
  var CheckIcon = require('@mui/icons-material/Check');
9
9
  var RemoveIcon = require('@mui/icons-material/Remove');
10
10
  var FiberManualRecordIcon = require('@mui/icons-material/FiberManualRecord');
11
- var DatePicker = require('@mui/x-date-pickers/DatePicker');
12
- var LocalizationProvider = require('@mui/x-date-pickers/LocalizationProvider');
13
- var AdapterDayjs = require('@mui/x-date-pickers/AdapterDayjs');
14
- var dayjs = require('dayjs');
15
- var AddIcon = require('@mui/icons-material/Add');
11
+ require('@mui/x-date-pickers/DatePicker');
12
+ require('@mui/x-date-pickers/LocalizationProvider');
13
+ require('@mui/x-date-pickers/AdapterDayjs');
14
+ require('dayjs');
15
+ require('@mui/icons-material/Add');
16
16
  var MuiTextField = require('@mui/material/TextField');
17
17
  var formik = require('formik');
18
18
  var Slider = require('react-slick');
19
19
  require('slick-carousel/slick/slick.css');
20
20
  require('slick-carousel/slick/slick-theme.css');
21
- var AttachMoneyIcon = require('@mui/icons-material/AttachMoney');
22
- var SearchIcon = require('@mui/icons-material/Search');
23
- var framerMotion = require('framer-motion');
24
- var CheckCircleIcon = require('@mui/icons-material/CheckCircle');
25
- var RefreshIcon = require('@mui/icons-material/Refresh');
21
+ require('@mui/icons-material/AttachMoney');
22
+ require('@mui/icons-material/Search');
23
+ require('framer-motion');
24
+ require('@mui/icons-material/CheckCircle');
25
+ require('@mui/icons-material/Refresh');
26
26
 
27
27
  const AVATAR_SIZES = {
28
28
  xs: 24,
@@ -233,7 +233,7 @@ const FONT_SIZE_ICON = {
233
233
  medium: '19px',
234
234
  small: '12px',
235
235
  };
236
- const FONT_SIZE_LOADING$1 = {
236
+ const FONT_SIZE_LOADING = {
237
237
  large: 40,
238
238
  medium: 22.5,
239
239
  small: 16,
@@ -274,7 +274,7 @@ var style_constant = /*#__PURE__*/Object.freeze({
274
274
  BORDER_RADIUS_ELEMENT_TAG: BORDER_RADIUS_ELEMENT_TAG,
275
275
  BORDER_RADIUS_ELEMENT_WRAPPER: BORDER_RADIUS_ELEMENT_WRAPPER,
276
276
  FONT_SIZE_ICON: FONT_SIZE_ICON,
277
- FONT_SIZE_LOADING: FONT_SIZE_LOADING$1,
277
+ FONT_SIZE_LOADING: FONT_SIZE_LOADING,
278
278
  GAP_ICON_CONTENT_BY_SIZE: GAP_ICON_CONTENT_BY_SIZE,
279
279
  HEIGHT_DEFAULT_TEXT_FIELD_BUTTON: HEIGHT_DEFAULT_TEXT_FIELD_BUTTON,
280
280
  HEIGHT_ELEMENT_OTHER: HEIGHT_ELEMENT_OTHER,
@@ -910,7 +910,7 @@ styles.styled(material.Stack)(() => ({
910
910
  alignItems: 'center',
911
911
  justifyContent: 'center',
912
912
  }));
913
- const StackRowAlignCenterJustEnd = styles.styled(material.Stack)(() => ({
913
+ styles.styled(material.Stack)(() => ({
914
914
  flexDirection: 'row',
915
915
  alignItems: 'center',
916
916
  justifyContent: 'flex-end',
@@ -923,7 +923,7 @@ styles.styled(material.Stack)(() => ({
923
923
  flexDirection: 'row',
924
924
  justifyContent: 'space-between',
925
925
  }));
926
- const StackRowAlignCenterJustBetween = styles.styled(material.Stack)(() => ({
926
+ styles.styled(material.Stack)(() => ({
927
927
  flexDirection: 'row',
928
928
  alignItems: 'center',
929
929
  justifyContent: 'space-between',
@@ -1202,60 +1202,6 @@ const AvatarUserComponent = ({ title, description, descriptionHref, onDescriptio
1202
1202
  }, children: [jsxRuntime.jsx(LinkIcon, { sx: { fontSize: 16, color: descriptionColor } }), description] }))] })] }));
1203
1203
  };
1204
1204
 
1205
- const SEPARATOR_URLS = {
1206
- '>': '/images/icon/chevron-right.svg',
1207
- '/': '/images/icon/slash-separator.svg',
1208
- };
1209
- const BreadcrumbsComponent = ({ items, separator = '>', maxItems = 5, idSelect, sx, sxItem, onChange, }) => {
1210
- // state
1211
- const [anchorEl, setAnchorEl] = React.useState(null);
1212
- const showCollapsed = items.length > maxItems;
1213
- const visibleItems = showCollapsed ? [items[0], ...items.slice(-2)] : items;
1214
- const collapsedItems = showCollapsed ? items.slice(1, -2) : [];
1215
- // function
1216
- const handleMenuOpen = (event) => {
1217
- setAnchorEl(event.currentTarget);
1218
- };
1219
- const handleMenuClose = () => {
1220
- setAnchorEl(null);
1221
- };
1222
- const renderItem = (item) => {
1223
- const isActive = item.id === idSelect;
1224
- return (jsxRuntime.jsxs(material.Link, { href: item.href, onClick: (e) => {
1225
- if (item.onClick) {
1226
- e.preventDefault();
1227
- item.onClick();
1228
- }
1229
- if (onChange) {
1230
- onChange(item.id);
1231
- }
1232
- }, sx: {
1233
- display: 'flex',
1234
- alignItems: 'center',
1235
- gap: GAP_ICON_CONTENT_BY_SIZE.medium,
1236
- ...TYPOGRAPHY_STYLES.textSm.semiBold,
1237
- color: isActive ? '#000000' : '#676E76',
1238
- cursor: 'pointer',
1239
- textDecoration: 'none',
1240
- ...sxItem,
1241
- }, children: [item.icon && jsxRuntime.jsx(IconElement, { icon: item.icon }), item.label] }, item.id));
1242
- };
1243
- const renderSeparator = () => jsxRuntime.jsx(ImageElement, { sx: { width: 14, height: 14 }, url: SEPARATOR_URLS[separator] });
1244
- return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(material.Breadcrumbs, { "aria-label": "breadcrumb", sx: { ...sx }, children: showCollapsed ? (jsxRuntime.jsxs(StackRowAlignCenter, { sx: { gap: GAP_ICON_CONTENT_BY_SIZE.small }, children: [renderItem(items[0]), renderSeparator(), jsxRuntime.jsx(material.IconButton, { size: "small", onClick: handleMenuOpen, sx: { p: 0, mt: 'auto' }, children: jsxRuntime.jsx(IconElement, { icon: "more_horiz" }) }), renderSeparator(), visibleItems.slice(1).map((item, idx) => (jsxRuntime.jsxs(React.Fragment, { children: [idx > 0 && renderSeparator(), renderItem(item)] }, item.id)))] })) : (items.map((item) => renderItem(item))) }), jsxRuntime.jsx(material.Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleMenuClose, disableScrollLock: true, children: collapsedItems.map((item) => {
1245
- const isActive = item.id === idSelect;
1246
- return (jsxRuntime.jsxs(material.MenuItem, { onClick: () => {
1247
- if (item.onClick) {
1248
- item.onClick();
1249
- }
1250
- handleMenuClose();
1251
- }, sx: {
1252
- gap: GAP_ICON_CONTENT_BY_SIZE.medium,
1253
- color: isActive ? '#0F766E' : '#111827',
1254
- bgcolor: isActive ? '#E0F2FE' : 'transparent',
1255
- }, children: [item.icon && jsxRuntime.jsx(IconElement, { icon: item.icon }), item.label] }, item.id));
1256
- }) })] }));
1257
- };
1258
-
1259
1205
  /** Shade values mapping */
1260
1206
  const SHADE_VALUES = {
1261
1207
  light: 100,
@@ -1396,17 +1342,6 @@ const ButtonComponent = ({ variant = 'solid', color = 'brand', shade = 'dark', s
1396
1342
  return (jsxRuntime.jsx(material.Button, { sx: { ...buttonSx, ...sx }, disabled: disabled || loading, fullWidth: fullWidth, startIcon: !isIconOnly ? prefixContent : undefined, endIcon: !isIconOnly ? suffixContent : undefined, ...props, children: isIconOnly ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [prefixContent, suffixContent] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [activeDot, children] })) }));
1397
1343
  };
1398
1344
 
1399
- const ButtonBarComponent = ({ layout, children, gap = 12, style }) => {
1400
- const containerStyle = {
1401
- display: 'flex',
1402
- flexDirection: layout === 'horizontal' ? 'row' : 'column',
1403
- gap: `${gap}px`,
1404
- alignItems: layout === 'horizontal' ? 'center' : 'stretch',
1405
- ...style,
1406
- };
1407
- return jsxRuntime.jsx("div", { style: containerStyle, children: children });
1408
- };
1409
-
1410
1345
  const CHECKBOX_COLORS = {
1411
1346
  default: {
1412
1347
  border: '#D0D5DD',
@@ -1530,356 +1465,44 @@ const CheckboxComponent = ({ checked = false, disabled = false, shape = 'square'
1530
1465
  }, children: title })] }));
1531
1466
  };
1532
1467
 
1533
- const CHIP_LABEL_PADDING = {
1534
- WITH_ICON_LEFT: '4px 8px 4px 4px',
1535
- WITH_ICON_RIGHT: '4px 4px 4px 8px',
1536
- NO_ICON: '4px 8px',
1537
- };
1538
- const CHIP_SIZE_CONFIG = {
1468
+ const CONTENT_TYPO = {
1539
1469
  small: {
1540
- height: 24,
1541
- fontSize: '12px',
1542
- borderRadius: '12px',
1543
- iconSize: '16px',
1470
+ titleFontSize: 14,
1471
+ titleFontWeight: 500,
1472
+ contentFontSize: 14,
1473
+ lineHeight: '20px',
1544
1474
  },
1545
1475
  medium: {
1546
- height: 32,
1547
- fontSize: '14px',
1548
- borderRadius: '16px',
1549
- iconSize: '18px',
1550
- },
1551
- large: {
1552
- height: 40,
1553
- fontSize: '16px',
1554
- borderRadius: '20px',
1555
- iconSize: '22px',
1476
+ titleFontSize: 16,
1477
+ titleFontWeight: 600,
1478
+ contentFontSize: 16,
1479
+ lineHeight: '24px',
1556
1480
  },
1557
1481
  };
1558
-
1559
- const getLabelPadding = (hasIcon, position) => {
1560
- if (!hasIcon)
1561
- return CHIP_LABEL_PADDING.NO_ICON;
1562
- return position === 'left' ? CHIP_LABEL_PADDING.WITH_ICON_LEFT : CHIP_LABEL_PADDING.WITH_ICON_RIGHT;
1563
- };
1564
- const ChipIcon = ({ icon, size, sxIcon }) => (jsxRuntime.jsx(material.Box, { sx: {
1565
- display: 'inline-flex',
1566
- alignItems: 'center',
1567
- justifyContent: 'center',
1568
- borderRadius: '50%',
1569
- padding: '2px',
1570
- '& svg': {
1571
- width: size,
1572
- height: size,
1573
- display: 'block',
1574
- },
1575
- ...sxIcon,
1576
- }, children: icon }));
1577
- const ChipComponent = ({ label, onAction, icon, disabled = false, clickable = true, sx, sxIcon, iconPosition = 'right', size = 'medium', }) => {
1578
- const sizeConfig = CHIP_SIZE_CONFIG[size];
1579
- const hasIcon = Boolean(icon);
1580
- return (jsxRuntime.jsx(material.Chip, { disabled: disabled, clickable: clickable, onClick: onAction, label: jsxRuntime.jsxs(StackRowAlignCenter, { sx: { gap: '8px' }, children: [hasIcon && iconPosition === 'left' && jsxRuntime.jsx(ChipIcon, { icon: icon, size: sizeConfig.iconSize, sxIcon: sxIcon }), jsxRuntime.jsx(material.Box, { component: "span", children: label }), hasIcon && iconPosition === 'right' && jsxRuntime.jsx(ChipIcon, { icon: icon, size: sizeConfig.iconSize, sxIcon: sxIcon })] }), sx: {
1581
- height: sizeConfig.height,
1582
- fontSize: sizeConfig.fontSize,
1583
- borderRadius: sizeConfig.borderRadius,
1584
- cursor: clickable ? 'pointer' : 'default',
1585
- backgroundColor: 'transparent',
1586
- border: '1px solid #D1D5DB',
1587
- '& .MuiChip-label': {
1588
- padding: getLabelPadding(hasIcon, iconPosition),
1589
- display: 'flex',
1590
- alignItems: 'center',
1591
- },
1592
- '& .MuiChip-label svg': {
1593
- flexShrink: 0,
1594
- color: '#4B5563',
1595
- },
1596
- '&.Mui-disabled': {
1597
- opacity: 0.5,
1598
- color: '#9E9E9E',
1599
- },
1600
- ...sx,
1601
- } }));
1602
- };
1603
-
1604
- const DateFieldComponent = ({ label = 'Label', placeholder = 'DD/MM/YYYY', value, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText = '', onChange, locale = 'vi', format = 'DD/MM/YYYY', sx, disablePastDates = false, ...props }) => {
1605
- // Convert string to Dayjs if needed
1606
- const dayjsValue = value && typeof value === 'string' ? dayjs(value, format) : value instanceof dayjs ? value : null;
1607
- const handleDateChange = (date) => {
1608
- onChange?.(date);
1609
- };
1610
- // Disable past dates function
1611
- const shouldDisableDate = (date) => {
1612
- if (!disablePastDates)
1613
- return false;
1614
- return date.isBefore(dayjs(), 'day');
1615
- };
1616
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
1617
- display: 'block',
1618
- ...TYPOGRAPHY.textFieldLabel,
1619
- color: COLOR_GRAY[800],
1620
- marginBottom: '4px',
1621
- }, children: label })), jsxRuntime.jsx(LocalizationProvider.LocalizationProvider, { dateAdapter: AdapterDayjs.AdapterDayjs, adapterLocale: locale, children: jsxRuntime.jsx(DatePicker.DatePicker, { value: dayjsValue, onChange: handleDateChange, disabled: disabled, format: format, shouldDisableDate: shouldDisableDate, slotProps: {
1622
- textField: {
1623
- placeholder,
1624
- error: error || false,
1625
- helperText: error ? errorMessage : helperText,
1626
- size: 'small',
1627
- fullWidth: true,
1628
- disabled: disabled,
1629
- },
1630
- openPickerButton: {
1631
- size: 'small',
1632
- },
1633
- }, slots: {
1634
- openPickerIcon: (props) => jsxRuntime.jsx(IconElement, { icon: "calendar_today", ...props }),
1635
- }, sx: {
1636
- width: '100%',
1637
- '& .MuiPickersInputBase-root': {
1638
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
1639
- '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
1640
- '&:hover fieldset': { borderColor: COLOR_NEUTRAL[400] },
1641
- '&.Mui-focused fieldset': { borderColor: COLOR_NEUTRAL[300], borderWidth: '2px' },
1642
- '&.Mui-focused': {
1643
- boxShadow: '0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px #FEE4E2',
1644
- },
1645
- '&.Mui-disabled': {
1646
- backgroundColor: COLOR_NEUTRAL[100],
1647
- '& fieldset': { borderColor: COLOR_NEUTRAL[200] },
1648
- '& input': { color: COLOR_NEUTRAL[400], WebkitTextFillColor: COLOR_NEUTRAL[400] },
1649
- },
1650
- '&.Mui-error fieldset': { borderColor: COLOR_ERROR[500] },
1651
- '&.Mui-error.Mui-focused fieldset': { borderColor: COLOR_ERROR[500], borderWidth: '2px' },
1652
- '&.Mui-error.Mui-focused': {
1653
- boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px rgba(255, 66, 79, 0.15)`,
1654
- },
1655
- ...(success && {
1656
- '&.Mui-focused': {
1657
- boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
1658
- },
1659
- }),
1660
- },
1661
- '& .MuiInputBase-input': {
1662
- ...TYPOGRAPHY.text14Regular,
1663
- padding: '12px 8px',
1664
- color: COLOR_GRAY[900],
1665
- '&::placeholder': { color: COLOR_NEUTRAL[400], opacity: 0.7 },
1666
- },
1667
- }, ...props }) }), success && !error && successMessage && (jsxRuntime.jsx(material.Typography, { sx: {
1668
- ...TYPOGRAPHY.textFieldHelper,
1669
- color: COLOR_SUCCESS[500],
1670
- marginTop: '4px',
1671
- }, children: successMessage }))] }));
1672
- };
1673
-
1674
- const DateRangePickerComponent = ({ label = 'Date Range', fromDate, toDate, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText = '', onChange, locale = 'vi', format = 'DD/MM/YYYY', minDate, maxDate, sx, disablePastDates = false, ...props }) => {
1675
- // Convert string to Dayjs if needed
1676
- const dayjsFromDate = fromDate && typeof fromDate === 'string' ? dayjs(fromDate, format) : fromDate instanceof dayjs ? fromDate : null;
1677
- const dayjsToDate = toDate && typeof toDate === 'string' ? dayjs(toDate, format) : toDate instanceof dayjs ? toDate : null;
1678
- // State for picker
1679
- const [pickerOpen, setPickerOpen] = React.useState(false);
1680
- const [selectingPhase, setSelectingPhase] = React.useState('from');
1681
- const inputRef = React.useRef(null);
1682
- // Disable past dates function
1683
- const shouldDisableDate = (date) => {
1684
- if (!disablePastDates)
1685
- return false;
1686
- return date.isBefore(dayjs(), 'day');
1687
- };
1688
- const handleInputClick = () => {
1689
- setPickerOpen(true);
1690
- // Only reset to 'from' if both dates are empty (fresh start)
1691
- if (!dayjsFromDate && !dayjsToDate) {
1692
- setSelectingPhase('from');
1693
- }
1694
- };
1695
- const handleDateChange = (date) => {
1696
- if (selectingPhase === 'from') {
1697
- onChange?.([date, dayjsToDate]);
1698
- if (date) {
1699
- // Auto switch to toDate selection
1700
- setSelectingPhase('to');
1701
- }
1702
- }
1703
- else {
1704
- onChange?.([dayjsFromDate, date]);
1705
- // Keep picker open for user to potentially change toDate
1482
+ const CheckboxContentComponent = ({ checked = false, disabled = false, title, content, size = 'medium', variant = 'filled', shape = 'square', iconType = 'check', onChange, sxCheckbox, sxLabel, sx, }) => {
1483
+ const typo = CONTENT_TYPO[size];
1484
+ const handleToggle = () => {
1485
+ if (!disabled) {
1486
+ onChange?.(!checked);
1706
1487
  }
1707
1488
  };
1708
- return (jsxRuntime.jsx(LocalizationProvider.LocalizationProvider, { dateAdapter: AdapterDayjs.AdapterDayjs, adapterLocale: locale, children: jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
1709
- display: 'block',
1710
- ...TYPOGRAPHY.textFieldLabel,
1711
- color: COLOR_GRAY[800],
1712
- marginBottom: '4px',
1713
- }, children: label })), jsxRuntime.jsxs(material.Box, { ref: inputRef, onClick: !disabled ? handleInputClick : undefined, sx: {
1714
- display: 'flex',
1715
- alignItems: 'center',
1716
- gap: '12px',
1717
- padding: '12px 8px',
1718
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
1719
- border: `1px solid ${error ? COLOR_ERROR[500] : COLOR_NEUTRAL[300]}`,
1720
- backgroundColor: disabled ? COLOR_NEUTRAL[100] : 'white',
1721
- cursor: disabled ? 'default' : 'pointer',
1722
- transition: 'all 0.2s ease',
1723
- ...(disabled
1724
- ? {}
1725
- : {
1726
- '&:hover': {
1727
- borderColor: error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
1728
- },
1729
- }),
1730
- }, children: [jsxRuntime.jsxs(material.Typography, { sx: {
1731
- flex: 1,
1732
- ...TYPOGRAPHY.text14Regular,
1733
- color: dayjsFromDate || dayjsToDate ? COLOR_GRAY[900] : COLOR_NEUTRAL[400],
1734
- }, children: [dayjsFromDate ? dayjsFromDate.format(format) : format, " \u2192", ' ', dayjsToDate ? dayjsToDate.format(format) : format] }), jsxRuntime.jsx(material.Box, { sx: {
1735
- display: 'flex',
1736
- alignItems: 'center',
1737
- justifyContent: 'center',
1738
- width: '24px',
1739
- height: '24px',
1740
- color: COLOR_NEUTRAL[400],
1741
- }, children: jsxRuntime.jsx(IconElement, { icon: "calendar_today" }) })] }), jsxRuntime.jsx(DatePicker.DatePicker, { open: pickerOpen, onOpen: () => setPickerOpen(true), onClose: () => setPickerOpen(false), value: selectingPhase === 'from' ? dayjsFromDate : dayjsToDate, onChange: handleDateChange, disabled: disabled, format: format, minDate: selectingPhase === 'from' ? minDate : dayjsFromDate || minDate, maxDate: selectingPhase === 'to' ? maxDate : dayjsToDate || maxDate, shouldDisableDate: shouldDisableDate, slotProps: {
1742
- textField: {
1743
- hidden: true,
1744
- size: 'small',
1745
- sx: {
1746
- display: 'none',
1747
- },
1748
- },
1749
- popper: {
1750
- anchorEl: inputRef.current,
1751
- placement: 'bottom-start',
1752
- },
1753
- } }), helperText && !error && !success && (jsxRuntime.jsx(material.Typography, { sx: {
1754
- ...TYPOGRAPHY.textFieldHelper,
1755
- color: COLOR_NEUTRAL[400],
1756
- marginTop: '4px',
1757
- }, children: helperText })), error && errorMessage && (jsxRuntime.jsx(material.Typography, { sx: {
1758
- ...TYPOGRAPHY.textFieldHelper,
1759
- color: COLOR_ERROR[500],
1760
- marginTop: '4px',
1761
- }, children: errorMessage })), success && !error && successMessage && (jsxRuntime.jsx(material.Typography, { sx: {
1762
- ...TYPOGRAPHY.textFieldHelper,
1763
- color: COLOR_SUCCESS[500],
1764
- marginTop: '4px',
1765
- }, children: successMessage }))] }) }));
1766
- };
1767
-
1768
- const DropdownFieldComponent = ({ label = '', placeholder = 'Select option', value = null, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, onChange, helperText = '', options = [], checkIconColor = COLOR_ACCENT[900], sx, }) => {
1769
- const borderRadiusValue = borderRadius === 'max' ? '100px' : `${borderRadius}px`;
1770
- const selectedOption = React.useMemo(() => options.find((opt) => opt.value === value), [options, value]);
1771
- const getHelperText = React.useCallback(() => {
1772
- if (error && errorMessage)
1773
- return errorMessage;
1774
- if (success && successMessage)
1775
- return successMessage;
1776
- if (helperText)
1777
- return helperText;
1778
- return '';
1779
- }, [error, errorMessage, success, successMessage, helperText]);
1780
- const getHelperTextColor = React.useCallback(() => {
1781
- if (error)
1782
- return COLOR_ERROR[500];
1783
- if (success)
1784
- return COLOR_SUCCESS[500];
1785
- return COLOR_NEUTRAL[400];
1786
- }, [error, success]);
1787
- const getBorderColor = React.useCallback(() => {
1788
- if (error)
1789
- return COLOR_ERROR[500];
1790
- if (success)
1791
- return COLOR_SUCCESS[500];
1792
- return COLOR_NEUTRAL[300];
1793
- }, [error, success]);
1794
- const selectSx = React.useMemo(() => ({
1795
- '& .MuiOutlinedInput-root': {
1796
- borderRadius: borderRadiusValue,
1797
- backgroundColor: disabled ? COLOR_NEUTRAL[100] : 'white',
1798
- transition: 'all 0.2s ease',
1799
- '& fieldset': {
1800
- borderColor: getBorderColor(),
1801
- },
1802
- '&:hover fieldset': {
1803
- borderColor: disabled ? getBorderColor() : error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
1804
- },
1805
- '&.Mui-focused fieldset': {
1806
- borderColor: error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
1807
- borderWidth: '1.5px',
1808
- },
1809
- },
1810
- '& .MuiOutlinedInput-input': {
1811
- padding: '12px 14px',
1812
- color: COLOR_GRAY[900],
1813
- '&::placeholder': {
1814
- color: COLOR_NEUTRAL[400],
1815
- opacity: 1,
1816
- },
1817
- },
1818
- }), [borderRadiusValue, disabled, error, getBorderColor]);
1819
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
1820
- display: 'block',
1821
- ...TYPOGRAPHY.textFieldLabel,
1822
- color: COLOR_GRAY[800],
1823
- marginBottom: '4px',
1824
- }, children: label })), jsxRuntime.jsx(material.Select, { fullWidth: true, value: value || '', onChange: (e) => {
1825
- const selectedValue = e.target.value;
1826
- const matchedOption = options.find((opt) => String(opt.value) === String(selectedValue));
1827
- if (matchedOption) {
1828
- onChange?.(matchedOption.value);
1829
- }
1830
- }, disabled: disabled, displayEmpty: true, MenuProps: {
1831
- disableScrollLock: true,
1832
- }, renderValue: () => {
1833
- if (!value) {
1834
- return (jsxRuntime.jsx(material.Box, { sx: { color: COLOR_NEUTRAL[400], display: 'flex', alignItems: 'center', gap: '8px' }, children: placeholder }));
1835
- }
1836
- return (jsxRuntime.jsxs(material.Box, { sx: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [selectedOption?.statusIndicator && (jsxRuntime.jsx(material.Box, { sx: {
1837
- width: '8px',
1838
- height: '8px',
1839
- borderRadius: '50%',
1840
- backgroundColor: '#4CAF50',
1841
- } })), selectedOption?.avatar && (jsxRuntime.jsx(material.Avatar, { src: selectedOption.avatar, sx: {
1842
- width: '24px',
1843
- height: '24px',
1844
- fontSize: '12px',
1845
- } })), selectedOption?.icon && jsxRuntime.jsx(IconElement, { icon: selectedOption.icon }), jsxRuntime.jsx(material.Typography, { sx: { color: COLOR_GRAY[900], ...TYPOGRAPHY.text14Regular }, children: selectedOption?.label })] }));
1846
- }, sx: selectSx, children: options.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.value, children: jsxRuntime.jsxs(material.Box, { sx: { display: 'flex', alignItems: 'center', gap: '8px', width: '100%' }, children: [option.statusIndicator && (jsxRuntime.jsx(material.Box, { sx: {
1847
- width: '8px',
1848
- height: '8px',
1849
- borderRadius: '50%',
1850
- backgroundColor: '#4CAF50',
1851
- } })), option.avatar && (jsxRuntime.jsx(material.Avatar, { src: option.avatar, sx: {
1852
- width: '24px',
1853
- height: '24px',
1854
- fontSize: '12px',
1855
- } })), option.icon && jsxRuntime.jsx(IconElement, { icon: option.icon }), jsxRuntime.jsx(material.Typography, { sx: { color: COLOR_GRAY[900], ...TYPOGRAPHY.text14Regular }, children: option.label }), value === option.value && (jsxRuntime.jsx(material.Box, { sx: { marginLeft: 'auto', display: 'flex', alignItems: 'center' }, children: jsxRuntime.jsx(IconElement, { icon: "check", sx: { color: checkIconColor } }) }))] }) }, option.value))) }), getHelperText() && (jsxRuntime.jsx(material.Box, { sx: {
1856
- color: getHelperTextColor(),
1857
- marginTop: '4px',
1858
- ...TYPOGRAPHY.textFieldHelper,
1859
- }, children: getHelperText() }))] }));
1860
- };
1861
-
1862
- const BACKGROUND_COLOR_GRID = '#FFFFFF';
1863
- const COLOR_CONTENT_GRID = '#27272A';
1864
- const BORDER_RADIUS_GRID_CONTAINER = 8;
1865
- const BORDER_RADIUS_GRID = 4;
1866
- const GridComponent = ({ sx = {}, sxContainer = {}, content = 'Grids', children, }) => {
1867
- return (jsxRuntime.jsxs(material.Stack, { sx: {
1868
- bgcolor: BACKGROUND_COLOR_GRID,
1869
- p: BORDER_RADIUS_GRID_CONTAINER,
1870
- borderRadius: BORDER_RADIUS_GRID_CONTAINER,
1871
- gap: BORDER_RADIUS_GRID,
1489
+ return (jsxRuntime.jsxs(material.Box, { onClick: handleToggle, sx: {
1490
+ display: 'flex',
1491
+ alignItems: 'flex-start',
1492
+ gap: '12px',
1493
+ cursor: disabled ? 'not-allowed' : 'pointer',
1494
+ width: 'fit-content',
1872
1495
  ...sx,
1873
- }, children: [content && (jsxRuntime.jsx(material.Typography, { sx: {
1874
- color: COLOR_CONTENT_GRID,
1875
- ...TYPOGRAPHY_STYLES.lg.bold,
1876
- }, children: content })), jsxRuntime.jsx(material.Container, { maxWidth: false, sx: {
1877
- bgcolor: BACKGROUND_COLOR_GRID,
1878
- borderRadius: BORDER_RADIUS_GRID,
1879
- minHeight: 400,
1880
- boxShadow: '0 0 8px -4px rgba(16, 24, 40, 0.3)',
1881
- ...sxContainer,
1882
- }, children: children })] }));
1496
+ }, children: [jsxRuntime.jsx(CheckboxComponent, { checked: checked, disabled: disabled, size: size, variant: variant, shape: shape, iconType: iconType, onChange: onChange, sxCheckbox: sxCheckbox }), jsxRuntime.jsxs(material.Box, { children: [jsxRuntime.jsx(material.Typography, { fontSize: typo.titleFontSize, fontWeight: typo.titleFontWeight, lineHeight: typo.lineHeight, sx: {
1497
+ userSelect: 'none',
1498
+ ...(disabled && { opacity: 0.6 }),
1499
+ ...sxLabel,
1500
+ }, children: title }), content && (jsxRuntime.jsx(material.Typography, { fontSize: typo.contentFontSize, lineHeight: typo.lineHeight, sx: {
1501
+ mt: '4px',
1502
+ userSelect: 'none',
1503
+ ...(disabled && { opacity: 0.6 }),
1504
+ ...sxLabel,
1505
+ }, children: content }))] })] }));
1883
1506
  };
1884
1507
 
1885
1508
  var BorderRadius;
@@ -1902,159 +1525,6 @@ var ButtonSize;
1902
1525
  ButtonSize[ButtonSize["SMALL"] = 32] = "SMALL";
1903
1526
  ButtonSize[ButtonSize["MEDIUM"] = 40] = "MEDIUM";
1904
1527
  })(ButtonSize || (ButtonSize = {}));
1905
- const Colors = {
1906
- BORDER_COLOR_BUTTON: '#07554B',
1907
- BORDER_COLOR_DISABLE: '#0000000D',
1908
- HOVER_BG_COLOR: 'rgba(7, 85, 75, 0.04)',
1909
- BACKGROUND_COLOR: '#FFFFFF',
1910
- TEXT_COLOR_READONLY: '#27272A',
1911
- };
1912
- const FONT_SIZE_LOADING = {
1913
- large: 40,
1914
- };
1915
- const BORDER_TEXT_FIELD_LOADING = 20;
1916
-
1917
- const InputStepperSkeleton = ({ orientation, buttonShape }) => {
1918
- return (jsxRuntime.jsxs(material.Box, { display: "inline-flex", flexDirection: orientation === Orientation.HORIZONTAL ? 'row' : 'column', alignItems: "center", gap: 1, children: [jsxRuntime.jsx(material.Skeleton, { variant: buttonShape === ShapeType.CIRCLE ? 'circular' : 'rectangular', width: FONT_SIZE_LOADING.large, height: FONT_SIZE_LOADING.large, sx: {
1919
- borderRadius: buttonShape === ShapeType.CIRCLE ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
1920
- maxHeight: FONT_SIZE_LOADING.large,
1921
- } }), jsxRuntime.jsx(material.Skeleton, { width: FONT_SIZE_LOADING.large, sx: {
1922
- borderRadius: `${BORDER_TEXT_FIELD_LOADING}px`,
1923
- } }), jsxRuntime.jsx(material.Skeleton, { variant: buttonShape === ShapeType.CIRCLE ? 'circular' : 'rectangular', width: FONT_SIZE_LOADING.large, height: FONT_SIZE_LOADING.large, sx: {
1924
- borderRadius: buttonShape === ShapeType.CIRCLE ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
1925
- maxHeight: FONT_SIZE_LOADING.large,
1926
- } })] }));
1927
- };
1928
-
1929
- const InputStepperComponent = ({ value: controlledValue, onChange, min, max, step = 1, disabled = false, readOnly = false, orientation = Orientation.HORIZONTAL, loading = false, sx = {}, sxTextField = {}, // style cho input chứa value
1930
- sxButton = {}, // style cho 2 button
1931
- buttonShape = ShapeType.SQUARE, buttonColor, textFieldShape = ShapeType.SQUARE, decrementIcon = jsxRuntime.jsx(RemoveIcon, {}), incrementIcon = jsxRuntime.jsx(AddIcon, {}), }) => {
1932
- const [internalValue, setInternalValue] = React.useState(controlledValue ?? min ?? 0);
1933
- const value = controlledValue !== undefined ? controlledValue : internalValue;
1934
- const updateValue = (newValue) => {
1935
- let finalValue = newValue;
1936
- if (min !== undefined && newValue < min)
1937
- finalValue = min;
1938
- if (max !== undefined && newValue > max)
1939
- finalValue = max;
1940
- if (controlledValue === undefined) {
1941
- setInternalValue(finalValue);
1942
- }
1943
- onChange?.(finalValue);
1944
- };
1945
- const handleIncrement = () => {
1946
- updateValue(Number(value) + step);
1947
- };
1948
- const handleDecrement = () => {
1949
- updateValue(Number(value) - step);
1950
- };
1951
- const handleInputChange = (event) => {
1952
- if (!readOnly) {
1953
- const newValue = event.target.value === '' ? min || 0 : Number(event.target.value);
1954
- if (!isNaN(newValue)) {
1955
- updateValue(newValue);
1956
- }
1957
- }
1958
- };
1959
- const isDecrementDisabled = disabled || (min !== undefined && Number(value) <= min);
1960
- const isIncrementDisabled = disabled || (max !== undefined && Number(value) >= max);
1961
- const buttonSize = ButtonSize.MEDIUM;
1962
- if (loading) {
1963
- return jsxRuntime.jsx(InputStepperSkeleton, { orientation: orientation, buttonShape: buttonShape });
1964
- }
1965
- return (jsxRuntime.jsxs(material.Box, { display: "inline-flex", flexDirection: orientation === 'horizontal' ? 'row' : 'column', alignItems: "center", gap: PADDING_GAP_ITEM, sx: {
1966
- opacity: disabled ? 0.5 : 1,
1967
- pointerEvents: disabled ? 'none' : 'auto',
1968
- ...sx,
1969
- }, children: [jsxRuntime.jsx(material.IconButton, { onClick: handleDecrement, disabled: isDecrementDisabled, sx: {
1970
- borderRadius: buttonShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
1971
- width: buttonSize,
1972
- height: buttonSize,
1973
- padding: PADDING_GAP_ITEM,
1974
- border: isDecrementDisabled
1975
- ? `1px solid ${Colors.BORDER_COLOR_DISABLE}`
1976
- : `1px solid ${Colors.BORDER_COLOR_BUTTON}`,
1977
- backgroundColor: Colors.BACKGROUND_COLOR,
1978
- color: buttonColor || Colors.BORDER_COLOR_BUTTON,
1979
- '&:hover': {
1980
- backgroundColor: Colors.HOVER_BG_COLOR,
1981
- },
1982
- ...(isDecrementDisabled ? {} : sxButton),
1983
- }, children: decrementIcon }), jsxRuntime.jsx(material.TextField, { value: value, onChange: handleInputChange, disabled: disabled, inputProps: {
1984
- min,
1985
- max,
1986
- step,
1987
- readOnly,
1988
- }, type: "number", sx: {
1989
- minWidth: buttonSize,
1990
- width: sxTextField?.width || buttonSize,
1991
- minHeight: buttonSize,
1992
- height: sxTextField?.height || buttonSize,
1993
- overflow: 'hidden',
1994
- borderRadius: textFieldShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
1995
- color: readOnly ? Colors.TEXT_COLOR_READONLY : '',
1996
- backgroundColor: readOnly ? 'transparent !important' : sxTextField?.backgroundColor,
1997
- '& .MuiOutlinedInput-root': {
1998
- width: '100%',
1999
- height: '100%',
2000
- borderRadius: 'inherit',
2001
- '& fieldset': {
2002
- borderColor: readOnly || disabled ? 'transparent' : sxTextField?.borderColor || Colors.BORDER_COLOR_BUTTON,
2003
- borderWidth: readOnly || disabled ? 0 : 1,
2004
- borderRadius: textFieldShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
2005
- },
2006
- '&:hover fieldset': readOnly || disabled
2007
- ? {}
2008
- : {
2009
- borderColor: sxTextField?.borderColorHover || Colors.BORDER_COLOR_BUTTON,
2010
- },
2011
- '&.Mui-focused fieldset': readOnly
2012
- ? {
2013
- borderColor: 'transparent',
2014
- borderWidth: 0,
2015
- }
2016
- : {
2017
- borderColor: sxTextField?.borderColorFocused || Colors.BORDER_COLOR_BUTTON,
2018
- borderWidth: 1,
2019
- },
2020
- '&.Mui-disabled fieldset': {
2021
- borderColor: Colors.BORDER_COLOR_DISABLE,
2022
- backgroundColor: readOnly ? 'transparent' : Colors.BORDER_COLOR_DISABLE,
2023
- },
2024
- },
2025
- '& input[type=number]::-webkit-outer-spin-button, & input[type=number]::-webkit-inner-spin-button': {
2026
- WebkitAppearance: 'none',
2027
- margin: 0,
2028
- },
2029
- '& input[type=number]': {
2030
- MozAppearance: 'textfield',
2031
- },
2032
- '& .MuiInputBase-input': {
2033
- textAlign: sxTextField?.textAlign || 'center',
2034
- padding: sxTextField?.padding || '4px 8px',
2035
- fontSize: readOnly ? '16px' : sxTextField?.fontSize || '1rem',
2036
- cursor: readOnly ? 'not-allowed' : 'text',
2037
- borderRadius: 'inherit',
2038
- backgroundColor: readOnly ? 'transparent' : sxTextField?.backgroundColor || 'transparent',
2039
- color: readOnly ? Colors.TEXT_COLOR_READONLY : sxTextField?.color || 'inherit',
2040
- fontWeight: sxTextField?.fontWeight || (readOnly ? 700 : 'normal'),
2041
- },
2042
- } }), jsxRuntime.jsx(material.IconButton, { onClick: handleIncrement, disabled: isIncrementDisabled, sx: {
2043
- borderRadius: buttonShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
2044
- width: buttonSize,
2045
- height: buttonSize,
2046
- padding: PADDING_GAP_ITEM,
2047
- border: isIncrementDisabled
2048
- ? `1px solid ${Colors.BORDER_COLOR_DISABLE}`
2049
- : `1px solid ${Colors.BORDER_COLOR_BUTTON}`,
2050
- backgroundColor: Colors.BACKGROUND_COLOR,
2051
- color: buttonColor || Colors.BORDER_COLOR_BUTTON,
2052
- '&:hover': {
2053
- backgroundColor: Colors.HOVER_BG_COLOR,
2054
- },
2055
- ...(isIncrementDisabled ? {} : sxButton),
2056
- }, children: incrementIcon })] }));
2057
- };
2058
1528
 
2059
1529
  const LinkInternalElement = ({ content, onClick, sx = {} }) => {
2060
1530
  return (jsxRuntime.jsx(material.Typography, { onClick: onClick, sx: {
@@ -2077,7 +1547,7 @@ const LinkElement = ({ onClick, sx = {}, target = '_self', ...rest }) => {
2077
1547
  }, ...rest }));
2078
1548
  };
2079
1549
 
2080
- const StyledTextField$3 = styles.styled(MuiTextField)(({ theme }) => {
1550
+ styles.styled(MuiTextField)(({ theme }) => {
2081
1551
  return {
2082
1552
  '& .MuiOutlinedInput-root': {
2083
1553
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -2119,57 +1589,6 @@ const StyledTextField$3 = styles.styled(MuiTextField)(({ theme }) => {
2119
1589
  },
2120
1590
  };
2121
1591
  });
2122
- const LinkFieldComponent = ({ label = 'Website', placeholder = 'www.untitledui.com', value, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText = 'This is a hint text to help user.', onChange, protocol = 'http://', iconAfter, sx, ...props }) => {
2123
- const [validationError, setValidationError] = React.useState(false);
2124
- const isValidLink = (url) => {
2125
- if (!url)
2126
- return true; // Empty is valid (optional field)
2127
- try {
2128
- const urlToTest = url.includes('://') ? url : `${protocol}${url}`;
2129
- new URL(urlToTest);
2130
- // Kiểm tra domain phải có dấu chấm (ít nhất là có TLD)
2131
- const hostname = new URL(urlToTest).hostname;
2132
- if (!hostname || !hostname.includes('.')) {
2133
- return false;
2134
- }
2135
- return true;
2136
- }
2137
- catch {
2138
- return false;
2139
- }
2140
- };
2141
- const handleBlur = (event) => {
2142
- const inputValue = event.target.value;
2143
- if (inputValue && !isValidLink(inputValue)) {
2144
- setValidationError(true);
2145
- }
2146
- else {
2147
- setValidationError(false);
2148
- }
2149
- };
2150
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
2151
- display: 'block',
2152
- ...TYPOGRAPHY.textFieldLabel,
2153
- color: COLOR_GRAY[800],
2154
- marginBottom: '4px',
2155
- }, children: label })), jsxRuntime.jsx(StyledTextField$3, { placeholder: placeholder, value: value, disabled: disabled, error: error || validationError, helperText: error || validationError ? (error ? errorMessage : 'đây không phải link') : '', size: "small", fullWidth: true, onChange: onChange, onBlur: handleBlur, InputProps: {
2156
- startAdornment: (jsxRuntime.jsx(material.InputAdornment, { position: "start", children: jsxRuntime.jsx(material.Typography, { sx: { color: COLOR_NEUTRAL[300], ...TYPOGRAPHY.text14Regular }, children: protocol }) })),
2157
- endAdornment: (success || error || validationError) && (jsxRuntime.jsx(material.InputAdornment, { position: "end", children: iconAfter ? (iconAfter) : (jsxRuntime.jsx(IconElement, { icon: error || validationError ? 'info' : 'check_circle', sx: { color: error || validationError ? COLOR_ERROR[500] : COLOR_SUCCESS[500] } })) })),
2158
- }, sx: {
2159
- '& .MuiOutlinedInput-root': {
2160
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2161
- ...(success && {
2162
- '&.Mui-focused': {
2163
- boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
2164
- },
2165
- }),
2166
- },
2167
- } }), success && !error && successMessage && (jsxRuntime.jsx(material.Typography, { sx: {
2168
- ...TYPOGRAPHY.textFieldHelper,
2169
- color: COLOR_SUCCESS[500],
2170
- marginTop: '4px',
2171
- }, children: successMessage }))] }));
2172
- };
2173
1592
 
2174
1593
  const MODAL_ICON_COLORS = {
2175
1594
  check_circle: '#10B981',
@@ -2279,15 +1698,7 @@ const ModalCardComponent = ({ open, isForm = false, onClose, items, nodeContent,
2279
1698
  }, children: jsxRuntime.jsxs(StackRowAlignCenter, { sx: { width: '100%' }, children: [nodeBottomLeft && jsxRuntime.jsx(material.Box, { sx: { width: '100%' }, children: nodeBottomLeft }), (buttonLeft || buttonCenter || buttonRight) && (jsxRuntime.jsxs(StackRow, { sx: { width: '100%', gap: PADDING_GAP_LAYOUT }, children: [buttonLeft && (jsxRuntime.jsx(material.Box, { sx: { flex: 1 }, children: jsxRuntime.jsx(ButtonComponent, { ...buttonLeft, fullWidth: true }) })), buttonCenter && (jsxRuntime.jsx(material.Box, { sx: { flex: 1 }, children: jsxRuntime.jsx(ButtonComponent, { ...buttonCenter, fullWidth: true }) })), buttonRight && (jsxRuntime.jsx(material.Box, { sx: { flex: 1 }, children: jsxRuntime.jsx(ButtonComponent, { ...buttonRight, fullWidth: true }) }))] }))] }) }))] }) }));
2280
1699
  };
2281
1700
 
2282
- const CURRENCIES = ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'CNY', 'VND', 'INR'];
2283
- // Format number with thousand separators
2284
- const formatMoneyDisplay = (value) => {
2285
- if (!value)
2286
- return '';
2287
- const numericOnly = value.replace(/\D/g, '');
2288
- return numericOnly.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
2289
- };
2290
- const StyledTextField$2 = styles.styled(MuiTextField)(({ theme }) => {
1701
+ styles.styled(MuiTextField)(({ theme }) => {
2291
1702
  return {
2292
1703
  '& .MuiOutlinedInput-root': {
2293
1704
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -2329,64 +1740,8 @@ const StyledTextField$2 = styles.styled(MuiTextField)(({ theme }) => {
2329
1740
  },
2330
1741
  };
2331
1742
  });
2332
- const MoneyFieldComponent = ({ label = 'Sale amount', placeholder = '1,000.00', value, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText = 'This is a hint text to help user.', onChange, currency = 'USD', onCurrencyChange, iconBefore, optionCurrencies = CURRENCIES, sx, ...props }) => {
2333
- const [selectedCurrency, setSelectedCurrency] = React.useState(currency);
2334
- const handleCurrencyChange = (e) => {
2335
- const newCurrency = e.target.value;
2336
- setSelectedCurrency(newCurrency);
2337
- onCurrencyChange?.(newCurrency);
2338
- };
2339
- const handleMoneyChange = (e) => {
2340
- const rawValue = e.target.value.replace(/,/g, '');
2341
- onChange?.({ ...e, target: { ...e.target, value: rawValue } });
2342
- };
2343
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
2344
- display: 'block',
2345
- ...TYPOGRAPHY.textFieldLabel,
2346
- color: COLOR_GRAY[800],
2347
- marginBottom: '4px',
2348
- }, children: label })), jsxRuntime.jsx(StyledTextField$2, { placeholder: placeholder, value: formatMoneyDisplay(value), disabled: disabled, error: error, helperText: error ? errorMessage : helperText, size: "small", fullWidth: true, onChange: handleMoneyChange, InputProps: {
2349
- startAdornment: (jsxRuntime.jsx(material.InputAdornment, { position: "start", children: iconBefore ? iconBefore : jsxRuntime.jsx(AttachMoneyIcon, { sx: { fontSize: '18px', color: COLOR_GRAY[400], mr: 0.5 } }) })),
2350
- endAdornment: (jsxRuntime.jsx(material.InputAdornment, { position: "end", children: jsxRuntime.jsxs(material.Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [(success || error) && (jsxRuntime.jsx(IconElement, { icon: error ? 'info' : 'check_circle', sx: { color: error ? COLOR_ERROR[500] : COLOR_SUCCESS[500] } })), jsxRuntime.jsx(material.Select, { value: selectedCurrency, onChange: handleCurrencyChange, disabled: disabled, variant: "standard", sx: {
2351
- border: 'none',
2352
- outline: 'none',
2353
- '& .MuiSelect-standard': { border: 'none' },
2354
- '&.MuiInput-underline:before': { borderBottom: 'none' },
2355
- '&.MuiInput-underline:hover:before': { borderBottom: 'none' },
2356
- '&.MuiInput-underline:after': { borderBottom: 'none' },
2357
- minWidth: '70px',
2358
- fontSize: '14px',
2359
- color: COLOR_GRAY[500],
2360
- }, children: optionCurrencies.map((curr) => (jsxRuntime.jsx(material.MenuItem, { value: curr, children: curr }, curr))) })] }) })),
2361
- }, sx: {
2362
- '& .MuiOutlinedInput-root': {
2363
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2364
- ...(success && {
2365
- '&.Mui-focused': {
2366
- boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
2367
- },
2368
- }),
2369
- },
2370
- } }), success && !error && successMessage && (jsxRuntime.jsx(material.Typography, { sx: {
2371
- ...TYPOGRAPHY.textFieldHelper,
2372
- color: COLOR_SUCCESS[600],
2373
- marginTop: '4px',
2374
- }, children: successMessage }))] }));
2375
- };
2376
1743
 
2377
- const COUNTRY_CODES = [
2378
- { code: 'US', name: 'United States', flag: '🇺🇸', value: '+1' },
2379
- { code: 'GB', name: 'United Kingdom', flag: '🇬🇧', value: '+44' },
2380
- { code: 'CA', name: 'Canada', flag: '🇨🇦', value: '+1' },
2381
- { code: 'AU', name: 'Australia', flag: '🇦🇺', value: '+61' },
2382
- { code: 'VN', name: 'Vietnam', flag: '🇻🇳', value: '+84' },
2383
- { code: 'JP', name: 'Japan', flag: '🇯🇵', value: '+81' },
2384
- { code: 'CN', name: 'China', flag: '🇨🇳', value: '+86' },
2385
- { code: 'IN', name: 'India', flag: '🇮🇳', value: '+91' },
2386
- { code: 'DE', name: 'Germany', flag: '🇩🇪', value: '+49' },
2387
- { code: 'FR', name: 'France', flag: '🇫🇷', value: '+33' },
2388
- ];
2389
- const StyledTextField$1 = styles.styled(MuiTextField)(({ theme }) => {
1744
+ styles.styled(MuiTextField)(({ theme }) => {
2390
1745
  return {
2391
1746
  '& .MuiOutlinedInput-root': {
2392
1747
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -2428,250 +1783,8 @@ const StyledTextField$1 = styles.styled(MuiTextField)(({ theme }) => {
2428
1783
  },
2429
1784
  };
2430
1785
  });
2431
- const PhoneNumberFieldComponent = ({ label = 'Số điện thoại', placeholder = '0123 456 789', value, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText, onChange, countryCode = 'US', onCountryCodeChange, countries = COUNTRY_CODES, sx, ...props }) => {
2432
- const handleCountryChange = (e) => {
2433
- const newCode = e.target.value;
2434
- onCountryCodeChange?.(newCode);
2435
- };
2436
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
2437
- display: 'block',
2438
- ...TYPOGRAPHY.textFieldLabel,
2439
- color: COLOR_GRAY[800],
2440
- marginBottom: '4px',
2441
- }, children: label })), jsxRuntime.jsx(StyledTextField$1, { placeholder: placeholder, value: value, disabled: disabled, error: error, helperText: error ? errorMessage : helperText, size: "small", fullWidth: true, onChange: onChange, InputProps: {
2442
- startAdornment: (jsxRuntime.jsx(material.InputAdornment, { position: "start", sx: { mr: 0 }, children: jsxRuntime.jsx(material.Select, { value: countryCode, onChange: handleCountryChange, disabled: disabled, variant: "standard", sx: {
2443
- border: 'none',
2444
- outline: 'none',
2445
- '& .MuiSelect-standard': { border: 'none' },
2446
- '&.MuiInput-underline:before': { borderBottom: 'none' },
2447
- '&.MuiInput-underline:hover:before': { borderBottom: 'none' },
2448
- '&.MuiInput-underline:after': { borderBottom: 'none' },
2449
- minWidth: '50px',
2450
- fontSize: '14px',
2451
- color: COLOR_GRAY[500],
2452
- display: 'flex',
2453
- alignItems: 'center',
2454
- gap: 0.5,
2455
- }, children: countries.map((c) => (jsxRuntime.jsx(material.MenuItem, { value: c.code, children: c.code }, c.code))) }) })),
2456
- endAdornment: (success || error) && (jsxRuntime.jsx(material.InputAdornment, { position: "end", children: jsxRuntime.jsx(IconElement, { icon: error ? 'info' : 'check_circle', sx: { color: error ? COLOR_ERROR[500] : COLOR_SUCCESS[500] } }) })),
2457
- }, sx: {
2458
- '& .MuiOutlinedInput-root': {
2459
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2460
- ...(success && {
2461
- '&.Mui-focused': {
2462
- boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
2463
- },
2464
- }),
2465
- },
2466
- } }), success && !error && successMessage && (jsxRuntime.jsx(material.Typography, { sx: {
2467
- ...TYPOGRAPHY.textFieldHelper,
2468
- color: COLOR_SUCCESS[600],
2469
- marginTop: '4px',
2470
- }, children: successMessage }))] }));
2471
- };
2472
1786
 
2473
- const PIN_SIZES = {
2474
- sm: { width: 40, height: 40, fontSize: 18 },
2475
- md: { width: 48, height: 48, fontSize: 20 },
2476
- lg: { width: 56, height: 56, fontSize: 24 },
2477
- };
2478
- const PIN_SPACING = {
2479
- sm: 8,
2480
- md: 12,
2481
- lg: 16,
2482
- };
2483
-
2484
- const PINComponent = ({ length = 6, value, onChange, label, error = false, errorMessage, type = 'text', disabled = false, autoFocus = false, onComplete, align = 'left', spacing = 'md', size = 'md', masked = false, borderFocusColor, sx, }) => {
2485
- const { palette } = material.useTheme();
2486
- const inputRefs = React.useRef([]);
2487
- React.useEffect(() => {
2488
- if (autoFocus && inputRefs.current[0]) {
2489
- inputRefs.current[0].focus();
2490
- }
2491
- }, [autoFocus]);
2492
- const handleChange = (index, val) => {
2493
- if (!/^\d*$/.test(val))
2494
- return;
2495
- const newValue = value.split('');
2496
- newValue[index] = val;
2497
- const updatedValue = newValue.join('').slice(0, length);
2498
- onChange(updatedValue);
2499
- if (val && index < length - 1) {
2500
- inputRefs.current[index + 1]?.focus();
2501
- }
2502
- if (updatedValue.length === length) {
2503
- onComplete?.(updatedValue);
2504
- }
2505
- };
2506
- const handleKeyDown = (index, e) => {
2507
- if (e.key === 'Backspace' && !value[index] && index > 0) {
2508
- inputRefs.current[index - 1]?.focus();
2509
- }
2510
- };
2511
- const renderPINDisplay = (index) => {
2512
- const inputValue = value[index] || '';
2513
- const isFilled = !!inputValue;
2514
- const sizeStyle = PIN_SIZES[size];
2515
- const borderColor = error ? palette.error.main : COLOR_GRAY[200];
2516
- const borderFocusColorValue = borderFocusColor || (error ? palette.error.main : COLOR_GRAY[900]);
2517
- if (type === 'bullet') {
2518
- return (jsxRuntime.jsxs(material.Box, { sx: { position: 'relative', width: sizeStyle.width, height: sizeStyle.height }, children: [jsxRuntime.jsx(material.TextField, { ref: (el) => {
2519
- const input = el?.querySelector('input');
2520
- if (input) {
2521
- inputRefs.current[index] = input;
2522
- }
2523
- }, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: disabled, error: error, sx: {
2524
- width: '100%',
2525
- height: '100%',
2526
- '& .MuiOutlinedInput-root': {
2527
- height: '100%',
2528
- padding: 0,
2529
- '& input': {
2530
- textAlign: 'center',
2531
- fontSize: sizeStyle.fontSize,
2532
- fontWeight: 600,
2533
- padding: 0,
2534
- color: masked && isFilled ? 'transparent' : 'inherit',
2535
- WebkitTextFillColor: masked && isFilled ? 'transparent' : 'unset',
2536
- caretColor: palette.primary.main,
2537
- '&::placeholder': {
2538
- color: palette.action.disabled,
2539
- opacity: 1,
2540
- },
2541
- },
2542
- '& fieldset': {
2543
- borderColor: borderColor,
2544
- borderRadius: '8px',
2545
- },
2546
- '&:hover fieldset': {
2547
- borderColor: error ? palette.error.main : borderFocusColorValue,
2548
- },
2549
- '&.Mui-focused fieldset': {
2550
- borderColor: borderFocusColorValue,
2551
- borderWidth: 1,
2552
- },
2553
- },
2554
- '& input': {
2555
- maxLength: 1,
2556
- },
2557
- }, placeholder: "-" }), masked && isFilled && (jsxRuntime.jsx(material.Box, { sx: {
2558
- position: 'absolute',
2559
- top: '50%',
2560
- left: '50%',
2561
- transform: 'translate(-50%, -50%)',
2562
- fontSize: sizeStyle.fontSize,
2563
- fontWeight: 600,
2564
- color: error ? palette.error.main : palette.text.primary,
2565
- pointerEvents: 'none',
2566
- }, children: "\u25CF" }))] }));
2567
- }
2568
- if (type === 'circle') {
2569
- return (jsxRuntime.jsxs(material.Box, { sx: { position: 'relative', width: sizeStyle.width, height: sizeStyle.height }, children: [jsxRuntime.jsx(material.TextField, { ref: (el) => {
2570
- const input = el?.querySelector('input');
2571
- if (input) {
2572
- inputRefs.current[index] = input;
2573
- }
2574
- }, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: disabled, error: error, sx: {
2575
- width: '100%',
2576
- height: '100%',
2577
- '& .MuiOutlinedInput-root': {
2578
- height: '100%',
2579
- padding: 0,
2580
- borderRadius: '50%',
2581
- '& input': {
2582
- textAlign: 'center',
2583
- fontSize: sizeStyle.fontSize,
2584
- fontWeight: 600,
2585
- padding: 0,
2586
- color: masked && isFilled ? 'transparent' : 'inherit',
2587
- WebkitTextFillColor: masked && isFilled ? 'transparent' : 'unset',
2588
- caretColor: palette.primary.main,
2589
- '&::placeholder': {
2590
- color: palette.action.disabled,
2591
- opacity: 1,
2592
- },
2593
- },
2594
- '& fieldset': {
2595
- borderColor: borderColor,
2596
- borderRadius: '50%',
2597
- },
2598
- '&:hover fieldset': {
2599
- borderColor: error ? palette.error.main : borderFocusColorValue,
2600
- },
2601
- '&.Mui-focused fieldset': {
2602
- borderColor: borderFocusColorValue,
2603
- borderWidth: 1,
2604
- },
2605
- },
2606
- '& input': {
2607
- maxLength: 1,
2608
- },
2609
- }, placeholder: "-" }), masked && isFilled && (jsxRuntime.jsx(material.Box, { sx: {
2610
- position: 'absolute',
2611
- top: '50%',
2612
- left: '50%',
2613
- transform: 'translate(-50%, -50%)',
2614
- fontSize: sizeStyle.fontSize,
2615
- fontWeight: 600,
2616
- color: error ? palette.error.main : palette.text.primary,
2617
- pointerEvents: 'none',
2618
- }, children: "\u25CF" }))] }));
2619
- }
2620
- // Default text type
2621
- return (jsxRuntime.jsx(material.TextField, { ref: (el) => {
2622
- const input = el?.querySelector('input');
2623
- if (input) {
2624
- inputRefs.current[index] = input;
2625
- }
2626
- }, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: disabled, error: error, sx: {
2627
- width: sizeStyle.width,
2628
- '& .MuiOutlinedInput-root': {
2629
- height: sizeStyle.height,
2630
- padding: 0,
2631
- '& input': {
2632
- textAlign: 'center',
2633
- fontSize: sizeStyle.fontSize,
2634
- fontWeight: 600,
2635
- padding: 0,
2636
- '&::placeholder': {
2637
- color: palette.action.disabled,
2638
- opacity: 1,
2639
- },
2640
- },
2641
- '& fieldset': {
2642
- borderColor: borderColor,
2643
- },
2644
- '&:hover fieldset': {
2645
- borderColor: error ? palette.error.main : borderFocusColorValue,
2646
- },
2647
- '&.Mui-focused fieldset': {
2648
- borderColor: borderFocusColorValue,
2649
- borderWidth: 1,
2650
- },
2651
- },
2652
- '& input': {
2653
- maxLength: 1,
2654
- },
2655
- }, placeholder: "-" }));
2656
- };
2657
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Box, { sx: {
2658
- mb: 1,
2659
- fontSize: 14,
2660
- fontWeight: 500,
2661
- color: palette.text.primary,
2662
- }, children: label })), jsxRuntime.jsx(material.Box, { sx: {
2663
- display: 'flex',
2664
- gap: `${PIN_SPACING[spacing]}px`,
2665
- justifyContent: align === 'center' ? 'center' : align === 'right' ? 'flex-end' : 'flex-start',
2666
- }, children: Array.from({ length }).map((_, index) => (jsxRuntime.jsx(material.Box, { children: renderPINDisplay(index) }, index))) }), error && errorMessage && (jsxRuntime.jsx(material.Box, { sx: {
2667
- mt: 1,
2668
- fontSize: 12,
2669
- color: palette.error.main,
2670
- textAlign: align,
2671
- }, children: errorMessage }))] }));
2672
- };
2673
-
2674
- const StyledAutocomplete$1 = material.styled(material.Autocomplete)(({ theme }) => ({
1787
+ material.styled(material.Autocomplete)(({ theme }) => ({
2675
1788
  '& .MuiOutlinedInput-root': {
2676
1789
  padding: '8px !important',
2677
1790
  display: 'flex',
@@ -2732,138 +1845,8 @@ const StyledAutocomplete$1 = material.styled(material.Autocomplete)(({ theme })
2732
1845
  },
2733
1846
  },
2734
1847
  }));
2735
- const SearchDropdownComponent = ({ value, onChange, onClear, onInputChange, onSearch, borderRadius = 6, disabled = false, multiple = false, label = '', placeholder = 'Search...', error = false, errorMessage, success = false, successMessage, helperText = '', options = [], loading = false, sx, }) => {
2736
- const DEBOUNCE_DELAY = 500;
2737
- const [inputValue, setInputValue] = React.useState('');
2738
- const [filteredOptions, setFilteredOptions] = React.useState(options);
2739
- const [isLoading, setIsLoading] = React.useState(false);
2740
- const debounceTimerRef = React.useRef(null);
2741
- const onSearchRef = React.useRef(onSearch);
2742
- const onInputChangeRef = React.useRef(onInputChange);
2743
- const optionsRef = React.useRef(options);
2744
- // Update refs when props change
2745
- React.useEffect(() => {
2746
- onSearchRef.current = onSearch;
2747
- onInputChangeRef.current = onInputChange;
2748
- optionsRef.current = options;
2749
- }, [onSearch, onInputChange, options]);
2750
- // Normalize value to array for internal state
2751
- const selectedValues = React.useMemo(() => {
2752
- if (!value)
2753
- return [];
2754
- const values = Array.isArray(value) ? value : [value];
2755
- return values.map((v) => (typeof v === 'object' ? v : { label: String(v), value: v }));
2756
- }, [value]);
2757
- // Search logic with debounce
2758
- React.useEffect(() => {
2759
- if (debounceTimerRef.current) {
2760
- clearTimeout(debounceTimerRef.current);
2761
- }
2762
- // Reset to original options when input is empty
2763
- if (!inputValue.trim()) {
2764
- setFilteredOptions(optionsRef.current);
2765
- return;
2766
- }
2767
- debounceTimerRef.current = setTimeout(async () => {
2768
- const searchFn = onSearchRef.current;
2769
- if (searchFn) {
2770
- // Async search
2771
- setIsLoading(true);
2772
- try {
2773
- const results = await searchFn(inputValue);
2774
- setFilteredOptions(results);
2775
- }
2776
- catch (error) {
2777
- console.error('Search error:', error);
2778
- setFilteredOptions([]);
2779
- }
2780
- finally {
2781
- setIsLoading(false);
2782
- }
2783
- }
2784
- else {
2785
- // Local filtering
2786
- const filtered = optionsRef.current.filter((opt) => opt.label.toLowerCase().includes(inputValue.toLowerCase()));
2787
- setFilteredOptions(filtered);
2788
- }
2789
- onInputChangeRef.current?.(inputValue);
2790
- }, DEBOUNCE_DELAY);
2791
- return () => {
2792
- if (debounceTimerRef.current) {
2793
- clearTimeout(debounceTimerRef.current);
2794
- }
2795
- };
2796
- }, [inputValue]);
2797
- const handleInputChange = (event, newInputValue) => {
2798
- setInputValue(newInputValue);
2799
- };
2800
- const handleChange = (event, newValue) => {
2801
- if (multiple) {
2802
- const result = Array.isArray(newValue) ? newValue : newValue ? [newValue] : [];
2803
- onChange?.(result.length > 0 ? result : null);
2804
- }
2805
- else {
2806
- onChange?.(newValue || null);
2807
- }
2808
- };
2809
- const handleClear = () => {
2810
- setInputValue('');
2811
- setFilteredOptions(optionsRef.current);
2812
- onClear?.();
2813
- };
2814
- return (jsxRuntime.jsxs(material.Box, { sx: { width: '100%', ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { variant: "subtitle2", sx: {
2815
- fontWeight: 500,
2816
- color: COLOR_GRAY[900],
2817
- marginBottom: '6px',
2818
- display: 'block',
2819
- }, children: label })), jsxRuntime.jsx(StyledAutocomplete$1, { multiple: multiple, freeSolo: true, options: filteredOptions, getOptionLabel: (option) => {
2820
- if (!option)
2821
- return '';
2822
- if (typeof option === 'object' && 'label' in option)
2823
- return option.label;
2824
- return String(option);
2825
- }, isOptionEqualToValue: (option, val) => {
2826
- if (!option || !val)
2827
- return false;
2828
- if (typeof option === 'object' && typeof val === 'object' && 'value' in option && 'value' in val) {
2829
- return option.value === val.value;
2830
- }
2831
- return false;
2832
- }, value: multiple ? selectedValues : selectedValues[0] || null, inputValue: inputValue, onInputChange: handleInputChange, onChange: handleChange, disabled: disabled || loading, loading: isLoading || loading, noOptionsText: inputValue ? 'No results found' : 'Type to search', sx: {
2833
- '& .MuiOutlinedInput-root': {
2834
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2835
- borderColor: error ? COLOR_ERROR[500] : success ? COLOR_SUCCESS[500] : undefined,
2836
- },
2837
- }, renderInput: (params) => (jsxRuntime.jsx(material.TextField, { ...params, placeholder: placeholder, variant: "outlined", size: "small", error: error, InputProps: {
2838
- ...params.InputProps,
2839
- startAdornment: (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.InputAdornment, { position: "start", sx: { marginLeft: '4px', marginRight: '0px' }, children: jsxRuntime.jsx(SearchIcon, { sx: { color: '#999', fontSize: '18px' } }) }), params.InputProps?.startAdornment] })),
2840
- } })), renderTags: (value, getTagProps) => value.map((option, index) => {
2841
- const label = typeof option === 'object' && option && 'label' in option ? option.label : String(option);
2842
- return jsxRuntime.jsx(material.Chip, { ...getTagProps({ index }), label: label, size: "small" });
2843
- }), renderOption: (props, option) => {
2844
- const { key, ...otherProps } = props;
2845
- const label = option?.label || '';
2846
- return (jsxRuntime.jsx(material.Box, { ...otherProps, component: "li", children: label }, key));
2847
- }, componentsProps: {
2848
- clearIndicator: {
2849
- onClick: handleClear,
2850
- },
2851
- } }), error && errorMessage && (jsxRuntime.jsx(material.Box, { sx: {
2852
- fontSize: '12px',
2853
- color: COLOR_ERROR[500],
2854
- marginTop: '4px',
2855
- }, children: errorMessage })), success && successMessage && (jsxRuntime.jsx(material.Box, { sx: {
2856
- fontSize: '12px',
2857
- color: COLOR_SUCCESS[500],
2858
- marginTop: '4px',
2859
- }, children: successMessage })), helperText && !error && !success && (jsxRuntime.jsx(material.Box, { sx: {
2860
- fontSize: '12px',
2861
- color: COLOR_NEUTRAL[500],
2862
- marginTop: '4px',
2863
- }, children: helperText }))] }));
2864
- };
2865
1848
 
2866
- const StyledAutocomplete = material.styled(material.Autocomplete)(({ theme }) => ({
1849
+ material.styled(material.Autocomplete)(({ theme }) => ({
2867
1850
  '& .MuiOutlinedInput-root': {
2868
1851
  padding: '0 !important',
2869
1852
  display: 'flex',
@@ -2902,45 +1885,6 @@ const StyledAutocomplete = material.styled(material.Autocomplete)(({ theme }) =>
2902
1885
  paddingRight: '8px',
2903
1886
  },
2904
1887
  }));
2905
- const SearchFieldComponent = ({ value, onChange, onClear, onInputChange, borderRadius = 6, disabled = false, placeholder = 'Placeholder', sx, }) => {
2906
- const DEBOUNCE_DELAY = 1000;
2907
- const [inputValue, setInputValue] = React.useState('');
2908
- const debounceTimer = React.useRef(null);
2909
- React.useEffect(() => {
2910
- if (debounceTimer.current) {
2911
- clearTimeout(debounceTimer.current);
2912
- }
2913
- debounceTimer.current = setTimeout(() => {
2914
- onInputChange?.(new Event('debounce'), inputValue, 'debounce');
2915
- }, DEBOUNCE_DELAY);
2916
- return () => {
2917
- if (debounceTimer.current) {
2918
- clearTimeout(debounceTimer.current);
2919
- }
2920
- };
2921
- }, [inputValue, onInputChange]);
2922
- const handleInputChange = React.useCallback((event, value) => {
2923
- setInputValue(value);
2924
- }, []);
2925
- const handleClear = React.useCallback(() => {
2926
- setInputValue('');
2927
- onClear?.();
2928
- onInputChange?.(new Event('clear'), '', 'clear');
2929
- }, [onClear, onInputChange]);
2930
- return (jsxRuntime.jsx(StyledAutocomplete, { freeSolo: true, options: [], inputValue: inputValue, onInputChange: handleInputChange, disabled: disabled, onChange: onChange, noOptionsText: null, sx: {
2931
- '& .MuiOutlinedInput-root': {
2932
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2933
- },
2934
- ...sx,
2935
- }, renderInput: (params) => (jsxRuntime.jsx(material.TextField, { ...params, placeholder: placeholder, variant: "outlined", size: "small", InputProps: {
2936
- ...params.InputProps,
2937
- startAdornment: (jsxRuntime.jsx(material.InputAdornment, { position: "start", sx: { marginLeft: '8px' }, children: jsxRuntime.jsx(SearchIcon, { sx: { color: '#999', fontSize: '20px' } }) })),
2938
- } })), componentsProps: {
2939
- clearIndicator: {
2940
- onClick: handleClear,
2941
- },
2942
- } }));
2943
- };
2944
1888
 
2945
1889
  const SWITCH_SIZE = {
2946
1890
  small: { width: '36px', height: '20px', thumbSize: '16px' },
@@ -2960,7 +1904,7 @@ const SWITCH_COLORS = {
2960
1904
  thumbFocusColor: '#F4EBFF',
2961
1905
  },
2962
1906
  };
2963
- const StyledSwitch = material.styled(material.Switch, {
1907
+ material.styled(material.Switch, {
2964
1908
  shouldForwardProp: (prop) => prop !== 'size',
2965
1909
  })(({ theme, size = 'medium' }) => {
2966
1910
  const mode = theme.palette.mode;
@@ -3013,61 +1957,7 @@ const StyledSwitch = material.styled(material.Switch, {
3013
1957
  },
3014
1958
  };
3015
1959
  });
3016
- const SwitchComponent = ({ title, sx, ...switchProps }) => {
3017
- if (!title) {
3018
- return jsxRuntime.jsx(StyledSwitch, { disableRipple: true, ...switchProps });
3019
- }
3020
- return (jsxRuntime.jsxs(StackRowAlignCenterJustEnd, { sx: {
3021
- display: 'inline-flex',
3022
- alignItems: 'center',
3023
- gap: '12px',
3024
- ...sx,
3025
- }, children: [jsxRuntime.jsx(StyledSwitch, { disableRipple: true, ...switchProps }), title] }));
3026
- };
3027
1960
 
3028
- const TAB_STYLES = {
3029
- position: 'relative',
3030
- padding: '18px 16px',
3031
- cursor: 'pointer',
3032
- minHeight: 44,
3033
- '&:hover': {
3034
- bgcolor: '#F3F4F6',
3035
- },
3036
- };
3037
- const TAB_UNDERLINE_STYLES = {
3038
- width: '1px',
3039
- alignSelf: 'stretch',
3040
- bgcolor: '#E5E7EB',
3041
- };
3042
- const TABS_CONTAINER_HORIZONTAL = {
3043
- display: 'flex',
3044
- alignItems: 'center',
3045
- borderBottom: '2px solid #E5E7EB',
3046
- };
3047
- const TAB_ACTIVE_BACKGROUND_HORIZONTAL = {
3048
- position: 'absolute',
3049
- bottom: -2,
3050
- left: 0,
3051
- right: 0,
3052
- height: '2px',
3053
- bgcolor: '#0F766E',
3054
- };
3055
- const TABS_CONTAINER_VERTICAL = {
3056
- position: 'relative',
3057
- padding: '10px',
3058
- cursor: 'pointer',
3059
- minHeight: 40,
3060
- '&:hover': {
3061
- bgcolor: '#F3F4F6',
3062
- },
3063
- width: '100%',
3064
- };
3065
- const TAB_ACTIVE_BACKGROUND_VERTICAL = {
3066
- position: 'absolute',
3067
- inset: 0,
3068
- bgcolor: '#E6EEED',
3069
- borderRadius: 1,
3070
- };
3071
1961
  var TabColors;
3072
1962
  (function (TabColors) {
3073
1963
  TabColors["ACTIVE_TEXT"] = "#0F766E";
@@ -3076,171 +1966,7 @@ var TabColors;
3076
1966
  TabColors["MENU_ACTIVE_BACKGROUND"] = "#E0F2FE";
3077
1967
  })(TabColors || (TabColors = {}));
3078
1968
 
3079
- const TabsComponent = ({ idSelect, tabs, size, direction = 'row', maxDisplay, onChange, sx, sxTabs, sxWrapper, }) => {
3080
- // state
3081
- const [selected, setSelected] = React.useState(idSelect);
3082
- const [anchorEl, setAnchorEl] = React.useState(null);
3083
- const layoutGroupId = React.useId();
3084
- React.useEffect(() => {
3085
- setSelected(idSelect);
3086
- }, [idSelect]);
3087
- const isVertical = direction === 'column';
3088
- const showOverflow = !isVertical && maxDisplay && tabs.length > maxDisplay;
3089
- const visibleTabs = showOverflow ? tabs.slice(0, maxDisplay) : tabs;
3090
- const overflowTabs = showOverflow ? tabs.slice(maxDisplay) : [];
3091
- // function
3092
- const handleOpenDropdown = (event) => {
3093
- setAnchorEl(event.currentTarget);
3094
- };
3095
- const handleTabClick = (tab) => {
3096
- setSelected(tab.id);
3097
- onChange?.(tab.id);
3098
- if (tab.onClick) {
3099
- tab.onClick();
3100
- }
3101
- };
3102
- const handleOverflowItemClick = (tab) => {
3103
- handleTabClick(tab);
3104
- setAnchorEl(null);
3105
- };
3106
- return (jsxRuntime.jsx(React.Fragment, { children: isVertical ? (jsxRuntime.jsx(framerMotion.LayoutGroup, { id: layoutGroupId, children: jsxRuntime.jsx(material.Stack, { direction: "column", sx: { width: 'fit-content', gap: PADDING_GAP_ITEM_SMALL, ...sxWrapper }, children: tabs.map((tab) => {
3107
- const isActive = tab.id === selected;
3108
- return (jsxRuntime.jsx(LinkElement, { href: tab.href, onClick: tab.onClick, id: tab.id, children: jsxRuntime.jsxs(material.Box, { sx: { position: 'relative' }, children: [jsxRuntime.jsx(material.Stack, { component: framerMotion.motion.div, sx: {
3109
- ...TABS_CONTAINER_VERTICAL,
3110
- color: isActive ? TabColors.ACTIVE_TEXT : TabColors.INACTIVE_TEXT,
3111
- }, onTap: () => handleTabClick(tab), children: jsxRuntime.jsxs(material.Box, { sx: {
3112
- ...TYPOGRAPHY_STYLES.textMd.medium,
3113
- display: 'flex',
3114
- alignItems: 'center',
3115
- gap: tab.icon ? 0.5 : 0,
3116
- position: 'relative',
3117
- zIndex: 1,
3118
- ...sx,
3119
- }, children: [tab.icon && jsxRuntime.jsx(IconElement, { size: size, icon: tab.icon }), tab.name] }) }), isActive && (jsxRuntime.jsx(material.Box, { component: framerMotion.motion.div, sx: { ...TAB_ACTIVE_BACKGROUND_VERTICAL }, layoutId: `${layoutGroupId}-background` }))] }) }, tab.id));
3120
- }) }) })) : (jsxRuntime.jsx(framerMotion.LayoutGroup, { id: layoutGroupId, children: jsxRuntime.jsxs(material.Box, { sx: { position: 'relative', display: 'flex', alignItems: 'center' }, children: [jsxRuntime.jsxs(material.Box, { sx: { ...TABS_CONTAINER_HORIZONTAL }, children: [visibleTabs.map((tab) => {
3121
- const isActive = tab.id === selected;
3122
- return (jsxRuntime.jsx(LinkElement, { href: tab.href, onClick: tab.onClick, id: tab.id, children: jsxRuntime.jsxs(material.Stack, { component: framerMotion.motion.div, sx: {
3123
- color: isActive ? TabColors.ACTIVE_TEXT : TabColors.INACTIVE_TEXT,
3124
- position: 'relative',
3125
- padding: '18px 16px',
3126
- cursor: 'pointer',
3127
- minHeight: 40,
3128
- '&:hover': {
3129
- bgcolor: TabColors.HOVER_BACKGROUND,
3130
- },
3131
- ...sxTabs,
3132
- }, onTap: () => handleTabClick(tab), children: [jsxRuntime.jsxs(material.Box, { sx: {
3133
- ...TYPOGRAPHY_STYLES.textMd.medium,
3134
- display: 'flex',
3135
- alignItems: 'center',
3136
- gap: tab.icon ? 0.5 : 0,
3137
- ...sx,
3138
- }, children: [tab.icon && jsxRuntime.jsx(IconElement, { size: size, icon: tab.icon }), tab.name] }), isActive && (jsxRuntime.jsx(material.Box, { component: framerMotion.motion.div, sx: { ...TAB_ACTIVE_BACKGROUND_HORIZONTAL }, layoutId: `${layoutGroupId}-underline` }))] }) }, tab.id));
3139
- }), showOverflow && (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(material.Box, { sx: { ...TAB_UNDERLINE_STYLES } }), jsxRuntime.jsx(material.Stack, { sx: {
3140
- ...TAB_STYLES,
3141
- }, onClick: handleOpenDropdown, children: jsxRuntime.jsx(IconElement, { icon: "more_horiz", size: size }) })] }))] }), jsxRuntime.jsx(material.Menu, { disableScrollLock: true, anchorEl: anchorEl, open: Boolean(anchorEl), onClose: () => setAnchorEl(null), anchorOrigin: {
3142
- vertical: 'bottom',
3143
- horizontal: 'left',
3144
- }, transformOrigin: {
3145
- vertical: 'top',
3146
- horizontal: 'left',
3147
- }, children: overflowTabs.map((tab) => {
3148
- const isActive = tab.id === selected;
3149
- return (jsxRuntime.jsx(material.MenuItem, { onClick: () => handleOverflowItemClick(tab), sx: {
3150
- color: isActive ? TabColors.ACTIVE_TEXT : TabColors.INACTIVE_TEXT,
3151
- bgcolor: isActive ? TabColors.MENU_ACTIVE_BACKGROUND : 'transparent',
3152
- '&:hover': {
3153
- bgcolor: isActive ? TabColors.MENU_ACTIVE_BACKGROUND : TabColors.HOVER_BACKGROUND,
3154
- },
3155
- }, children: jsxRuntime.jsxs(material.Box, { sx: {
3156
- display: 'flex',
3157
- alignItems: 'center',
3158
- gap: tab.icon ? 0.5 : 0,
3159
- }, children: [tab.icon && jsxRuntime.jsx(IconElement, { size: size, icon: tab.icon }), tab.name] }) }, tab.id));
3160
- }) })] }) })) }));
3161
- };
3162
-
3163
- const TextAreaComponent = ({ label = '', placeholder = '', value = '', disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, onChange, onBlur, helperText = '', rows = 4, maxLength, sx, }) => {
3164
- const borderRadiusValue = borderRadius === 'max' ? '100px' : `${borderRadius}px`;
3165
- const getHelperText = () => {
3166
- if (error && errorMessage)
3167
- return errorMessage;
3168
- if (success && successMessage)
3169
- return successMessage;
3170
- if (helperText)
3171
- return helperText;
3172
- return '';
3173
- };
3174
- const getHelperTextColor = () => {
3175
- if (error)
3176
- return COLOR_ERROR[500];
3177
- if (success)
3178
- return COLOR_SUCCESS[500];
3179
- return COLOR_NEUTRAL[400];
3180
- };
3181
- const getBorderColor = () => {
3182
- if (error)
3183
- return COLOR_ERROR[500];
3184
- if (success)
3185
- return COLOR_SUCCESS[500];
3186
- return COLOR_NEUTRAL[300];
3187
- };
3188
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
3189
- display: 'block',
3190
- ...TYPOGRAPHY.textFieldLabel,
3191
- color: COLOR_GRAY[800],
3192
- marginBottom: '4px',
3193
- }, children: label })), jsxRuntime.jsxs(material.Box, { sx: { position: 'relative' }, children: [jsxRuntime.jsx(material.TextField, { fullWidth: true, multiline: true, rows: rows, placeholder: placeholder, value: value, disabled: disabled, onChange: (e) => onChange?.(e.target.value), onBlur: (e) => onBlur?.(e.target.value), inputProps: {
3194
- maxLength: maxLength,
3195
- style: {
3196
- ...TYPOGRAPHY.text14Regular,
3197
- paddingBottom: maxLength ? '24px' : '0px',
3198
- },
3199
- }, sx: {
3200
- '& .MuiOutlinedInput-root': {
3201
- borderRadius: borderRadiusValue,
3202
- backgroundColor: disabled ? COLOR_NEUTRAL[100] : 'white',
3203
- transition: 'all 0.2s ease',
3204
- '& fieldset': {
3205
- borderColor: getBorderColor(),
3206
- },
3207
- '&:hover fieldset': {
3208
- borderColor: disabled ? getBorderColor() : error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
3209
- },
3210
- '&.Mui-focused fieldset': {
3211
- borderColor: error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
3212
- borderWidth: '1.5px',
3213
- },
3214
- },
3215
- '& .MuiOutlinedInput-input': {
3216
- color: value ? COLOR_GRAY[900] : COLOR_NEUTRAL[400],
3217
- '&::placeholder': {
3218
- color: COLOR_NEUTRAL[400],
3219
- opacity: 1,
3220
- },
3221
- '&:disabled': {
3222
- color: COLOR_NEUTRAL[400],
3223
- WebkitTextFillColor: COLOR_NEUTRAL[400],
3224
- },
3225
- },
3226
- '& .MuiOutlinedInput-notchedOutline': {
3227
- borderColor: getBorderColor(),
3228
- },
3229
- } }), maxLength && (jsxRuntime.jsxs(material.Box, { sx: {
3230
- position: 'absolute',
3231
- bottom: '8px',
3232
- right: '12px',
3233
- fontSize: '12px',
3234
- color: COLOR_NEUTRAL[400],
3235
- pointerEvents: 'none',
3236
- }, children: ["(", value?.length, "/", maxLength, ")"] }))] }), getHelperText() && (jsxRuntime.jsx(material.Box, { sx: {
3237
- color: getHelperTextColor(),
3238
- marginTop: '4px',
3239
- ...TYPOGRAPHY.textFieldHelper,
3240
- }, children: getHelperText() }))] }));
3241
- };
3242
-
3243
- const StyledTextField = styles.styled(MuiTextField)(({ theme }) => {
1969
+ styles.styled(MuiTextField)(({ theme }) => {
3244
1970
  return {
3245
1971
  '& .MuiOutlinedInput-root': {
3246
1972
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -3282,236 +2008,23 @@ const StyledTextField = styles.styled(MuiTextField)(({ theme }) => {
3282
2008
  },
3283
2009
  };
3284
2010
  });
3285
- const TextFieldComponent = ({ label, placeholder = 'Placeholder', value, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText, onChange, iconBefore, iconAfter, sx, ...props }) => {
3286
- return (jsxRuntime.jsxs(material.Box, { sx: { ...sx }, children: [label && (jsxRuntime.jsx(material.Typography, { sx: {
3287
- display: 'block',
3288
- ...TYPOGRAPHY.textFieldLabel,
3289
- color: COLOR_GRAY[800],
3290
- marginBottom: '4px',
3291
- }, children: label })), jsxRuntime.jsx(StyledTextField, { placeholder: placeholder, value: value, disabled: disabled, error: error, helperText: error ? errorMessage : helperText, size: "small", fullWidth: true, onChange: onChange, InputProps: {
3292
- startAdornment: iconBefore ? jsxRuntime.jsx(material.InputAdornment, { position: "start", children: iconBefore }) : undefined,
3293
- endAdornment: iconAfter ? jsxRuntime.jsx(material.InputAdornment, { position: "end", children: iconAfter }) : undefined,
3294
- }, sx: {
3295
- '& .MuiOutlinedInput-root': {
3296
- borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
3297
- ...(success && {
3298
- '&.Mui-focused': {
3299
- boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
3300
- },
3301
- }),
3302
- },
3303
- }, ...props }), success && !error && successMessage && (jsxRuntime.jsx(material.Typography, { sx: {
3304
- ...TYPOGRAPHY.textFieldHelper,
3305
- color: COLOR_SUCCESS[500],
3306
- marginTop: '4px',
3307
- }, children: successMessage }))] }));
3308
- };
3309
2011
 
3310
- const SX_STYLES = {
2012
+ ({
3311
2013
  progressBar: {
3312
- position: 'absolute',
3313
- top: 0,
3314
- left: 0,
3315
- height: '100%',
3316
- backgroundColor: COLOR_GRAY[200],
3317
- opacity: 0.8,
3318
- transition: 'width 0.3s ease, opacity 0.3s ease',
3319
- animation: 'wave 1.5s linear infinite',
3320
- '@keyframes wave': {
3321
- '0%': { backgroundPosition: '0% 0%' },
3322
- '50%': { backgroundPosition: '100% 0%' },
3323
- '100%': { backgroundPosition: '0% 0%' },
3324
- },
3325
- },
3326
- imageIcon: {
3327
- width: '40px',
3328
- height: '40px',
3329
- flexShrink: 0,
3330
- position: 'relative',
3331
- zIndex: 1,
3332
- },
3333
- contentBox: {
3334
- flexGrow: 1,
3335
- minWidth: 0,
3336
- position: 'relative',
3337
- zIndex: 1,
3338
- },
3339
- textBox: {
3340
- minWidth: 0,
3341
- flexGrow: 1,
3342
- mr: 2,
3343
- },
3344
- actionBox: {
3345
- flexShrink: 0,
3346
- display: 'flex',
3347
- alignItems: 'center',
3348
- position: 'relative',
3349
- zIndex: 1,
3350
- },
3351
- linearProgress: {
3352
- mt: 1,
3353
- width: '100%',
3354
- },
3355
- };
3356
-
3357
- const VIDEO_EXTENSIONS = ['mp4', 'mov', 'avi', 'wmv', 'flv', 'mkv', 'webm'];
3358
- const ICON_PATH = {
3359
- VIDEO: '/images/icon/film.svg',
3360
- FILE: '/images/icon/file.svg',
3361
- };
3362
- // ============================================================================
3363
- // Helper Functions
3364
- // ============================================================================
3365
- const formatFileSize = (bytes) => {
3366
- if (bytes === 0)
3367
- return '0 Bytes';
3368
- const k = 1024;
3369
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
3370
- const i = Math.floor(Math.log(bytes) / Math.log(k));
3371
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
3372
- };
3373
- const getFileIcon = (fileName) => {
3374
- const ext = fileName.split('.').pop()?.toLowerCase();
3375
- return ext && VIDEO_EXTENSIONS.includes(ext) ? ICON_PATH.VIDEO : ICON_PATH.FILE;
3376
- };
3377
- // ============================================================================
3378
- // Component
3379
- // ============================================================================
3380
- const UploaderItemComponent = ({ file, progress = 0, status = 'pending', onDelete, onRetry, isProcess = true, borderSuccess, borderError = COLOR_ERROR[600], sx, }) => {
3381
- // Memoize
3382
- const iconSrc = React.useMemo(() => getFileIcon(file.name), [file.name]);
3383
- // Status flags
3384
- const isCompleted = status === 'success';
3385
- const isFailed = status === 'failed';
3386
- const isUploading = status === 'uploading';
3387
- // Determine colors based on status
3388
- const borderColor = isFailed
3389
- ? String(borderError)
3390
- : isCompleted && borderSuccess
3391
- ? String(borderSuccess)
3392
- : '#e0e0e0';
3393
- const textColor = isFailed ? String(borderError) : '#737373';
3394
- const subtitleColor = isFailed ? String(borderError) : '#a3a3a3';
3395
- // Render progress info
3396
- const renderProgressInfo = () => {
3397
- if (isUploading && !isCompleted)
3398
- return ` • ${Math.round(progress)}% uploaded`;
3399
- if (isFailed)
3400
- return ' • Upload failed';
3401
- return '';
3402
- };
3403
- return (jsxRuntime.jsxs(material.Box, { sx: {
3404
- display: 'flex',
3405
- alignItems: 'flex-start',
3406
- padding: '16px',
3407
- border: '1px solid',
3408
- borderColor: borderColor,
3409
- borderRadius: '8px',
3410
- gap: '16px',
3411
- position: 'relative',
3412
- overflow: 'hidden',
3413
- transition: 'background-color 0.3s ease',
3414
- backgroundColor: 'background.paper',
3415
- ...sx,
3416
- }, children: [!isProcess && isUploading && !isCompleted && !isFailed && (jsxRuntime.jsx(material.Box, { sx: { ...SX_STYLES.progressBar, width: `${progress}%` } })), jsxRuntime.jsx(material.Box, { component: "img", src: iconSrc, alt: "file icon", sx: SX_STYLES.imageIcon }), jsxRuntime.jsxs(material.Box, { sx: SX_STYLES.contentBox, children: [jsxRuntime.jsxs(StackRowAlignCenterJustBetween, { children: [jsxRuntime.jsxs(material.Box, { sx: SX_STYLES.textBox, children: [jsxRuntime.jsx(material.Typography, { noWrap: true, title: file.name, sx: { ...TYPOGRAPHY.text14Medium, color: textColor }, children: file.name }), jsxRuntime.jsxs(material.Typography, { sx: { ...TYPOGRAPHY.text14Regular, color: subtitleColor }, children: [formatFileSize(file.size), renderProgressInfo()] })] }), jsxRuntime.jsxs(material.Box, { sx: SX_STYLES.actionBox, children: [!isProcess && isUploading && !isCompleted && (jsxRuntime.jsx(material.Box, { sx: { position: 'relative', display: 'inline-flex', mr: 1 }, children: jsxRuntime.jsx(material.CircularProgress, { variant: "determinate", value: progress, color: "success", size: 24 }) })), isCompleted && jsxRuntime.jsx(CheckCircleIcon, { color: "success", sx: { mr: 1 } }), isFailed && onRetry && (jsxRuntime.jsx(material.IconButton, { size: "small", onClick: onRetry, title: "Retry upload", children: jsxRuntime.jsx(RefreshIcon, { fontSize: "small", sx: { color: borderError } }) })), !isFailed && onDelete && (jsxRuntime.jsx(material.IconButton, { size: "small", onClick: onDelete, children: jsxRuntime.jsx(IconElement, { icon: "delete" }) }))] })] }), isProcess && isUploading && (jsxRuntime.jsx(material.Box, { sx: SX_STYLES.linearProgress, children: jsxRuntime.jsx(material.LinearProgress, { variant: "determinate", value: progress, color: "success", sx: { height: 8, borderRadius: 4 } }) }))] })] }));
3417
- };
3418
-
3419
- const UploaderComponent = ({ onFilesSelected, accept = '*', multiple = true, children, sx, labelSx, uploadLabel = 'Click to upload', appearance, files: externalFiles, onDeleteFile, onRetryFile, borderError, }) => {
3420
- const fileInputRef = React.useRef(null);
3421
- const [isDragging, setIsDragging] = React.useState(false);
3422
- // Sử dụng external files nếu có, nếu không hiển thị rỗng
3423
- const displayFiles = externalFiles || [];
3424
- const handleClick = () => {
3425
- fileInputRef.current?.click();
3426
- };
3427
- const handleFileChange = (event) => {
3428
- const selectedFiles = event.target.files;
3429
- if (selectedFiles) {
3430
- const fileArray = Array.from(selectedFiles);
3431
- onFilesSelected(fileArray);
3432
- }
3433
- event.target.value = '';
3434
- };
3435
- const handleDeleteFile = (file) => {
3436
- if (onDeleteFile) {
3437
- onDeleteFile(file);
3438
- }
3439
- };
3440
- const handleDragOver = (e) => {
3441
- e.preventDefault();
3442
- e.stopPropagation();
3443
- setIsDragging(true);
3444
- };
3445
- const handleDragLeave = (e) => {
3446
- e.preventDefault();
3447
- e.stopPropagation();
3448
- setIsDragging(false);
3449
- };
3450
- const handleDrop = (e) => {
3451
- e.preventDefault();
3452
- e.stopPropagation();
3453
- setIsDragging(false);
3454
- const files = e.dataTransfer.files;
3455
- if (files) {
3456
- onFilesSelected(Array.from(files));
3457
- }
3458
- };
3459
- return (jsxRuntime.jsxs(material.Box, { sx: {
3460
- border: '2px solid',
3461
- borderColor: appearance?.borderColor || 'action.selected',
3462
- borderRadius: '8px',
3463
- padding: '32px 24px',
3464
- textAlign: 'center',
3465
- cursor: 'pointer',
3466
- transition: 'all 0.3s ease',
3467
- backgroundColor: isDragging ? 'action.selected' : appearance?.background || 'background.paper',
3468
- '&:hover': {
3469
- borderColor: appearance?.borderColorHover || appearance?.borderColor || 'primary.main',
3470
- filter: 'brightness(0.92)',
3471
- },
3472
- ...sx,
3473
- }, onClick: handleClick, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, style: { display: 'none' } }), displayFiles.length === 0 && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: children ? (jsxRuntime.jsxs(material.Box, { children: [children, jsxRuntime.jsxs(material.Box, { component: "p", sx: {
3474
- color: 'primary.main',
3475
- textDecoration: 'underline',
3476
- fontWeight: 500,
3477
- fontSize: '14px',
3478
- marginTop: '12px',
3479
- margin: '12px 0 0 0',
3480
- ...labelSx,
3481
- }, children: [uploadLabel, " or drag and drop"] })] })) : (jsxRuntime.jsxs(material.Box, { children: [jsxRuntime.jsx(material.Box, { component: "img", src: "/images/icon/uploader.svg", alt: "Upload icon", sx: {
3482
- width: '46px',
3483
- height: '46px',
3484
- marginBottom: '12px',
3485
- } }), jsxRuntime.jsxs(material.Box, { component: "p", sx: {
3486
- color: 'primary.main',
3487
- textDecoration: 'underline',
3488
- fontWeight: 500,
3489
- fontSize: '14px',
3490
- margin: 0,
3491
- ...labelSx,
3492
- }, children: [uploadLabel, " or drag and drop"] })] })) })), displayFiles && displayFiles.length > 0 && (jsxRuntime.jsx(material.Box, { sx: {
3493
- marginTop: '24px',
3494
- display: 'flex',
3495
- flexDirection: 'column',
3496
- gap: '12px',
3497
- textAlign: 'left',
3498
- }, onClick: (e) => e.stopPropagation(), children: displayFiles.map((item, index) => (jsxRuntime.jsx(UploaderItemComponent, { file: item.file, progress: item.progress, status: item.status, isProcess: item.isProcess ?? true, onDelete: () => handleDeleteFile(item.file), onRetry: () => onRetryFile?.(item.id), borderSuccess: appearance?.borderSuccess, borderError: borderError }, item.id || index))) }))] }));
3499
- };
2014
+ backgroundColor: COLOR_GRAY[200]}});
3500
2015
 
3501
2016
  exports.AVATAR_SIZES = AVATAR_SIZES;
3502
2017
  exports.AvatarComponent = AvatarComponent;
3503
- exports.AvatarGroup = AvatarGroupComponent;
3504
- exports.AvatarLabelGroup = AvatarLabelGroupComponent;
3505
- exports.AvatarProfile = AvatarProfileComponent;
3506
- exports.AvatarUser = AvatarUserComponent;
2018
+ exports.AvatarGroupComponent = AvatarGroupComponent;
2019
+ exports.AvatarLabelGroupComponent = AvatarLabelGroupComponent;
2020
+ exports.AvatarProfileComponent = AvatarProfileComponent;
2021
+ exports.AvatarUserComponent = AvatarUserComponent;
3507
2022
  exports.BADGE_FONT_SIZES = BADGE_FONT_SIZES;
3508
2023
  exports.BADGE_SIZES = BADGE_SIZES;
3509
2024
  exports.BadgeImage = BadgeImage;
3510
2025
  exports.BadgeLive = BadgeLive;
3511
2026
  exports.BadgeNumber = BadgeNumber;
3512
2027
  exports.BadgeOnline = BadgeOnline;
3513
- exports.Breadcrumbs = BreadcrumbsComponent;
3514
- exports.ButtonBar = ButtonBarComponent;
3515
2028
  exports.ButtonComponent = ButtonComponent;
3516
2029
  exports.COLOR_ACCENT = COLOR_ACCENT;
3517
2030
  exports.COLOR_BRAND = COLOR_BRAND;
@@ -3521,23 +2034,16 @@ exports.COLOR_INFO = COLOR_INFO;
3521
2034
  exports.COLOR_NEUTRAL = COLOR_NEUTRAL;
3522
2035
  exports.COLOR_SUCCESS = COLOR_SUCCESS;
3523
2036
  exports.COLOR_WARNING = COLOR_WARNING;
3524
- exports.Checkbox = CheckboxComponent;
3525
- exports.Chip = ChipComponent;
3526
- exports.DateField = DateFieldComponent;
3527
- exports.DateRangePicker = DateRangePickerComponent;
2037
+ exports.CheckboxContentComponent = CheckboxContentComponent;
3528
2038
  exports.DialogWrapper = DialogWrapper;
3529
- exports.DropdownField = DropdownFieldComponent;
3530
2039
  exports.FONT_FAMILY = FONT_FAMILY;
3531
2040
  exports.FONT_SIZE = FONT_SIZE;
3532
2041
  exports.FONT_STYLE = FONT_STYLE;
3533
2042
  exports.FONT_WEIGHT = FONT_WEIGHT;
3534
- exports.Grid = GridComponent;
3535
2043
  exports.IconElement = IconElement;
3536
2044
  exports.ImageElement = ImageElement;
3537
- exports.InputStepper = InputStepperComponent;
3538
2045
  exports.LINE_HEIGHT = LINE_HEIGHT;
3539
2046
  exports.LinkElement = LinkElement;
3540
- exports.LinkField = LinkFieldComponent;
3541
2047
  exports.LinkInternalElement = LinkInternalElement;
3542
2048
  exports.MAP_SIZE = MAP_SIZE;
3543
2049
  exports.Modal = ModalComponent;
@@ -3545,21 +2051,11 @@ exports.ModalCard = ModalCardComponent;
3545
2051
  exports.ModalDescription = ModalDescription;
3546
2052
  exports.ModalIcon = ModalIcon;
3547
2053
  exports.ModalTitle = ModalTitle;
3548
- exports.MoneyField = MoneyFieldComponent;
3549
- exports.PIN = PINComponent;
3550
- exports.PhoneField = PhoneNumberFieldComponent;
3551
2054
  exports.SIZE_EXTRA_LARGE = SIZE_EXTRA_LARGE;
3552
2055
  exports.STYLE = style_constant;
3553
- exports.SearchDropdown = SearchDropdownComponent;
3554
- exports.SearchField = SearchFieldComponent;
3555
- exports.Switch = SwitchComponent;
3556
2056
  exports.TYPOGRAPHY = TYPOGRAPHY;
3557
2057
  exports.TYPOGRAPHY_STYLES = TYPOGRAPHY_STYLES;
3558
- exports.Tabs = TabsComponent;
3559
- exports.TextArea = TextAreaComponent;
3560
- exports.TextField = TextFieldComponent;
3561
2058
  exports.TypographyOneLine = TypographyOneLine;
3562
- exports.Uploader = UploaderComponent;
3563
2059
  exports.createTypography = createTypography;
3564
2060
  exports.getBadgePosition = getBadgePosition;
3565
2061
  //# sourceMappingURL=index.js.map