sevatech-library 1.0.1 → 1.0.2

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.
Files changed (136) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/index.js +1568 -23
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/components/avatar/avatar-group.component.d.ts +1 -1
  5. package/dist/cjs/types/components/avatar/avatar-group.component.d.ts.map +1 -1
  6. package/dist/cjs/types/components/avatar/avatar-label-group.component.d.ts +1 -1
  7. package/dist/cjs/types/components/avatar/avatar-label-group.component.d.ts.map +1 -1
  8. package/dist/cjs/types/components/avatar/avatar-profile.component.d.ts +1 -1
  9. package/dist/cjs/types/components/avatar/avatar-profile.component.d.ts.map +1 -1
  10. package/dist/cjs/types/components/avatar/avatar-user.component.d.ts +1 -1
  11. package/dist/cjs/types/components/avatar/avatar-user.component.d.ts.map +1 -1
  12. package/dist/cjs/types/components/avatar/index.d.ts +4 -4
  13. package/dist/cjs/types/components/avatar/index.d.ts.map +1 -1
  14. package/dist/cjs/types/components/bread-crumbs/bread-crumbs.component.d.ts +1 -1
  15. package/dist/cjs/types/components/bread-crumbs/bread-crumbs.component.d.ts.map +1 -1
  16. package/dist/cjs/types/components/button-bar/button-bar.component.d.ts +1 -1
  17. package/dist/cjs/types/components/button-bar/button-bar.component.d.ts.map +1 -1
  18. package/dist/cjs/types/components/checkbox/checkbox-content.component.d.ts.map +1 -1
  19. package/dist/cjs/types/components/checkbox/checkbox.component.d.ts +1 -1
  20. package/dist/cjs/types/components/checkbox/checkbox.component.d.ts.map +1 -1
  21. package/dist/cjs/types/components/chip/chip.component.d.ts +1 -1
  22. package/dist/cjs/types/components/chip/chip.component.d.ts.map +1 -1
  23. package/dist/cjs/types/components/date-field/date-field.component.d.ts +1 -1
  24. package/dist/cjs/types/components/date-field/date-field.component.d.ts.map +1 -1
  25. package/dist/cjs/types/components/date-range-picker/date-range-picker.component.d.ts +1 -1
  26. package/dist/cjs/types/components/date-range-picker/date-range-picker.component.d.ts.map +1 -1
  27. package/dist/cjs/types/components/dropdown-field/dropdown-field.component.d.ts +1 -1
  28. package/dist/cjs/types/components/dropdown-field/dropdown-field.component.d.ts.map +1 -1
  29. package/dist/cjs/types/components/grid/grid.component.d.ts +1 -1
  30. package/dist/cjs/types/components/grid/grid.component.d.ts.map +1 -1
  31. package/dist/cjs/types/components/input-stepper/input-stepper-skeleton.d.ts +1 -1
  32. package/dist/cjs/types/components/input-stepper/input-stepper-skeleton.d.ts.map +1 -1
  33. package/dist/cjs/types/components/input-stepper/input-stepper.component.d.ts +1 -1
  34. package/dist/cjs/types/components/input-stepper/input-stepper.component.d.ts.map +1 -1
  35. package/dist/cjs/types/components/link-field/link-field.component.d.ts +1 -1
  36. package/dist/cjs/types/components/link-field/link-field.component.d.ts.map +1 -1
  37. package/dist/cjs/types/components/modal/index.d.ts +2 -2
  38. package/dist/cjs/types/components/modal/index.d.ts.map +1 -1
  39. package/dist/cjs/types/components/modal/modal-card.component.d.ts +1 -1
  40. package/dist/cjs/types/components/modal/modal-card.component.d.ts.map +1 -1
  41. package/dist/cjs/types/components/modal/modal.component.d.ts +1 -1
  42. package/dist/cjs/types/components/modal/modal.component.d.ts.map +1 -1
  43. package/dist/cjs/types/components/money-field/money-field.component.d.ts +1 -1
  44. package/dist/cjs/types/components/money-field/money-field.component.d.ts.map +1 -1
  45. package/dist/cjs/types/components/phone-number-field/phone-number-field.component.d.ts +1 -1
  46. package/dist/cjs/types/components/phone-number-field/phone-number-field.component.d.ts.map +1 -1
  47. package/dist/cjs/types/components/pin/pin.component.d.ts +1 -1
  48. package/dist/cjs/types/components/pin/pin.component.d.ts.map +1 -1
  49. package/dist/cjs/types/components/search-dropdown/search-dropdown.component.d.ts +1 -1
  50. package/dist/cjs/types/components/search-dropdown/search-dropdown.component.d.ts.map +1 -1
  51. package/dist/cjs/types/components/search-field/search-field.component.d.ts +1 -1
  52. package/dist/cjs/types/components/search-field/search-field.component.d.ts.map +1 -1
  53. package/dist/cjs/types/components/switch/switch-content.component.d.ts +1 -1
  54. package/dist/cjs/types/components/switch/switch-content.component.d.ts.map +1 -1
  55. package/dist/cjs/types/components/switch/switch.component.d.ts +1 -1
  56. package/dist/cjs/types/components/switch/switch.component.d.ts.map +1 -1
  57. package/dist/cjs/types/components/tab/tab.component.d.ts +1 -1
  58. package/dist/cjs/types/components/tab/tab.component.d.ts.map +1 -1
  59. package/dist/cjs/types/components/text-area/text-area.component.d.ts +1 -1
  60. package/dist/cjs/types/components/text-area/text-area.component.d.ts.map +1 -1
  61. package/dist/cjs/types/components/text-field/text-field.component.d.ts +1 -1
  62. package/dist/cjs/types/components/text-field/text-field.component.d.ts.map +1 -1
  63. package/dist/cjs/types/components/typography/typography-limit-one-line.component.d.ts +1 -1
  64. package/dist/cjs/types/components/typography/typography-limit-one-line.component.d.ts.map +1 -1
  65. package/dist/cjs/types/components/uploader/uploader-item.component.d.ts +1 -1
  66. package/dist/cjs/types/components/uploader/uploader-item.component.d.ts.map +1 -1
  67. package/dist/cjs/types/components/uploader/uploader.component.d.ts +1 -1
  68. package/dist/cjs/types/components/uploader/uploader.component.d.ts.map +1 -1
  69. package/dist/esm/index.js +1551 -26
  70. package/dist/esm/index.js.map +1 -1
  71. package/dist/esm/types/components/avatar/avatar-group.component.d.ts +1 -1
  72. package/dist/esm/types/components/avatar/avatar-group.component.d.ts.map +1 -1
  73. package/dist/esm/types/components/avatar/avatar-label-group.component.d.ts +1 -1
  74. package/dist/esm/types/components/avatar/avatar-label-group.component.d.ts.map +1 -1
  75. package/dist/esm/types/components/avatar/avatar-profile.component.d.ts +1 -1
  76. package/dist/esm/types/components/avatar/avatar-profile.component.d.ts.map +1 -1
  77. package/dist/esm/types/components/avatar/avatar-user.component.d.ts +1 -1
  78. package/dist/esm/types/components/avatar/avatar-user.component.d.ts.map +1 -1
  79. package/dist/esm/types/components/avatar/index.d.ts +4 -4
  80. package/dist/esm/types/components/avatar/index.d.ts.map +1 -1
  81. package/dist/esm/types/components/bread-crumbs/bread-crumbs.component.d.ts +1 -1
  82. package/dist/esm/types/components/bread-crumbs/bread-crumbs.component.d.ts.map +1 -1
  83. package/dist/esm/types/components/button-bar/button-bar.component.d.ts +1 -1
  84. package/dist/esm/types/components/button-bar/button-bar.component.d.ts.map +1 -1
  85. package/dist/esm/types/components/checkbox/checkbox-content.component.d.ts.map +1 -1
  86. package/dist/esm/types/components/checkbox/checkbox.component.d.ts +1 -1
  87. package/dist/esm/types/components/checkbox/checkbox.component.d.ts.map +1 -1
  88. package/dist/esm/types/components/chip/chip.component.d.ts +1 -1
  89. package/dist/esm/types/components/chip/chip.component.d.ts.map +1 -1
  90. package/dist/esm/types/components/date-field/date-field.component.d.ts +1 -1
  91. package/dist/esm/types/components/date-field/date-field.component.d.ts.map +1 -1
  92. package/dist/esm/types/components/date-range-picker/date-range-picker.component.d.ts +1 -1
  93. package/dist/esm/types/components/date-range-picker/date-range-picker.component.d.ts.map +1 -1
  94. package/dist/esm/types/components/dropdown-field/dropdown-field.component.d.ts +1 -1
  95. package/dist/esm/types/components/dropdown-field/dropdown-field.component.d.ts.map +1 -1
  96. package/dist/esm/types/components/grid/grid.component.d.ts +1 -1
  97. package/dist/esm/types/components/grid/grid.component.d.ts.map +1 -1
  98. package/dist/esm/types/components/input-stepper/input-stepper-skeleton.d.ts +1 -1
  99. package/dist/esm/types/components/input-stepper/input-stepper-skeleton.d.ts.map +1 -1
  100. package/dist/esm/types/components/input-stepper/input-stepper.component.d.ts +1 -1
  101. package/dist/esm/types/components/input-stepper/input-stepper.component.d.ts.map +1 -1
  102. package/dist/esm/types/components/link-field/link-field.component.d.ts +1 -1
  103. package/dist/esm/types/components/link-field/link-field.component.d.ts.map +1 -1
  104. package/dist/esm/types/components/modal/index.d.ts +2 -2
  105. package/dist/esm/types/components/modal/index.d.ts.map +1 -1
  106. package/dist/esm/types/components/modal/modal-card.component.d.ts +1 -1
  107. package/dist/esm/types/components/modal/modal-card.component.d.ts.map +1 -1
  108. package/dist/esm/types/components/modal/modal.component.d.ts +1 -1
  109. package/dist/esm/types/components/modal/modal.component.d.ts.map +1 -1
  110. package/dist/esm/types/components/money-field/money-field.component.d.ts +1 -1
  111. package/dist/esm/types/components/money-field/money-field.component.d.ts.map +1 -1
  112. package/dist/esm/types/components/phone-number-field/phone-number-field.component.d.ts +1 -1
  113. package/dist/esm/types/components/phone-number-field/phone-number-field.component.d.ts.map +1 -1
  114. package/dist/esm/types/components/pin/pin.component.d.ts +1 -1
  115. package/dist/esm/types/components/pin/pin.component.d.ts.map +1 -1
  116. package/dist/esm/types/components/search-dropdown/search-dropdown.component.d.ts +1 -1
  117. package/dist/esm/types/components/search-dropdown/search-dropdown.component.d.ts.map +1 -1
  118. package/dist/esm/types/components/search-field/search-field.component.d.ts +1 -1
  119. package/dist/esm/types/components/search-field/search-field.component.d.ts.map +1 -1
  120. package/dist/esm/types/components/switch/switch-content.component.d.ts +1 -1
  121. package/dist/esm/types/components/switch/switch-content.component.d.ts.map +1 -1
  122. package/dist/esm/types/components/switch/switch.component.d.ts +1 -1
  123. package/dist/esm/types/components/switch/switch.component.d.ts.map +1 -1
  124. package/dist/esm/types/components/tab/tab.component.d.ts +1 -1
  125. package/dist/esm/types/components/tab/tab.component.d.ts.map +1 -1
  126. package/dist/esm/types/components/text-area/text-area.component.d.ts +1 -1
  127. package/dist/esm/types/components/text-area/text-area.component.d.ts.map +1 -1
  128. package/dist/esm/types/components/text-field/text-field.component.d.ts +1 -1
  129. package/dist/esm/types/components/text-field/text-field.component.d.ts.map +1 -1
  130. package/dist/esm/types/components/typography/typography-limit-one-line.component.d.ts +1 -1
  131. package/dist/esm/types/components/typography/typography-limit-one-line.component.d.ts.map +1 -1
  132. package/dist/esm/types/components/uploader/uploader-item.component.d.ts +1 -1
  133. package/dist/esm/types/components/uploader/uploader-item.component.d.ts.map +1 -1
  134. package/dist/esm/types/components/uploader/uploader.component.d.ts +1 -1
  135. package/dist/esm/types/components/uploader/uploader.component.d.ts.map +1 -1
  136. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -1,26 +1,26 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { Stack, TableCell, tableCellClasses, LinearProgress, linearProgressClasses, useTheme, Skeleton, Box, Icon, Typography, Link, CircularProgress, Button, Dialog, DialogContent, DialogActions, styled as styled$1, Autocomplete, Switch } from '@mui/material';
3
- import React, { useState } from 'react';
2
+ import { Stack, TableCell, tableCellClasses, LinearProgress, linearProgressClasses, useTheme, Skeleton, Box, Icon, Typography, Link, Breadcrumbs, IconButton, Menu, MenuItem, CircularProgress, Button, Chip, Select, Avatar, Container, TextField, InputAdornment, Dialog, DialogContent, DialogActions, styled as styled$1, Autocomplete, Switch } from '@mui/material';
3
+ import React, { useState, useRef, useMemo, useCallback, useEffect, useId } from 'react';
4
4
  import { styled } from '@mui/material/styles';
5
5
  import LinkIcon from '@mui/icons-material/Link';
6
6
  import CheckIcon from '@mui/icons-material/Check';
7
7
  import RemoveIcon from '@mui/icons-material/Remove';
8
8
  import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
9
- import '@mui/x-date-pickers/DatePicker';
10
- import '@mui/x-date-pickers/LocalizationProvider';
11
- import '@mui/x-date-pickers/AdapterDayjs';
12
- import 'dayjs';
13
- import '@mui/icons-material/Add';
9
+ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
10
+ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
11
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
12
+ import dayjs from 'dayjs';
13
+ import AddIcon from '@mui/icons-material/Add';
14
14
  import MuiTextField from '@mui/material/TextField';
15
15
  import { Form } from 'formik';
16
16
  import Slider from 'react-slick';
17
17
  import 'slick-carousel/slick/slick.css';
18
18
  import 'slick-carousel/slick/slick-theme.css';
19
- import '@mui/icons-material/AttachMoney';
20
- import '@mui/icons-material/Search';
21
- import 'framer-motion';
22
- import '@mui/icons-material/CheckCircle';
23
- import '@mui/icons-material/Refresh';
19
+ import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
20
+ import SearchIcon from '@mui/icons-material/Search';
21
+ import { LayoutGroup, motion } from 'framer-motion';
22
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
23
+ import RefreshIcon from '@mui/icons-material/Refresh';
24
24
 
25
25
  const AVATAR_SIZES = {
26
26
  xs: 24,
@@ -231,7 +231,7 @@ const FONT_SIZE_ICON = {
231
231
  medium: '19px',
232
232
  small: '12px',
233
233
  };
234
- const FONT_SIZE_LOADING = {
234
+ const FONT_SIZE_LOADING$1 = {
235
235
  large: 40,
236
236
  medium: 22.5,
237
237
  small: 16,
@@ -272,7 +272,7 @@ var style_constant = /*#__PURE__*/Object.freeze({
272
272
  BORDER_RADIUS_ELEMENT_TAG: BORDER_RADIUS_ELEMENT_TAG,
273
273
  BORDER_RADIUS_ELEMENT_WRAPPER: BORDER_RADIUS_ELEMENT_WRAPPER,
274
274
  FONT_SIZE_ICON: FONT_SIZE_ICON,
275
- FONT_SIZE_LOADING: FONT_SIZE_LOADING,
275
+ FONT_SIZE_LOADING: FONT_SIZE_LOADING$1,
276
276
  GAP_ICON_CONTENT_BY_SIZE: GAP_ICON_CONTENT_BY_SIZE,
277
277
  HEIGHT_DEFAULT_TEXT_FIELD_BUTTON: HEIGHT_DEFAULT_TEXT_FIELD_BUTTON,
278
278
  HEIGHT_ELEMENT_OTHER: HEIGHT_ELEMENT_OTHER,
@@ -908,7 +908,7 @@ styled(Stack)(() => ({
908
908
  alignItems: 'center',
909
909
  justifyContent: 'center',
910
910
  }));
911
- styled(Stack)(() => ({
911
+ const StackRowAlignCenterJustEnd = styled(Stack)(() => ({
912
912
  flexDirection: 'row',
913
913
  alignItems: 'center',
914
914
  justifyContent: 'flex-end',
@@ -921,7 +921,7 @@ styled(Stack)(() => ({
921
921
  flexDirection: 'row',
922
922
  justifyContent: 'space-between',
923
923
  }));
924
- styled(Stack)(() => ({
924
+ const StackRowAlignCenterJustBetween = styled(Stack)(() => ({
925
925
  flexDirection: 'row',
926
926
  alignItems: 'center',
927
927
  justifyContent: 'space-between',
@@ -1200,6 +1200,60 @@ const AvatarUserComponent = ({ title, description, descriptionHref, onDescriptio
1200
1200
  }, children: [jsx(LinkIcon, { sx: { fontSize: 16, color: descriptionColor } }), description] }))] })] }));
1201
1201
  };
1202
1202
 
1203
+ const SEPARATOR_URLS = {
1204
+ '>': '/images/icon/chevron-right.svg',
1205
+ '/': '/images/icon/slash-separator.svg',
1206
+ };
1207
+ const BreadcrumbsComponent = ({ items, separator = '>', maxItems = 5, idSelect, sx, sxItem, onChange, }) => {
1208
+ // state
1209
+ const [anchorEl, setAnchorEl] = useState(null);
1210
+ const showCollapsed = items.length > maxItems;
1211
+ const visibleItems = showCollapsed ? [items[0], ...items.slice(-2)] : items;
1212
+ const collapsedItems = showCollapsed ? items.slice(1, -2) : [];
1213
+ // function
1214
+ const handleMenuOpen = (event) => {
1215
+ setAnchorEl(event.currentTarget);
1216
+ };
1217
+ const handleMenuClose = () => {
1218
+ setAnchorEl(null);
1219
+ };
1220
+ const renderItem = (item) => {
1221
+ const isActive = item.id === idSelect;
1222
+ return (jsxs(Link, { href: item.href, onClick: (e) => {
1223
+ if (item.onClick) {
1224
+ e.preventDefault();
1225
+ item.onClick();
1226
+ }
1227
+ if (onChange) {
1228
+ onChange(item.id);
1229
+ }
1230
+ }, sx: {
1231
+ display: 'flex',
1232
+ alignItems: 'center',
1233
+ gap: GAP_ICON_CONTENT_BY_SIZE.medium,
1234
+ ...TYPOGRAPHY_STYLES.textSm.semiBold,
1235
+ color: isActive ? '#000000' : '#676E76',
1236
+ cursor: 'pointer',
1237
+ textDecoration: 'none',
1238
+ ...sxItem,
1239
+ }, children: [item.icon && jsx(IconElement, { icon: item.icon }), item.label] }, item.id));
1240
+ };
1241
+ const renderSeparator = () => jsx(ImageElement, { sx: { width: 14, height: 14 }, url: SEPARATOR_URLS[separator] });
1242
+ return (jsxs(React.Fragment, { children: [jsx(Breadcrumbs, { "aria-label": "breadcrumb", sx: { ...sx }, children: showCollapsed ? (jsxs(StackRowAlignCenter, { sx: { gap: GAP_ICON_CONTENT_BY_SIZE.small }, children: [renderItem(items[0]), renderSeparator(), jsx(IconButton, { size: "small", onClick: handleMenuOpen, sx: { p: 0, mt: 'auto' }, children: jsx(IconElement, { icon: "more_horiz" }) }), renderSeparator(), visibleItems.slice(1).map((item, idx) => (jsxs(React.Fragment, { children: [idx > 0 && renderSeparator(), renderItem(item)] }, item.id)))] })) : (items.map((item) => renderItem(item))) }), jsx(Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleMenuClose, disableScrollLock: true, children: collapsedItems.map((item) => {
1243
+ const isActive = item.id === idSelect;
1244
+ return (jsxs(MenuItem, { onClick: () => {
1245
+ if (item.onClick) {
1246
+ item.onClick();
1247
+ }
1248
+ handleMenuClose();
1249
+ }, sx: {
1250
+ gap: GAP_ICON_CONTENT_BY_SIZE.medium,
1251
+ color: isActive ? '#0F766E' : '#111827',
1252
+ bgcolor: isActive ? '#E0F2FE' : 'transparent',
1253
+ }, children: [item.icon && jsx(IconElement, { icon: item.icon }), item.label] }, item.id));
1254
+ }) })] }));
1255
+ };
1256
+
1203
1257
  /** Shade values mapping */
1204
1258
  const SHADE_VALUES = {
1205
1259
  light: 100,
@@ -1340,6 +1394,17 @@ const ButtonComponent = ({ variant = 'solid', color = 'brand', shade = 'dark', s
1340
1394
  return (jsx(Button, { sx: { ...buttonSx, ...sx }, disabled: disabled || loading, fullWidth: fullWidth, startIcon: !isIconOnly ? prefixContent : undefined, endIcon: !isIconOnly ? suffixContent : undefined, ...props, children: isIconOnly ? (jsxs(Fragment, { children: [prefixContent, suffixContent] })) : (jsxs(Fragment, { children: [activeDot, children] })) }));
1341
1395
  };
1342
1396
 
1397
+ const ButtonBarComponent = ({ layout, children, gap = 12, style }) => {
1398
+ const containerStyle = {
1399
+ display: 'flex',
1400
+ flexDirection: layout === 'horizontal' ? 'row' : 'column',
1401
+ gap: `${gap}px`,
1402
+ alignItems: layout === 'horizontal' ? 'center' : 'stretch',
1403
+ ...style,
1404
+ };
1405
+ return jsx("div", { style: containerStyle, children: children });
1406
+ };
1407
+
1343
1408
  const CHECKBOX_COLORS = {
1344
1409
  default: {
1345
1410
  border: '#D0D5DD',
@@ -1503,6 +1568,358 @@ const CheckboxContentComponent = ({ checked = false, disabled = false, title, co
1503
1568
  }, children: content }))] })] }));
1504
1569
  };
1505
1570
 
1571
+ const CHIP_LABEL_PADDING = {
1572
+ WITH_ICON_LEFT: '4px 8px 4px 4px',
1573
+ WITH_ICON_RIGHT: '4px 4px 4px 8px',
1574
+ NO_ICON: '4px 8px',
1575
+ };
1576
+ const CHIP_SIZE_CONFIG = {
1577
+ small: {
1578
+ height: 24,
1579
+ fontSize: '12px',
1580
+ borderRadius: '12px',
1581
+ iconSize: '16px',
1582
+ },
1583
+ medium: {
1584
+ height: 32,
1585
+ fontSize: '14px',
1586
+ borderRadius: '16px',
1587
+ iconSize: '18px',
1588
+ },
1589
+ large: {
1590
+ height: 40,
1591
+ fontSize: '16px',
1592
+ borderRadius: '20px',
1593
+ iconSize: '22px',
1594
+ },
1595
+ };
1596
+
1597
+ const getLabelPadding = (hasIcon, position) => {
1598
+ if (!hasIcon)
1599
+ return CHIP_LABEL_PADDING.NO_ICON;
1600
+ return position === 'left' ? CHIP_LABEL_PADDING.WITH_ICON_LEFT : CHIP_LABEL_PADDING.WITH_ICON_RIGHT;
1601
+ };
1602
+ const ChipIcon = ({ icon, size, sxIcon }) => (jsx(Box, { sx: {
1603
+ display: 'inline-flex',
1604
+ alignItems: 'center',
1605
+ justifyContent: 'center',
1606
+ borderRadius: '50%',
1607
+ padding: '2px',
1608
+ '& svg': {
1609
+ width: size,
1610
+ height: size,
1611
+ display: 'block',
1612
+ },
1613
+ ...sxIcon,
1614
+ }, children: icon }));
1615
+ const ChipComponent = ({ label, onAction, icon, disabled = false, clickable = true, sx, sxIcon, iconPosition = 'right', size = 'medium', }) => {
1616
+ const sizeConfig = CHIP_SIZE_CONFIG[size];
1617
+ const hasIcon = Boolean(icon);
1618
+ return (jsx(Chip, { disabled: disabled, clickable: clickable, onClick: onAction, label: jsxs(StackRowAlignCenter, { sx: { gap: '8px' }, children: [hasIcon && iconPosition === 'left' && jsx(ChipIcon, { icon: icon, size: sizeConfig.iconSize, sxIcon: sxIcon }), jsx(Box, { component: "span", children: label }), hasIcon && iconPosition === 'right' && jsx(ChipIcon, { icon: icon, size: sizeConfig.iconSize, sxIcon: sxIcon })] }), sx: {
1619
+ height: sizeConfig.height,
1620
+ fontSize: sizeConfig.fontSize,
1621
+ borderRadius: sizeConfig.borderRadius,
1622
+ cursor: clickable ? 'pointer' : 'default',
1623
+ backgroundColor: 'transparent',
1624
+ border: '1px solid #D1D5DB',
1625
+ '& .MuiChip-label': {
1626
+ padding: getLabelPadding(hasIcon, iconPosition),
1627
+ display: 'flex',
1628
+ alignItems: 'center',
1629
+ },
1630
+ '& .MuiChip-label svg': {
1631
+ flexShrink: 0,
1632
+ color: '#4B5563',
1633
+ },
1634
+ '&.Mui-disabled': {
1635
+ opacity: 0.5,
1636
+ color: '#9E9E9E',
1637
+ },
1638
+ ...sx,
1639
+ } }));
1640
+ };
1641
+
1642
+ 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 }) => {
1643
+ // Convert string to Dayjs if needed
1644
+ const dayjsValue = value && typeof value === 'string' ? dayjs(value, format) : value instanceof dayjs ? value : null;
1645
+ const handleDateChange = (date) => {
1646
+ onChange?.(date);
1647
+ };
1648
+ // Disable past dates function
1649
+ const shouldDisableDate = (date) => {
1650
+ if (!disablePastDates)
1651
+ return false;
1652
+ return date.isBefore(dayjs(), 'day');
1653
+ };
1654
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
1655
+ display: 'block',
1656
+ ...TYPOGRAPHY.textFieldLabel,
1657
+ color: COLOR_GRAY[800],
1658
+ marginBottom: '4px',
1659
+ }, children: label })), jsx(LocalizationProvider, { dateAdapter: AdapterDayjs, adapterLocale: locale, children: jsx(DatePicker, { value: dayjsValue, onChange: handleDateChange, disabled: disabled, format: format, shouldDisableDate: shouldDisableDate, slotProps: {
1660
+ textField: {
1661
+ placeholder,
1662
+ error: error || false,
1663
+ helperText: error ? errorMessage : helperText,
1664
+ size: 'small',
1665
+ fullWidth: true,
1666
+ disabled: disabled,
1667
+ },
1668
+ openPickerButton: {
1669
+ size: 'small',
1670
+ },
1671
+ }, slots: {
1672
+ openPickerIcon: (props) => jsx(IconElement, { icon: "calendar_today", ...props }),
1673
+ }, sx: {
1674
+ width: '100%',
1675
+ '& .MuiPickersInputBase-root': {
1676
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
1677
+ '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
1678
+ '&:hover fieldset': { borderColor: COLOR_NEUTRAL[400] },
1679
+ '&.Mui-focused fieldset': { borderColor: COLOR_NEUTRAL[300], borderWidth: '2px' },
1680
+ '&.Mui-focused': {
1681
+ boxShadow: '0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px #FEE4E2',
1682
+ },
1683
+ '&.Mui-disabled': {
1684
+ backgroundColor: COLOR_NEUTRAL[100],
1685
+ '& fieldset': { borderColor: COLOR_NEUTRAL[200] },
1686
+ '& input': { color: COLOR_NEUTRAL[400], WebkitTextFillColor: COLOR_NEUTRAL[400] },
1687
+ },
1688
+ '&.Mui-error fieldset': { borderColor: COLOR_ERROR[500] },
1689
+ '&.Mui-error.Mui-focused fieldset': { borderColor: COLOR_ERROR[500], borderWidth: '2px' },
1690
+ '&.Mui-error.Mui-focused': {
1691
+ boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px rgba(255, 66, 79, 0.15)`,
1692
+ },
1693
+ ...(success && {
1694
+ '&.Mui-focused': {
1695
+ boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
1696
+ },
1697
+ }),
1698
+ },
1699
+ '& .MuiInputBase-input': {
1700
+ ...TYPOGRAPHY.text14Regular,
1701
+ padding: '12px 8px',
1702
+ color: COLOR_GRAY[900],
1703
+ '&::placeholder': { color: COLOR_NEUTRAL[400], opacity: 0.7 },
1704
+ },
1705
+ }, ...props }) }), success && !error && successMessage && (jsx(Typography, { sx: {
1706
+ ...TYPOGRAPHY.textFieldHelper,
1707
+ color: COLOR_SUCCESS[500],
1708
+ marginTop: '4px',
1709
+ }, children: successMessage }))] }));
1710
+ };
1711
+
1712
+ 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 }) => {
1713
+ // Convert string to Dayjs if needed
1714
+ const dayjsFromDate = fromDate && typeof fromDate === 'string' ? dayjs(fromDate, format) : fromDate instanceof dayjs ? fromDate : null;
1715
+ const dayjsToDate = toDate && typeof toDate === 'string' ? dayjs(toDate, format) : toDate instanceof dayjs ? toDate : null;
1716
+ // State for picker
1717
+ const [pickerOpen, setPickerOpen] = useState(false);
1718
+ const [selectingPhase, setSelectingPhase] = useState('from');
1719
+ const inputRef = useRef(null);
1720
+ // Disable past dates function
1721
+ const shouldDisableDate = (date) => {
1722
+ if (!disablePastDates)
1723
+ return false;
1724
+ return date.isBefore(dayjs(), 'day');
1725
+ };
1726
+ const handleInputClick = () => {
1727
+ setPickerOpen(true);
1728
+ // Only reset to 'from' if both dates are empty (fresh start)
1729
+ if (!dayjsFromDate && !dayjsToDate) {
1730
+ setSelectingPhase('from');
1731
+ }
1732
+ };
1733
+ const handleDateChange = (date) => {
1734
+ if (selectingPhase === 'from') {
1735
+ onChange?.([date, dayjsToDate]);
1736
+ if (date) {
1737
+ // Auto switch to toDate selection
1738
+ setSelectingPhase('to');
1739
+ }
1740
+ }
1741
+ else {
1742
+ onChange?.([dayjsFromDate, date]);
1743
+ // Keep picker open for user to potentially change toDate
1744
+ }
1745
+ };
1746
+ return (jsx(LocalizationProvider, { dateAdapter: AdapterDayjs, adapterLocale: locale, children: jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
1747
+ display: 'block',
1748
+ ...TYPOGRAPHY.textFieldLabel,
1749
+ color: COLOR_GRAY[800],
1750
+ marginBottom: '4px',
1751
+ }, children: label })), jsxs(Box, { ref: inputRef, onClick: !disabled ? handleInputClick : undefined, sx: {
1752
+ display: 'flex',
1753
+ alignItems: 'center',
1754
+ gap: '12px',
1755
+ padding: '12px 8px',
1756
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
1757
+ border: `1px solid ${error ? COLOR_ERROR[500] : COLOR_NEUTRAL[300]}`,
1758
+ backgroundColor: disabled ? COLOR_NEUTRAL[100] : 'white',
1759
+ cursor: disabled ? 'default' : 'pointer',
1760
+ transition: 'all 0.2s ease',
1761
+ ...(disabled
1762
+ ? {}
1763
+ : {
1764
+ '&:hover': {
1765
+ borderColor: error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
1766
+ },
1767
+ }),
1768
+ }, children: [jsxs(Typography, { sx: {
1769
+ flex: 1,
1770
+ ...TYPOGRAPHY.text14Regular,
1771
+ color: dayjsFromDate || dayjsToDate ? COLOR_GRAY[900] : COLOR_NEUTRAL[400],
1772
+ }, children: [dayjsFromDate ? dayjsFromDate.format(format) : format, " \u2192", ' ', dayjsToDate ? dayjsToDate.format(format) : format] }), jsx(Box, { sx: {
1773
+ display: 'flex',
1774
+ alignItems: 'center',
1775
+ justifyContent: 'center',
1776
+ width: '24px',
1777
+ height: '24px',
1778
+ color: COLOR_NEUTRAL[400],
1779
+ }, children: jsx(IconElement, { icon: "calendar_today" }) })] }), jsx(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: {
1780
+ textField: {
1781
+ hidden: true,
1782
+ size: 'small',
1783
+ sx: {
1784
+ display: 'none',
1785
+ },
1786
+ },
1787
+ popper: {
1788
+ anchorEl: inputRef.current,
1789
+ placement: 'bottom-start',
1790
+ },
1791
+ } }), helperText && !error && !success && (jsx(Typography, { sx: {
1792
+ ...TYPOGRAPHY.textFieldHelper,
1793
+ color: COLOR_NEUTRAL[400],
1794
+ marginTop: '4px',
1795
+ }, children: helperText })), error && errorMessage && (jsx(Typography, { sx: {
1796
+ ...TYPOGRAPHY.textFieldHelper,
1797
+ color: COLOR_ERROR[500],
1798
+ marginTop: '4px',
1799
+ }, children: errorMessage })), success && !error && successMessage && (jsx(Typography, { sx: {
1800
+ ...TYPOGRAPHY.textFieldHelper,
1801
+ color: COLOR_SUCCESS[500],
1802
+ marginTop: '4px',
1803
+ }, children: successMessage }))] }) }));
1804
+ };
1805
+
1806
+ 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, }) => {
1807
+ const borderRadiusValue = borderRadius === 'max' ? '100px' : `${borderRadius}px`;
1808
+ const selectedOption = useMemo(() => options.find((opt) => opt.value === value), [options, value]);
1809
+ const getHelperText = useCallback(() => {
1810
+ if (error && errorMessage)
1811
+ return errorMessage;
1812
+ if (success && successMessage)
1813
+ return successMessage;
1814
+ if (helperText)
1815
+ return helperText;
1816
+ return '';
1817
+ }, [error, errorMessage, success, successMessage, helperText]);
1818
+ const getHelperTextColor = useCallback(() => {
1819
+ if (error)
1820
+ return COLOR_ERROR[500];
1821
+ if (success)
1822
+ return COLOR_SUCCESS[500];
1823
+ return COLOR_NEUTRAL[400];
1824
+ }, [error, success]);
1825
+ const getBorderColor = useCallback(() => {
1826
+ if (error)
1827
+ return COLOR_ERROR[500];
1828
+ if (success)
1829
+ return COLOR_SUCCESS[500];
1830
+ return COLOR_NEUTRAL[300];
1831
+ }, [error, success]);
1832
+ const selectSx = useMemo(() => ({
1833
+ '& .MuiOutlinedInput-root': {
1834
+ borderRadius: borderRadiusValue,
1835
+ backgroundColor: disabled ? COLOR_NEUTRAL[100] : 'white',
1836
+ transition: 'all 0.2s ease',
1837
+ '& fieldset': {
1838
+ borderColor: getBorderColor(),
1839
+ },
1840
+ '&:hover fieldset': {
1841
+ borderColor: disabled ? getBorderColor() : error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
1842
+ },
1843
+ '&.Mui-focused fieldset': {
1844
+ borderColor: error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
1845
+ borderWidth: '1.5px',
1846
+ },
1847
+ },
1848
+ '& .MuiOutlinedInput-input': {
1849
+ padding: '12px 14px',
1850
+ color: COLOR_GRAY[900],
1851
+ '&::placeholder': {
1852
+ color: COLOR_NEUTRAL[400],
1853
+ opacity: 1,
1854
+ },
1855
+ },
1856
+ }), [borderRadiusValue, disabled, error, getBorderColor]);
1857
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
1858
+ display: 'block',
1859
+ ...TYPOGRAPHY.textFieldLabel,
1860
+ color: COLOR_GRAY[800],
1861
+ marginBottom: '4px',
1862
+ }, children: label })), jsx(Select, { fullWidth: true, value: value || '', onChange: (e) => {
1863
+ const selectedValue = e.target.value;
1864
+ const matchedOption = options.find((opt) => String(opt.value) === String(selectedValue));
1865
+ if (matchedOption) {
1866
+ onChange?.(matchedOption.value);
1867
+ }
1868
+ }, disabled: disabled, displayEmpty: true, MenuProps: {
1869
+ disableScrollLock: true,
1870
+ }, renderValue: () => {
1871
+ if (!value) {
1872
+ return (jsx(Box, { sx: { color: COLOR_NEUTRAL[400], display: 'flex', alignItems: 'center', gap: '8px' }, children: placeholder }));
1873
+ }
1874
+ return (jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [selectedOption?.statusIndicator && (jsx(Box, { sx: {
1875
+ width: '8px',
1876
+ height: '8px',
1877
+ borderRadius: '50%',
1878
+ backgroundColor: '#4CAF50',
1879
+ } })), selectedOption?.avatar && (jsx(Avatar, { src: selectedOption.avatar, sx: {
1880
+ width: '24px',
1881
+ height: '24px',
1882
+ fontSize: '12px',
1883
+ } })), selectedOption?.icon && jsx(IconElement, { icon: selectedOption.icon }), jsx(Typography, { sx: { color: COLOR_GRAY[900], ...TYPOGRAPHY.text14Regular }, children: selectedOption?.label })] }));
1884
+ }, sx: selectSx, children: options.map((option) => (jsx(MenuItem, { value: option.value, children: jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: '8px', width: '100%' }, children: [option.statusIndicator && (jsx(Box, { sx: {
1885
+ width: '8px',
1886
+ height: '8px',
1887
+ borderRadius: '50%',
1888
+ backgroundColor: '#4CAF50',
1889
+ } })), option.avatar && (jsx(Avatar, { src: option.avatar, sx: {
1890
+ width: '24px',
1891
+ height: '24px',
1892
+ fontSize: '12px',
1893
+ } })), option.icon && jsx(IconElement, { icon: option.icon }), jsx(Typography, { sx: { color: COLOR_GRAY[900], ...TYPOGRAPHY.text14Regular }, children: option.label }), value === option.value && (jsx(Box, { sx: { marginLeft: 'auto', display: 'flex', alignItems: 'center' }, children: jsx(IconElement, { icon: "check", sx: { color: checkIconColor } }) }))] }) }, option.value))) }), getHelperText() && (jsx(Box, { sx: {
1894
+ color: getHelperTextColor(),
1895
+ marginTop: '4px',
1896
+ ...TYPOGRAPHY.textFieldHelper,
1897
+ }, children: getHelperText() }))] }));
1898
+ };
1899
+
1900
+ const BACKGROUND_COLOR_GRID = '#FFFFFF';
1901
+ const COLOR_CONTENT_GRID = '#27272A';
1902
+ const BORDER_RADIUS_GRID_CONTAINER = 8;
1903
+ const BORDER_RADIUS_GRID = 4;
1904
+ const GridComponent = ({ sx = {}, sxContainer = {}, content = 'Grids', children, }) => {
1905
+ return (jsxs(Stack, { sx: {
1906
+ bgcolor: BACKGROUND_COLOR_GRID,
1907
+ p: BORDER_RADIUS_GRID_CONTAINER,
1908
+ borderRadius: BORDER_RADIUS_GRID_CONTAINER,
1909
+ gap: BORDER_RADIUS_GRID,
1910
+ ...sx,
1911
+ }, children: [content && (jsx(Typography, { sx: {
1912
+ color: COLOR_CONTENT_GRID,
1913
+ ...TYPOGRAPHY_STYLES.lg.bold,
1914
+ }, children: content })), jsx(Container, { maxWidth: false, sx: {
1915
+ bgcolor: BACKGROUND_COLOR_GRID,
1916
+ borderRadius: BORDER_RADIUS_GRID,
1917
+ minHeight: 400,
1918
+ boxShadow: '0 0 8px -4px rgba(16, 24, 40, 0.3)',
1919
+ ...sxContainer,
1920
+ }, children: children })] }));
1921
+ };
1922
+
1506
1923
  var BorderRadius;
1507
1924
  (function (BorderRadius) {
1508
1925
  BorderRadius["SQUARE"] = "4px";
@@ -1523,6 +1940,159 @@ var ButtonSize;
1523
1940
  ButtonSize[ButtonSize["SMALL"] = 32] = "SMALL";
1524
1941
  ButtonSize[ButtonSize["MEDIUM"] = 40] = "MEDIUM";
1525
1942
  })(ButtonSize || (ButtonSize = {}));
1943
+ const Colors = {
1944
+ BORDER_COLOR_BUTTON: '#07554B',
1945
+ BORDER_COLOR_DISABLE: '#0000000D',
1946
+ HOVER_BG_COLOR: 'rgba(7, 85, 75, 0.04)',
1947
+ BACKGROUND_COLOR: '#FFFFFF',
1948
+ TEXT_COLOR_READONLY: '#27272A',
1949
+ };
1950
+ const FONT_SIZE_LOADING = {
1951
+ large: 40,
1952
+ };
1953
+ const BORDER_TEXT_FIELD_LOADING = 20;
1954
+
1955
+ const InputStepperSkeleton = ({ orientation, buttonShape }) => {
1956
+ return (jsxs(Box, { display: "inline-flex", flexDirection: orientation === Orientation.HORIZONTAL ? 'row' : 'column', alignItems: "center", gap: 1, children: [jsx(Skeleton, { variant: buttonShape === ShapeType.CIRCLE ? 'circular' : 'rectangular', width: FONT_SIZE_LOADING.large, height: FONT_SIZE_LOADING.large, sx: {
1957
+ borderRadius: buttonShape === ShapeType.CIRCLE ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
1958
+ maxHeight: FONT_SIZE_LOADING.large,
1959
+ } }), jsx(Skeleton, { width: FONT_SIZE_LOADING.large, sx: {
1960
+ borderRadius: `${BORDER_TEXT_FIELD_LOADING}px`,
1961
+ } }), jsx(Skeleton, { variant: buttonShape === ShapeType.CIRCLE ? 'circular' : 'rectangular', width: FONT_SIZE_LOADING.large, height: FONT_SIZE_LOADING.large, sx: {
1962
+ borderRadius: buttonShape === ShapeType.CIRCLE ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
1963
+ maxHeight: FONT_SIZE_LOADING.large,
1964
+ } })] }));
1965
+ };
1966
+
1967
+ 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
1968
+ sxButton = {}, // style cho 2 button
1969
+ buttonShape = ShapeType.SQUARE, buttonColor, textFieldShape = ShapeType.SQUARE, decrementIcon = jsx(RemoveIcon, {}), incrementIcon = jsx(AddIcon, {}), }) => {
1970
+ const [internalValue, setInternalValue] = useState(controlledValue ?? min ?? 0);
1971
+ const value = controlledValue !== undefined ? controlledValue : internalValue;
1972
+ const updateValue = (newValue) => {
1973
+ let finalValue = newValue;
1974
+ if (min !== undefined && newValue < min)
1975
+ finalValue = min;
1976
+ if (max !== undefined && newValue > max)
1977
+ finalValue = max;
1978
+ if (controlledValue === undefined) {
1979
+ setInternalValue(finalValue);
1980
+ }
1981
+ onChange?.(finalValue);
1982
+ };
1983
+ const handleIncrement = () => {
1984
+ updateValue(Number(value) + step);
1985
+ };
1986
+ const handleDecrement = () => {
1987
+ updateValue(Number(value) - step);
1988
+ };
1989
+ const handleInputChange = (event) => {
1990
+ if (!readOnly) {
1991
+ const newValue = event.target.value === '' ? min || 0 : Number(event.target.value);
1992
+ if (!isNaN(newValue)) {
1993
+ updateValue(newValue);
1994
+ }
1995
+ }
1996
+ };
1997
+ const isDecrementDisabled = disabled || (min !== undefined && Number(value) <= min);
1998
+ const isIncrementDisabled = disabled || (max !== undefined && Number(value) >= max);
1999
+ const buttonSize = ButtonSize.MEDIUM;
2000
+ if (loading) {
2001
+ return jsx(InputStepperSkeleton, { orientation: orientation, buttonShape: buttonShape });
2002
+ }
2003
+ return (jsxs(Box, { display: "inline-flex", flexDirection: orientation === 'horizontal' ? 'row' : 'column', alignItems: "center", gap: PADDING_GAP_ITEM, sx: {
2004
+ opacity: disabled ? 0.5 : 1,
2005
+ pointerEvents: disabled ? 'none' : 'auto',
2006
+ ...sx,
2007
+ }, children: [jsx(IconButton, { onClick: handleDecrement, disabled: isDecrementDisabled, sx: {
2008
+ borderRadius: buttonShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
2009
+ width: buttonSize,
2010
+ height: buttonSize,
2011
+ padding: PADDING_GAP_ITEM,
2012
+ border: isDecrementDisabled
2013
+ ? `1px solid ${Colors.BORDER_COLOR_DISABLE}`
2014
+ : `1px solid ${Colors.BORDER_COLOR_BUTTON}`,
2015
+ backgroundColor: Colors.BACKGROUND_COLOR,
2016
+ color: buttonColor || Colors.BORDER_COLOR_BUTTON,
2017
+ '&:hover': {
2018
+ backgroundColor: Colors.HOVER_BG_COLOR,
2019
+ },
2020
+ ...(isDecrementDisabled ? {} : sxButton),
2021
+ }, children: decrementIcon }), jsx(TextField, { value: value, onChange: handleInputChange, disabled: disabled, inputProps: {
2022
+ min,
2023
+ max,
2024
+ step,
2025
+ readOnly,
2026
+ }, type: "number", sx: {
2027
+ minWidth: buttonSize,
2028
+ width: sxTextField?.width || buttonSize,
2029
+ minHeight: buttonSize,
2030
+ height: sxTextField?.height || buttonSize,
2031
+ overflow: 'hidden',
2032
+ borderRadius: textFieldShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
2033
+ color: readOnly ? Colors.TEXT_COLOR_READONLY : '',
2034
+ backgroundColor: readOnly ? 'transparent !important' : sxTextField?.backgroundColor,
2035
+ '& .MuiOutlinedInput-root': {
2036
+ width: '100%',
2037
+ height: '100%',
2038
+ borderRadius: 'inherit',
2039
+ '& fieldset': {
2040
+ borderColor: readOnly || disabled ? 'transparent' : sxTextField?.borderColor || Colors.BORDER_COLOR_BUTTON,
2041
+ borderWidth: readOnly || disabled ? 0 : 1,
2042
+ borderRadius: textFieldShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
2043
+ },
2044
+ '&:hover fieldset': readOnly || disabled
2045
+ ? {}
2046
+ : {
2047
+ borderColor: sxTextField?.borderColorHover || Colors.BORDER_COLOR_BUTTON,
2048
+ },
2049
+ '&.Mui-focused fieldset': readOnly
2050
+ ? {
2051
+ borderColor: 'transparent',
2052
+ borderWidth: 0,
2053
+ }
2054
+ : {
2055
+ borderColor: sxTextField?.borderColorFocused || Colors.BORDER_COLOR_BUTTON,
2056
+ borderWidth: 1,
2057
+ },
2058
+ '&.Mui-disabled fieldset': {
2059
+ borderColor: Colors.BORDER_COLOR_DISABLE,
2060
+ backgroundColor: readOnly ? 'transparent' : Colors.BORDER_COLOR_DISABLE,
2061
+ },
2062
+ },
2063
+ '& input[type=number]::-webkit-outer-spin-button, & input[type=number]::-webkit-inner-spin-button': {
2064
+ WebkitAppearance: 'none',
2065
+ margin: 0,
2066
+ },
2067
+ '& input[type=number]': {
2068
+ MozAppearance: 'textfield',
2069
+ },
2070
+ '& .MuiInputBase-input': {
2071
+ textAlign: sxTextField?.textAlign || 'center',
2072
+ padding: sxTextField?.padding || '4px 8px',
2073
+ fontSize: readOnly ? '16px' : sxTextField?.fontSize || '1rem',
2074
+ cursor: readOnly ? 'not-allowed' : 'text',
2075
+ borderRadius: 'inherit',
2076
+ backgroundColor: readOnly ? 'transparent' : sxTextField?.backgroundColor || 'transparent',
2077
+ color: readOnly ? Colors.TEXT_COLOR_READONLY : sxTextField?.color || 'inherit',
2078
+ fontWeight: sxTextField?.fontWeight || (readOnly ? 700 : 'normal'),
2079
+ },
2080
+ } }), jsx(IconButton, { onClick: handleIncrement, disabled: isIncrementDisabled, sx: {
2081
+ borderRadius: buttonShape === 'circle' ? BorderRadius.CIRCLE : BorderRadius.SQUARE,
2082
+ width: buttonSize,
2083
+ height: buttonSize,
2084
+ padding: PADDING_GAP_ITEM,
2085
+ border: isIncrementDisabled
2086
+ ? `1px solid ${Colors.BORDER_COLOR_DISABLE}`
2087
+ : `1px solid ${Colors.BORDER_COLOR_BUTTON}`,
2088
+ backgroundColor: Colors.BACKGROUND_COLOR,
2089
+ color: buttonColor || Colors.BORDER_COLOR_BUTTON,
2090
+ '&:hover': {
2091
+ backgroundColor: Colors.HOVER_BG_COLOR,
2092
+ },
2093
+ ...(isIncrementDisabled ? {} : sxButton),
2094
+ }, children: incrementIcon })] }));
2095
+ };
1526
2096
 
1527
2097
  const LinkInternalElement = ({ content, onClick, sx = {} }) => {
1528
2098
  return (jsx(Typography, { onClick: onClick, sx: {
@@ -1545,7 +2115,7 @@ const LinkElement = ({ onClick, sx = {}, target = '_self', ...rest }) => {
1545
2115
  }, ...rest }));
1546
2116
  };
1547
2117
 
1548
- styled(MuiTextField)(({ theme }) => {
2118
+ const StyledTextField$3 = styled(MuiTextField)(({ theme }) => {
1549
2119
  return {
1550
2120
  '& .MuiOutlinedInput-root': {
1551
2121
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -1587,6 +2157,57 @@ styled(MuiTextField)(({ theme }) => {
1587
2157
  },
1588
2158
  };
1589
2159
  });
2160
+ 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 }) => {
2161
+ const [validationError, setValidationError] = useState(false);
2162
+ const isValidLink = (url) => {
2163
+ if (!url)
2164
+ return true; // Empty is valid (optional field)
2165
+ try {
2166
+ const urlToTest = url.includes('://') ? url : `${protocol}${url}`;
2167
+ new URL(urlToTest);
2168
+ // Kiểm tra domain phải có dấu chấm (ít nhất là có TLD)
2169
+ const hostname = new URL(urlToTest).hostname;
2170
+ if (!hostname || !hostname.includes('.')) {
2171
+ return false;
2172
+ }
2173
+ return true;
2174
+ }
2175
+ catch {
2176
+ return false;
2177
+ }
2178
+ };
2179
+ const handleBlur = (event) => {
2180
+ const inputValue = event.target.value;
2181
+ if (inputValue && !isValidLink(inputValue)) {
2182
+ setValidationError(true);
2183
+ }
2184
+ else {
2185
+ setValidationError(false);
2186
+ }
2187
+ };
2188
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
2189
+ display: 'block',
2190
+ ...TYPOGRAPHY.textFieldLabel,
2191
+ color: COLOR_GRAY[800],
2192
+ marginBottom: '4px',
2193
+ }, children: label })), 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: {
2194
+ startAdornment: (jsx(InputAdornment, { position: "start", children: jsx(Typography, { sx: { color: COLOR_NEUTRAL[300], ...TYPOGRAPHY.text14Regular }, children: protocol }) })),
2195
+ endAdornment: (success || error || validationError) && (jsx(InputAdornment, { position: "end", children: iconAfter ? (iconAfter) : (jsx(IconElement, { icon: error || validationError ? 'info' : 'check_circle', sx: { color: error || validationError ? COLOR_ERROR[500] : COLOR_SUCCESS[500] } })) })),
2196
+ }, sx: {
2197
+ '& .MuiOutlinedInput-root': {
2198
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2199
+ ...(success && {
2200
+ '&.Mui-focused': {
2201
+ boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
2202
+ },
2203
+ }),
2204
+ },
2205
+ } }), success && !error && successMessage && (jsx(Typography, { sx: {
2206
+ ...TYPOGRAPHY.textFieldHelper,
2207
+ color: COLOR_SUCCESS[500],
2208
+ marginTop: '4px',
2209
+ }, children: successMessage }))] }));
2210
+ };
1590
2211
 
1591
2212
  const MODAL_ICON_COLORS = {
1592
2213
  check_circle: '#10B981',
@@ -1696,7 +2317,15 @@ const ModalCardComponent = ({ open, isForm = false, onClose, items, nodeContent,
1696
2317
  }, children: jsxs(StackRowAlignCenter, { sx: { width: '100%' }, children: [nodeBottomLeft && jsx(Box, { sx: { width: '100%' }, children: nodeBottomLeft }), (buttonLeft || buttonCenter || buttonRight) && (jsxs(StackRow, { sx: { width: '100%', gap: PADDING_GAP_LAYOUT }, children: [buttonLeft && (jsx(Box, { sx: { flex: 1 }, children: jsx(ButtonComponent, { ...buttonLeft, fullWidth: true }) })), buttonCenter && (jsx(Box, { sx: { flex: 1 }, children: jsx(ButtonComponent, { ...buttonCenter, fullWidth: true }) })), buttonRight && (jsx(Box, { sx: { flex: 1 }, children: jsx(ButtonComponent, { ...buttonRight, fullWidth: true }) }))] }))] }) }))] }) }));
1697
2318
  };
1698
2319
 
1699
- styled(MuiTextField)(({ theme }) => {
2320
+ const CURRENCIES = ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'CNY', 'VND', 'INR'];
2321
+ // Format number with thousand separators
2322
+ const formatMoneyDisplay = (value) => {
2323
+ if (!value)
2324
+ return '';
2325
+ const numericOnly = value.replace(/\D/g, '');
2326
+ return numericOnly.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
2327
+ };
2328
+ const StyledTextField$2 = styled(MuiTextField)(({ theme }) => {
1700
2329
  return {
1701
2330
  '& .MuiOutlinedInput-root': {
1702
2331
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -1738,8 +2367,64 @@ styled(MuiTextField)(({ theme }) => {
1738
2367
  },
1739
2368
  };
1740
2369
  });
2370
+ 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 }) => {
2371
+ const [selectedCurrency, setSelectedCurrency] = useState(currency);
2372
+ const handleCurrencyChange = (e) => {
2373
+ const newCurrency = e.target.value;
2374
+ setSelectedCurrency(newCurrency);
2375
+ onCurrencyChange?.(newCurrency);
2376
+ };
2377
+ const handleMoneyChange = (e) => {
2378
+ const rawValue = e.target.value.replace(/,/g, '');
2379
+ onChange?.({ ...e, target: { ...e.target, value: rawValue } });
2380
+ };
2381
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
2382
+ display: 'block',
2383
+ ...TYPOGRAPHY.textFieldLabel,
2384
+ color: COLOR_GRAY[800],
2385
+ marginBottom: '4px',
2386
+ }, children: label })), jsx(StyledTextField$2, { placeholder: placeholder, value: formatMoneyDisplay(value), disabled: disabled, error: error, helperText: error ? errorMessage : helperText, size: "small", fullWidth: true, onChange: handleMoneyChange, InputProps: {
2387
+ startAdornment: (jsx(InputAdornment, { position: "start", children: iconBefore ? iconBefore : jsx(AttachMoneyIcon, { sx: { fontSize: '18px', color: COLOR_GRAY[400], mr: 0.5 } }) })),
2388
+ endAdornment: (jsx(InputAdornment, { position: "end", children: jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [(success || error) && (jsx(IconElement, { icon: error ? 'info' : 'check_circle', sx: { color: error ? COLOR_ERROR[500] : COLOR_SUCCESS[500] } })), jsx(Select, { value: selectedCurrency, onChange: handleCurrencyChange, disabled: disabled, variant: "standard", sx: {
2389
+ border: 'none',
2390
+ outline: 'none',
2391
+ '& .MuiSelect-standard': { border: 'none' },
2392
+ '&.MuiInput-underline:before': { borderBottom: 'none' },
2393
+ '&.MuiInput-underline:hover:before': { borderBottom: 'none' },
2394
+ '&.MuiInput-underline:after': { borderBottom: 'none' },
2395
+ minWidth: '70px',
2396
+ fontSize: '14px',
2397
+ color: COLOR_GRAY[500],
2398
+ }, children: optionCurrencies.map((curr) => (jsx(MenuItem, { value: curr, children: curr }, curr))) })] }) })),
2399
+ }, sx: {
2400
+ '& .MuiOutlinedInput-root': {
2401
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2402
+ ...(success && {
2403
+ '&.Mui-focused': {
2404
+ boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
2405
+ },
2406
+ }),
2407
+ },
2408
+ } }), success && !error && successMessage && (jsx(Typography, { sx: {
2409
+ ...TYPOGRAPHY.textFieldHelper,
2410
+ color: COLOR_SUCCESS[600],
2411
+ marginTop: '4px',
2412
+ }, children: successMessage }))] }));
2413
+ };
1741
2414
 
1742
- styled(MuiTextField)(({ theme }) => {
2415
+ const COUNTRY_CODES = [
2416
+ { code: 'US', name: 'United States', flag: '🇺🇸', value: '+1' },
2417
+ { code: 'GB', name: 'United Kingdom', flag: '🇬🇧', value: '+44' },
2418
+ { code: 'CA', name: 'Canada', flag: '🇨🇦', value: '+1' },
2419
+ { code: 'AU', name: 'Australia', flag: '🇦🇺', value: '+61' },
2420
+ { code: 'VN', name: 'Vietnam', flag: '🇻🇳', value: '+84' },
2421
+ { code: 'JP', name: 'Japan', flag: '🇯🇵', value: '+81' },
2422
+ { code: 'CN', name: 'China', flag: '🇨🇳', value: '+86' },
2423
+ { code: 'IN', name: 'India', flag: '🇮🇳', value: '+91' },
2424
+ { code: 'DE', name: 'Germany', flag: '🇩🇪', value: '+49' },
2425
+ { code: 'FR', name: 'France', flag: '🇫🇷', value: '+33' },
2426
+ ];
2427
+ const StyledTextField$1 = styled(MuiTextField)(({ theme }) => {
1743
2428
  return {
1744
2429
  '& .MuiOutlinedInput-root': {
1745
2430
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -1781,8 +2466,250 @@ styled(MuiTextField)(({ theme }) => {
1781
2466
  },
1782
2467
  };
1783
2468
  });
2469
+ 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 }) => {
2470
+ const handleCountryChange = (e) => {
2471
+ const newCode = e.target.value;
2472
+ onCountryCodeChange?.(newCode);
2473
+ };
2474
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
2475
+ display: 'block',
2476
+ ...TYPOGRAPHY.textFieldLabel,
2477
+ color: COLOR_GRAY[800],
2478
+ marginBottom: '4px',
2479
+ }, children: label })), jsx(StyledTextField$1, { placeholder: placeholder, value: value, disabled: disabled, error: error, helperText: error ? errorMessage : helperText, size: "small", fullWidth: true, onChange: onChange, InputProps: {
2480
+ startAdornment: (jsx(InputAdornment, { position: "start", sx: { mr: 0 }, children: jsx(Select, { value: countryCode, onChange: handleCountryChange, disabled: disabled, variant: "standard", sx: {
2481
+ border: 'none',
2482
+ outline: 'none',
2483
+ '& .MuiSelect-standard': { border: 'none' },
2484
+ '&.MuiInput-underline:before': { borderBottom: 'none' },
2485
+ '&.MuiInput-underline:hover:before': { borderBottom: 'none' },
2486
+ '&.MuiInput-underline:after': { borderBottom: 'none' },
2487
+ minWidth: '50px',
2488
+ fontSize: '14px',
2489
+ color: COLOR_GRAY[500],
2490
+ display: 'flex',
2491
+ alignItems: 'center',
2492
+ gap: 0.5,
2493
+ }, children: countries.map((c) => (jsx(MenuItem, { value: c.code, children: c.code }, c.code))) }) })),
2494
+ endAdornment: (success || error) && (jsx(InputAdornment, { position: "end", children: jsx(IconElement, { icon: error ? 'info' : 'check_circle', sx: { color: error ? COLOR_ERROR[500] : COLOR_SUCCESS[500] } }) })),
2495
+ }, sx: {
2496
+ '& .MuiOutlinedInput-root': {
2497
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2498
+ ...(success && {
2499
+ '&.Mui-focused': {
2500
+ boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
2501
+ },
2502
+ }),
2503
+ },
2504
+ } }), success && !error && successMessage && (jsx(Typography, { sx: {
2505
+ ...TYPOGRAPHY.textFieldHelper,
2506
+ color: COLOR_SUCCESS[600],
2507
+ marginTop: '4px',
2508
+ }, children: successMessage }))] }));
2509
+ };
2510
+
2511
+ const PIN_SIZES = {
2512
+ sm: { width: 40, height: 40, fontSize: 18 },
2513
+ md: { width: 48, height: 48, fontSize: 20 },
2514
+ lg: { width: 56, height: 56, fontSize: 24 },
2515
+ };
2516
+ const PIN_SPACING = {
2517
+ sm: 8,
2518
+ md: 12,
2519
+ lg: 16,
2520
+ };
1784
2521
 
1785
- styled$1(Autocomplete)(({ theme }) => ({
2522
+ 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, }) => {
2523
+ const { palette } = useTheme();
2524
+ const inputRefs = useRef([]);
2525
+ useEffect(() => {
2526
+ if (autoFocus && inputRefs.current[0]) {
2527
+ inputRefs.current[0].focus();
2528
+ }
2529
+ }, [autoFocus]);
2530
+ const handleChange = (index, val) => {
2531
+ if (!/^\d*$/.test(val))
2532
+ return;
2533
+ const newValue = value.split('');
2534
+ newValue[index] = val;
2535
+ const updatedValue = newValue.join('').slice(0, length);
2536
+ onChange(updatedValue);
2537
+ if (val && index < length - 1) {
2538
+ inputRefs.current[index + 1]?.focus();
2539
+ }
2540
+ if (updatedValue.length === length) {
2541
+ onComplete?.(updatedValue);
2542
+ }
2543
+ };
2544
+ const handleKeyDown = (index, e) => {
2545
+ if (e.key === 'Backspace' && !value[index] && index > 0) {
2546
+ inputRefs.current[index - 1]?.focus();
2547
+ }
2548
+ };
2549
+ const renderPINDisplay = (index) => {
2550
+ const inputValue = value[index] || '';
2551
+ const isFilled = !!inputValue;
2552
+ const sizeStyle = PIN_SIZES[size];
2553
+ const borderColor = error ? palette.error.main : COLOR_GRAY[200];
2554
+ const borderFocusColorValue = borderFocusColor || (error ? palette.error.main : COLOR_GRAY[900]);
2555
+ if (type === 'bullet') {
2556
+ return (jsxs(Box, { sx: { position: 'relative', width: sizeStyle.width, height: sizeStyle.height }, children: [jsx(TextField, { ref: (el) => {
2557
+ const input = el?.querySelector('input');
2558
+ if (input) {
2559
+ inputRefs.current[index] = input;
2560
+ }
2561
+ }, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: disabled, error: error, sx: {
2562
+ width: '100%',
2563
+ height: '100%',
2564
+ '& .MuiOutlinedInput-root': {
2565
+ height: '100%',
2566
+ padding: 0,
2567
+ '& input': {
2568
+ textAlign: 'center',
2569
+ fontSize: sizeStyle.fontSize,
2570
+ fontWeight: 600,
2571
+ padding: 0,
2572
+ color: masked && isFilled ? 'transparent' : 'inherit',
2573
+ WebkitTextFillColor: masked && isFilled ? 'transparent' : 'unset',
2574
+ caretColor: palette.primary.main,
2575
+ '&::placeholder': {
2576
+ color: palette.action.disabled,
2577
+ opacity: 1,
2578
+ },
2579
+ },
2580
+ '& fieldset': {
2581
+ borderColor: borderColor,
2582
+ borderRadius: '8px',
2583
+ },
2584
+ '&:hover fieldset': {
2585
+ borderColor: error ? palette.error.main : borderFocusColorValue,
2586
+ },
2587
+ '&.Mui-focused fieldset': {
2588
+ borderColor: borderFocusColorValue,
2589
+ borderWidth: 1,
2590
+ },
2591
+ },
2592
+ '& input': {
2593
+ maxLength: 1,
2594
+ },
2595
+ }, placeholder: "-" }), masked && isFilled && (jsx(Box, { sx: {
2596
+ position: 'absolute',
2597
+ top: '50%',
2598
+ left: '50%',
2599
+ transform: 'translate(-50%, -50%)',
2600
+ fontSize: sizeStyle.fontSize,
2601
+ fontWeight: 600,
2602
+ color: error ? palette.error.main : palette.text.primary,
2603
+ pointerEvents: 'none',
2604
+ }, children: "\u25CF" }))] }));
2605
+ }
2606
+ if (type === 'circle') {
2607
+ return (jsxs(Box, { sx: { position: 'relative', width: sizeStyle.width, height: sizeStyle.height }, children: [jsx(TextField, { ref: (el) => {
2608
+ const input = el?.querySelector('input');
2609
+ if (input) {
2610
+ inputRefs.current[index] = input;
2611
+ }
2612
+ }, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: disabled, error: error, sx: {
2613
+ width: '100%',
2614
+ height: '100%',
2615
+ '& .MuiOutlinedInput-root': {
2616
+ height: '100%',
2617
+ padding: 0,
2618
+ borderRadius: '50%',
2619
+ '& input': {
2620
+ textAlign: 'center',
2621
+ fontSize: sizeStyle.fontSize,
2622
+ fontWeight: 600,
2623
+ padding: 0,
2624
+ color: masked && isFilled ? 'transparent' : 'inherit',
2625
+ WebkitTextFillColor: masked && isFilled ? 'transparent' : 'unset',
2626
+ caretColor: palette.primary.main,
2627
+ '&::placeholder': {
2628
+ color: palette.action.disabled,
2629
+ opacity: 1,
2630
+ },
2631
+ },
2632
+ '& fieldset': {
2633
+ borderColor: borderColor,
2634
+ borderRadius: '50%',
2635
+ },
2636
+ '&:hover fieldset': {
2637
+ borderColor: error ? palette.error.main : borderFocusColorValue,
2638
+ },
2639
+ '&.Mui-focused fieldset': {
2640
+ borderColor: borderFocusColorValue,
2641
+ borderWidth: 1,
2642
+ },
2643
+ },
2644
+ '& input': {
2645
+ maxLength: 1,
2646
+ },
2647
+ }, placeholder: "-" }), masked && isFilled && (jsx(Box, { sx: {
2648
+ position: 'absolute',
2649
+ top: '50%',
2650
+ left: '50%',
2651
+ transform: 'translate(-50%, -50%)',
2652
+ fontSize: sizeStyle.fontSize,
2653
+ fontWeight: 600,
2654
+ color: error ? palette.error.main : palette.text.primary,
2655
+ pointerEvents: 'none',
2656
+ }, children: "\u25CF" }))] }));
2657
+ }
2658
+ // Default text type
2659
+ return (jsx(TextField, { ref: (el) => {
2660
+ const input = el?.querySelector('input');
2661
+ if (input) {
2662
+ inputRefs.current[index] = input;
2663
+ }
2664
+ }, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: disabled, error: error, sx: {
2665
+ width: sizeStyle.width,
2666
+ '& .MuiOutlinedInput-root': {
2667
+ height: sizeStyle.height,
2668
+ padding: 0,
2669
+ '& input': {
2670
+ textAlign: 'center',
2671
+ fontSize: sizeStyle.fontSize,
2672
+ fontWeight: 600,
2673
+ padding: 0,
2674
+ '&::placeholder': {
2675
+ color: palette.action.disabled,
2676
+ opacity: 1,
2677
+ },
2678
+ },
2679
+ '& fieldset': {
2680
+ borderColor: borderColor,
2681
+ },
2682
+ '&:hover fieldset': {
2683
+ borderColor: error ? palette.error.main : borderFocusColorValue,
2684
+ },
2685
+ '&.Mui-focused fieldset': {
2686
+ borderColor: borderFocusColorValue,
2687
+ borderWidth: 1,
2688
+ },
2689
+ },
2690
+ '& input': {
2691
+ maxLength: 1,
2692
+ },
2693
+ }, placeholder: "-" }));
2694
+ };
2695
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Box, { sx: {
2696
+ mb: 1,
2697
+ fontSize: 14,
2698
+ fontWeight: 500,
2699
+ color: palette.text.primary,
2700
+ }, children: label })), jsx(Box, { sx: {
2701
+ display: 'flex',
2702
+ gap: `${PIN_SPACING[spacing]}px`,
2703
+ justifyContent: align === 'center' ? 'center' : align === 'right' ? 'flex-end' : 'flex-start',
2704
+ }, children: Array.from({ length }).map((_, index) => (jsx(Box, { children: renderPINDisplay(index) }, index))) }), error && errorMessage && (jsx(Box, { sx: {
2705
+ mt: 1,
2706
+ fontSize: 12,
2707
+ color: palette.error.main,
2708
+ textAlign: align,
2709
+ }, children: errorMessage }))] }));
2710
+ };
2711
+
2712
+ const StyledAutocomplete$1 = styled$1(Autocomplete)(({ theme }) => ({
1786
2713
  '& .MuiOutlinedInput-root': {
1787
2714
  padding: '8px !important',
1788
2715
  display: 'flex',
@@ -1843,8 +2770,138 @@ styled$1(Autocomplete)(({ theme }) => ({
1843
2770
  },
1844
2771
  },
1845
2772
  }));
2773
+ 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, }) => {
2774
+ const DEBOUNCE_DELAY = 500;
2775
+ const [inputValue, setInputValue] = useState('');
2776
+ const [filteredOptions, setFilteredOptions] = useState(options);
2777
+ const [isLoading, setIsLoading] = useState(false);
2778
+ const debounceTimerRef = useRef(null);
2779
+ const onSearchRef = useRef(onSearch);
2780
+ const onInputChangeRef = useRef(onInputChange);
2781
+ const optionsRef = useRef(options);
2782
+ // Update refs when props change
2783
+ useEffect(() => {
2784
+ onSearchRef.current = onSearch;
2785
+ onInputChangeRef.current = onInputChange;
2786
+ optionsRef.current = options;
2787
+ }, [onSearch, onInputChange, options]);
2788
+ // Normalize value to array for internal state
2789
+ const selectedValues = useMemo(() => {
2790
+ if (!value)
2791
+ return [];
2792
+ const values = Array.isArray(value) ? value : [value];
2793
+ return values.map((v) => (typeof v === 'object' ? v : { label: String(v), value: v }));
2794
+ }, [value]);
2795
+ // Search logic with debounce
2796
+ useEffect(() => {
2797
+ if (debounceTimerRef.current) {
2798
+ clearTimeout(debounceTimerRef.current);
2799
+ }
2800
+ // Reset to original options when input is empty
2801
+ if (!inputValue.trim()) {
2802
+ setFilteredOptions(optionsRef.current);
2803
+ return;
2804
+ }
2805
+ debounceTimerRef.current = setTimeout(async () => {
2806
+ const searchFn = onSearchRef.current;
2807
+ if (searchFn) {
2808
+ // Async search
2809
+ setIsLoading(true);
2810
+ try {
2811
+ const results = await searchFn(inputValue);
2812
+ setFilteredOptions(results);
2813
+ }
2814
+ catch (error) {
2815
+ console.error('Search error:', error);
2816
+ setFilteredOptions([]);
2817
+ }
2818
+ finally {
2819
+ setIsLoading(false);
2820
+ }
2821
+ }
2822
+ else {
2823
+ // Local filtering
2824
+ const filtered = optionsRef.current.filter((opt) => opt.label.toLowerCase().includes(inputValue.toLowerCase()));
2825
+ setFilteredOptions(filtered);
2826
+ }
2827
+ onInputChangeRef.current?.(inputValue);
2828
+ }, DEBOUNCE_DELAY);
2829
+ return () => {
2830
+ if (debounceTimerRef.current) {
2831
+ clearTimeout(debounceTimerRef.current);
2832
+ }
2833
+ };
2834
+ }, [inputValue]);
2835
+ const handleInputChange = (event, newInputValue) => {
2836
+ setInputValue(newInputValue);
2837
+ };
2838
+ const handleChange = (event, newValue) => {
2839
+ if (multiple) {
2840
+ const result = Array.isArray(newValue) ? newValue : newValue ? [newValue] : [];
2841
+ onChange?.(result.length > 0 ? result : null);
2842
+ }
2843
+ else {
2844
+ onChange?.(newValue || null);
2845
+ }
2846
+ };
2847
+ const handleClear = () => {
2848
+ setInputValue('');
2849
+ setFilteredOptions(optionsRef.current);
2850
+ onClear?.();
2851
+ };
2852
+ return (jsxs(Box, { sx: { width: '100%', ...sx }, children: [label && (jsx(Typography, { variant: "subtitle2", sx: {
2853
+ fontWeight: 500,
2854
+ color: COLOR_GRAY[900],
2855
+ marginBottom: '6px',
2856
+ display: 'block',
2857
+ }, children: label })), jsx(StyledAutocomplete$1, { multiple: multiple, freeSolo: true, options: filteredOptions, getOptionLabel: (option) => {
2858
+ if (!option)
2859
+ return '';
2860
+ if (typeof option === 'object' && 'label' in option)
2861
+ return option.label;
2862
+ return String(option);
2863
+ }, isOptionEqualToValue: (option, val) => {
2864
+ if (!option || !val)
2865
+ return false;
2866
+ if (typeof option === 'object' && typeof val === 'object' && 'value' in option && 'value' in val) {
2867
+ return option.value === val.value;
2868
+ }
2869
+ return false;
2870
+ }, 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: {
2871
+ '& .MuiOutlinedInput-root': {
2872
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2873
+ borderColor: error ? COLOR_ERROR[500] : success ? COLOR_SUCCESS[500] : undefined,
2874
+ },
2875
+ }, renderInput: (params) => (jsx(TextField, { ...params, placeholder: placeholder, variant: "outlined", size: "small", error: error, InputProps: {
2876
+ ...params.InputProps,
2877
+ startAdornment: (jsxs(Fragment, { children: [jsx(InputAdornment, { position: "start", sx: { marginLeft: '4px', marginRight: '0px' }, children: jsx(SearchIcon, { sx: { color: '#999', fontSize: '18px' } }) }), params.InputProps?.startAdornment] })),
2878
+ } })), renderTags: (value, getTagProps) => value.map((option, index) => {
2879
+ const label = typeof option === 'object' && option && 'label' in option ? option.label : String(option);
2880
+ return jsx(Chip, { ...getTagProps({ index }), label: label, size: "small" });
2881
+ }), renderOption: (props, option) => {
2882
+ const { key, ...otherProps } = props;
2883
+ const label = option?.label || '';
2884
+ return (jsx(Box, { ...otherProps, component: "li", children: label }, key));
2885
+ }, componentsProps: {
2886
+ clearIndicator: {
2887
+ onClick: handleClear,
2888
+ },
2889
+ } }), error && errorMessage && (jsx(Box, { sx: {
2890
+ fontSize: '12px',
2891
+ color: COLOR_ERROR[500],
2892
+ marginTop: '4px',
2893
+ }, children: errorMessage })), success && successMessage && (jsx(Box, { sx: {
2894
+ fontSize: '12px',
2895
+ color: COLOR_SUCCESS[500],
2896
+ marginTop: '4px',
2897
+ }, children: successMessage })), helperText && !error && !success && (jsx(Box, { sx: {
2898
+ fontSize: '12px',
2899
+ color: COLOR_NEUTRAL[500],
2900
+ marginTop: '4px',
2901
+ }, children: helperText }))] }));
2902
+ };
1846
2903
 
1847
- styled$1(Autocomplete)(({ theme }) => ({
2904
+ const StyledAutocomplete = styled$1(Autocomplete)(({ theme }) => ({
1848
2905
  '& .MuiOutlinedInput-root': {
1849
2906
  padding: '0 !important',
1850
2907
  display: 'flex',
@@ -1883,6 +2940,45 @@ styled$1(Autocomplete)(({ theme }) => ({
1883
2940
  paddingRight: '8px',
1884
2941
  },
1885
2942
  }));
2943
+ const SearchFieldComponent = ({ value, onChange, onClear, onInputChange, borderRadius = 6, disabled = false, placeholder = 'Placeholder', sx, }) => {
2944
+ const DEBOUNCE_DELAY = 1000;
2945
+ const [inputValue, setInputValue] = useState('');
2946
+ const debounceTimer = useRef(null);
2947
+ useEffect(() => {
2948
+ if (debounceTimer.current) {
2949
+ clearTimeout(debounceTimer.current);
2950
+ }
2951
+ debounceTimer.current = setTimeout(() => {
2952
+ onInputChange?.(new Event('debounce'), inputValue, 'debounce');
2953
+ }, DEBOUNCE_DELAY);
2954
+ return () => {
2955
+ if (debounceTimer.current) {
2956
+ clearTimeout(debounceTimer.current);
2957
+ }
2958
+ };
2959
+ }, [inputValue, onInputChange]);
2960
+ const handleInputChange = useCallback((event, value) => {
2961
+ setInputValue(value);
2962
+ }, []);
2963
+ const handleClear = useCallback(() => {
2964
+ setInputValue('');
2965
+ onClear?.();
2966
+ onInputChange?.(new Event('clear'), '', 'clear');
2967
+ }, [onClear, onInputChange]);
2968
+ return (jsx(StyledAutocomplete, { freeSolo: true, options: [], inputValue: inputValue, onInputChange: handleInputChange, disabled: disabled, onChange: onChange, noOptionsText: null, sx: {
2969
+ '& .MuiOutlinedInput-root': {
2970
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
2971
+ },
2972
+ ...sx,
2973
+ }, renderInput: (params) => (jsx(TextField, { ...params, placeholder: placeholder, variant: "outlined", size: "small", InputProps: {
2974
+ ...params.InputProps,
2975
+ startAdornment: (jsx(InputAdornment, { position: "start", sx: { marginLeft: '8px' }, children: jsx(SearchIcon, { sx: { color: '#999', fontSize: '20px' } }) })),
2976
+ } })), componentsProps: {
2977
+ clearIndicator: {
2978
+ onClick: handleClear,
2979
+ },
2980
+ } }));
2981
+ };
1886
2982
 
1887
2983
  const SWITCH_SIZE = {
1888
2984
  small: { width: '36px', height: '20px', thumbSize: '16px' },
@@ -1902,7 +2998,7 @@ const SWITCH_COLORS = {
1902
2998
  thumbFocusColor: '#F4EBFF',
1903
2999
  },
1904
3000
  };
1905
- styled$1(Switch, {
3001
+ const StyledSwitch = styled$1(Switch, {
1906
3002
  shouldForwardProp: (prop) => prop !== 'size',
1907
3003
  })(({ theme, size = 'medium' }) => {
1908
3004
  const mode = theme.palette.mode;
@@ -1955,7 +3051,61 @@ styled$1(Switch, {
1955
3051
  },
1956
3052
  };
1957
3053
  });
3054
+ const SwitchComponent = ({ title, sx, ...switchProps }) => {
3055
+ if (!title) {
3056
+ return jsx(StyledSwitch, { disableRipple: true, ...switchProps });
3057
+ }
3058
+ return (jsxs(StackRowAlignCenterJustEnd, { sx: {
3059
+ display: 'inline-flex',
3060
+ alignItems: 'center',
3061
+ gap: '12px',
3062
+ ...sx,
3063
+ }, children: [jsx(StyledSwitch, { disableRipple: true, ...switchProps }), title] }));
3064
+ };
1958
3065
 
3066
+ const TAB_STYLES = {
3067
+ position: 'relative',
3068
+ padding: '18px 16px',
3069
+ cursor: 'pointer',
3070
+ minHeight: 44,
3071
+ '&:hover': {
3072
+ bgcolor: '#F3F4F6',
3073
+ },
3074
+ };
3075
+ const TAB_UNDERLINE_STYLES = {
3076
+ width: '1px',
3077
+ alignSelf: 'stretch',
3078
+ bgcolor: '#E5E7EB',
3079
+ };
3080
+ const TABS_CONTAINER_HORIZONTAL = {
3081
+ display: 'flex',
3082
+ alignItems: 'center',
3083
+ borderBottom: '2px solid #E5E7EB',
3084
+ };
3085
+ const TAB_ACTIVE_BACKGROUND_HORIZONTAL = {
3086
+ position: 'absolute',
3087
+ bottom: -2,
3088
+ left: 0,
3089
+ right: 0,
3090
+ height: '2px',
3091
+ bgcolor: '#0F766E',
3092
+ };
3093
+ const TABS_CONTAINER_VERTICAL = {
3094
+ position: 'relative',
3095
+ padding: '10px',
3096
+ cursor: 'pointer',
3097
+ minHeight: 40,
3098
+ '&:hover': {
3099
+ bgcolor: '#F3F4F6',
3100
+ },
3101
+ width: '100%',
3102
+ };
3103
+ const TAB_ACTIVE_BACKGROUND_VERTICAL = {
3104
+ position: 'absolute',
3105
+ inset: 0,
3106
+ bgcolor: '#E6EEED',
3107
+ borderRadius: 1,
3108
+ };
1959
3109
  var TabColors;
1960
3110
  (function (TabColors) {
1961
3111
  TabColors["ACTIVE_TEXT"] = "#0F766E";
@@ -1964,7 +3114,171 @@ var TabColors;
1964
3114
  TabColors["MENU_ACTIVE_BACKGROUND"] = "#E0F2FE";
1965
3115
  })(TabColors || (TabColors = {}));
1966
3116
 
1967
- styled(MuiTextField)(({ theme }) => {
3117
+ const TabsComponent = ({ idSelect, tabs, size, direction = 'row', maxDisplay, onChange, sx, sxTabs, sxWrapper, }) => {
3118
+ // state
3119
+ const [selected, setSelected] = useState(idSelect);
3120
+ const [anchorEl, setAnchorEl] = useState(null);
3121
+ const layoutGroupId = useId();
3122
+ useEffect(() => {
3123
+ setSelected(idSelect);
3124
+ }, [idSelect]);
3125
+ const isVertical = direction === 'column';
3126
+ const showOverflow = !isVertical && maxDisplay && tabs.length > maxDisplay;
3127
+ const visibleTabs = showOverflow ? tabs.slice(0, maxDisplay) : tabs;
3128
+ const overflowTabs = showOverflow ? tabs.slice(maxDisplay) : [];
3129
+ // function
3130
+ const handleOpenDropdown = (event) => {
3131
+ setAnchorEl(event.currentTarget);
3132
+ };
3133
+ const handleTabClick = (tab) => {
3134
+ setSelected(tab.id);
3135
+ onChange?.(tab.id);
3136
+ if (tab.onClick) {
3137
+ tab.onClick();
3138
+ }
3139
+ };
3140
+ const handleOverflowItemClick = (tab) => {
3141
+ handleTabClick(tab);
3142
+ setAnchorEl(null);
3143
+ };
3144
+ return (jsx(React.Fragment, { children: isVertical ? (jsx(LayoutGroup, { id: layoutGroupId, children: jsx(Stack, { direction: "column", sx: { width: 'fit-content', gap: PADDING_GAP_ITEM_SMALL, ...sxWrapper }, children: tabs.map((tab) => {
3145
+ const isActive = tab.id === selected;
3146
+ return (jsx(LinkElement, { href: tab.href, onClick: tab.onClick, id: tab.id, children: jsxs(Box, { sx: { position: 'relative' }, children: [jsx(Stack, { component: motion.div, sx: {
3147
+ ...TABS_CONTAINER_VERTICAL,
3148
+ color: isActive ? TabColors.ACTIVE_TEXT : TabColors.INACTIVE_TEXT,
3149
+ }, onTap: () => handleTabClick(tab), children: jsxs(Box, { sx: {
3150
+ ...TYPOGRAPHY_STYLES.textMd.medium,
3151
+ display: 'flex',
3152
+ alignItems: 'center',
3153
+ gap: tab.icon ? 0.5 : 0,
3154
+ position: 'relative',
3155
+ zIndex: 1,
3156
+ ...sx,
3157
+ }, children: [tab.icon && jsx(IconElement, { size: size, icon: tab.icon }), tab.name] }) }), isActive && (jsx(Box, { component: motion.div, sx: { ...TAB_ACTIVE_BACKGROUND_VERTICAL }, layoutId: `${layoutGroupId}-background` }))] }) }, tab.id));
3158
+ }) }) })) : (jsx(LayoutGroup, { id: layoutGroupId, children: jsxs(Box, { sx: { position: 'relative', display: 'flex', alignItems: 'center' }, children: [jsxs(Box, { sx: { ...TABS_CONTAINER_HORIZONTAL }, children: [visibleTabs.map((tab) => {
3159
+ const isActive = tab.id === selected;
3160
+ return (jsx(LinkElement, { href: tab.href, onClick: tab.onClick, id: tab.id, children: jsxs(Stack, { component: motion.div, sx: {
3161
+ color: isActive ? TabColors.ACTIVE_TEXT : TabColors.INACTIVE_TEXT,
3162
+ position: 'relative',
3163
+ padding: '18px 16px',
3164
+ cursor: 'pointer',
3165
+ minHeight: 40,
3166
+ '&:hover': {
3167
+ bgcolor: TabColors.HOVER_BACKGROUND,
3168
+ },
3169
+ ...sxTabs,
3170
+ }, onTap: () => handleTabClick(tab), children: [jsxs(Box, { sx: {
3171
+ ...TYPOGRAPHY_STYLES.textMd.medium,
3172
+ display: 'flex',
3173
+ alignItems: 'center',
3174
+ gap: tab.icon ? 0.5 : 0,
3175
+ ...sx,
3176
+ }, children: [tab.icon && jsx(IconElement, { size: size, icon: tab.icon }), tab.name] }), isActive && (jsx(Box, { component: motion.div, sx: { ...TAB_ACTIVE_BACKGROUND_HORIZONTAL }, layoutId: `${layoutGroupId}-underline` }))] }) }, tab.id));
3177
+ }), showOverflow && (jsxs(React.Fragment, { children: [jsx(Box, { sx: { ...TAB_UNDERLINE_STYLES } }), jsx(Stack, { sx: {
3178
+ ...TAB_STYLES,
3179
+ }, onClick: handleOpenDropdown, children: jsx(IconElement, { icon: "more_horiz", size: size }) })] }))] }), jsx(Menu, { disableScrollLock: true, anchorEl: anchorEl, open: Boolean(anchorEl), onClose: () => setAnchorEl(null), anchorOrigin: {
3180
+ vertical: 'bottom',
3181
+ horizontal: 'left',
3182
+ }, transformOrigin: {
3183
+ vertical: 'top',
3184
+ horizontal: 'left',
3185
+ }, children: overflowTabs.map((tab) => {
3186
+ const isActive = tab.id === selected;
3187
+ return (jsx(MenuItem, { onClick: () => handleOverflowItemClick(tab), sx: {
3188
+ color: isActive ? TabColors.ACTIVE_TEXT : TabColors.INACTIVE_TEXT,
3189
+ bgcolor: isActive ? TabColors.MENU_ACTIVE_BACKGROUND : 'transparent',
3190
+ '&:hover': {
3191
+ bgcolor: isActive ? TabColors.MENU_ACTIVE_BACKGROUND : TabColors.HOVER_BACKGROUND,
3192
+ },
3193
+ }, children: jsxs(Box, { sx: {
3194
+ display: 'flex',
3195
+ alignItems: 'center',
3196
+ gap: tab.icon ? 0.5 : 0,
3197
+ }, children: [tab.icon && jsx(IconElement, { size: size, icon: tab.icon }), tab.name] }) }, tab.id));
3198
+ }) })] }) })) }));
3199
+ };
3200
+
3201
+ const TextAreaComponent = ({ label = '', placeholder = '', value = '', disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, onChange, onBlur, helperText = '', rows = 4, maxLength, sx, }) => {
3202
+ const borderRadiusValue = borderRadius === 'max' ? '100px' : `${borderRadius}px`;
3203
+ const getHelperText = () => {
3204
+ if (error && errorMessage)
3205
+ return errorMessage;
3206
+ if (success && successMessage)
3207
+ return successMessage;
3208
+ if (helperText)
3209
+ return helperText;
3210
+ return '';
3211
+ };
3212
+ const getHelperTextColor = () => {
3213
+ if (error)
3214
+ return COLOR_ERROR[500];
3215
+ if (success)
3216
+ return COLOR_SUCCESS[500];
3217
+ return COLOR_NEUTRAL[400];
3218
+ };
3219
+ const getBorderColor = () => {
3220
+ if (error)
3221
+ return COLOR_ERROR[500];
3222
+ if (success)
3223
+ return COLOR_SUCCESS[500];
3224
+ return COLOR_NEUTRAL[300];
3225
+ };
3226
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
3227
+ display: 'block',
3228
+ ...TYPOGRAPHY.textFieldLabel,
3229
+ color: COLOR_GRAY[800],
3230
+ marginBottom: '4px',
3231
+ }, children: label })), jsxs(Box, { sx: { position: 'relative' }, children: [jsx(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: {
3232
+ maxLength: maxLength,
3233
+ style: {
3234
+ ...TYPOGRAPHY.text14Regular,
3235
+ paddingBottom: maxLength ? '24px' : '0px',
3236
+ },
3237
+ }, sx: {
3238
+ '& .MuiOutlinedInput-root': {
3239
+ borderRadius: borderRadiusValue,
3240
+ backgroundColor: disabled ? COLOR_NEUTRAL[100] : 'white',
3241
+ transition: 'all 0.2s ease',
3242
+ '& fieldset': {
3243
+ borderColor: getBorderColor(),
3244
+ },
3245
+ '&:hover fieldset': {
3246
+ borderColor: disabled ? getBorderColor() : error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
3247
+ },
3248
+ '&.Mui-focused fieldset': {
3249
+ borderColor: error ? COLOR_ERROR[500] : COLOR_NEUTRAL[400],
3250
+ borderWidth: '1.5px',
3251
+ },
3252
+ },
3253
+ '& .MuiOutlinedInput-input': {
3254
+ color: value ? COLOR_GRAY[900] : COLOR_NEUTRAL[400],
3255
+ '&::placeholder': {
3256
+ color: COLOR_NEUTRAL[400],
3257
+ opacity: 1,
3258
+ },
3259
+ '&:disabled': {
3260
+ color: COLOR_NEUTRAL[400],
3261
+ WebkitTextFillColor: COLOR_NEUTRAL[400],
3262
+ },
3263
+ },
3264
+ '& .MuiOutlinedInput-notchedOutline': {
3265
+ borderColor: getBorderColor(),
3266
+ },
3267
+ } }), maxLength && (jsxs(Box, { sx: {
3268
+ position: 'absolute',
3269
+ bottom: '8px',
3270
+ right: '12px',
3271
+ fontSize: '12px',
3272
+ color: COLOR_NEUTRAL[400],
3273
+ pointerEvents: 'none',
3274
+ }, children: ["(", value?.length, "/", maxLength, ")"] }))] }), getHelperText() && (jsx(Box, { sx: {
3275
+ color: getHelperTextColor(),
3276
+ marginTop: '4px',
3277
+ ...TYPOGRAPHY.textFieldHelper,
3278
+ }, children: getHelperText() }))] }));
3279
+ };
3280
+
3281
+ const StyledTextField = styled(MuiTextField)(({ theme }) => {
1968
3282
  return {
1969
3283
  '& .MuiOutlinedInput-root': {
1970
3284
  '& fieldset': { borderColor: COLOR_NEUTRAL[300] },
@@ -2006,10 +3320,221 @@ styled(MuiTextField)(({ theme }) => {
2006
3320
  },
2007
3321
  };
2008
3322
  });
3323
+ const TextFieldComponent = ({ label, placeholder = 'Placeholder', value, disabled = false, error = false, success = false, errorMessage, successMessage, borderRadius = 6, helperText, onChange, iconBefore, iconAfter, sx, ...props }) => {
3324
+ return (jsxs(Box, { sx: { ...sx }, children: [label && (jsx(Typography, { sx: {
3325
+ display: 'block',
3326
+ ...TYPOGRAPHY.textFieldLabel,
3327
+ color: COLOR_GRAY[800],
3328
+ marginBottom: '4px',
3329
+ }, children: label })), jsx(StyledTextField, { placeholder: placeholder, value: value, disabled: disabled, error: error, helperText: error ? errorMessage : helperText, size: "small", fullWidth: true, onChange: onChange, InputProps: {
3330
+ startAdornment: iconBefore ? jsx(InputAdornment, { position: "start", children: iconBefore }) : undefined,
3331
+ endAdornment: iconAfter ? jsx(InputAdornment, { position: "end", children: iconAfter }) : undefined,
3332
+ }, sx: {
3333
+ '& .MuiOutlinedInput-root': {
3334
+ borderRadius: borderRadius === 'max' ? '100px' : `${borderRadius}px`,
3335
+ ...(success && {
3336
+ '&.Mui-focused': {
3337
+ boxShadow: `0 1px 2px 0 rgba(10, 13, 18, 0.05), 0 0 0 4px ${COLOR_SUCCESS[100]}`,
3338
+ },
3339
+ }),
3340
+ },
3341
+ }, ...props }), success && !error && successMessage && (jsx(Typography, { sx: {
3342
+ ...TYPOGRAPHY.textFieldHelper,
3343
+ color: COLOR_SUCCESS[500],
3344
+ marginTop: '4px',
3345
+ }, children: successMessage }))] }));
3346
+ };
2009
3347
 
2010
- ({
3348
+ const SX_STYLES = {
2011
3349
  progressBar: {
2012
- backgroundColor: COLOR_GRAY[200]}});
3350
+ position: 'absolute',
3351
+ top: 0,
3352
+ left: 0,
3353
+ height: '100%',
3354
+ backgroundColor: COLOR_GRAY[200],
3355
+ opacity: 0.8,
3356
+ transition: 'width 0.3s ease, opacity 0.3s ease',
3357
+ animation: 'wave 1.5s linear infinite',
3358
+ '@keyframes wave': {
3359
+ '0%': { backgroundPosition: '0% 0%' },
3360
+ '50%': { backgroundPosition: '100% 0%' },
3361
+ '100%': { backgroundPosition: '0% 0%' },
3362
+ },
3363
+ },
3364
+ imageIcon: {
3365
+ width: '40px',
3366
+ height: '40px',
3367
+ flexShrink: 0,
3368
+ position: 'relative',
3369
+ zIndex: 1,
3370
+ },
3371
+ contentBox: {
3372
+ flexGrow: 1,
3373
+ minWidth: 0,
3374
+ position: 'relative',
3375
+ zIndex: 1,
3376
+ },
3377
+ textBox: {
3378
+ minWidth: 0,
3379
+ flexGrow: 1,
3380
+ mr: 2,
3381
+ },
3382
+ actionBox: {
3383
+ flexShrink: 0,
3384
+ display: 'flex',
3385
+ alignItems: 'center',
3386
+ position: 'relative',
3387
+ zIndex: 1,
3388
+ },
3389
+ linearProgress: {
3390
+ mt: 1,
3391
+ width: '100%',
3392
+ },
3393
+ };
3394
+
3395
+ const VIDEO_EXTENSIONS = ['mp4', 'mov', 'avi', 'wmv', 'flv', 'mkv', 'webm'];
3396
+ const ICON_PATH = {
3397
+ VIDEO: '/images/icon/film.svg',
3398
+ FILE: '/images/icon/file.svg',
3399
+ };
3400
+ // ============================================================================
3401
+ // Helper Functions
3402
+ // ============================================================================
3403
+ const formatFileSize = (bytes) => {
3404
+ if (bytes === 0)
3405
+ return '0 Bytes';
3406
+ const k = 1024;
3407
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
3408
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
3409
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
3410
+ };
3411
+ const getFileIcon = (fileName) => {
3412
+ const ext = fileName.split('.').pop()?.toLowerCase();
3413
+ return ext && VIDEO_EXTENSIONS.includes(ext) ? ICON_PATH.VIDEO : ICON_PATH.FILE;
3414
+ };
3415
+ // ============================================================================
3416
+ // Component
3417
+ // ============================================================================
3418
+ const UploaderItemComponent = ({ file, progress = 0, status = 'pending', onDelete, onRetry, isProcess = true, borderSuccess, borderError = COLOR_ERROR[600], sx, }) => {
3419
+ // Memoize
3420
+ const iconSrc = useMemo(() => getFileIcon(file.name), [file.name]);
3421
+ // Status flags
3422
+ const isCompleted = status === 'success';
3423
+ const isFailed = status === 'failed';
3424
+ const isUploading = status === 'uploading';
3425
+ // Determine colors based on status
3426
+ const borderColor = isFailed
3427
+ ? String(borderError)
3428
+ : isCompleted && borderSuccess
3429
+ ? String(borderSuccess)
3430
+ : '#e0e0e0';
3431
+ const textColor = isFailed ? String(borderError) : '#737373';
3432
+ const subtitleColor = isFailed ? String(borderError) : '#a3a3a3';
3433
+ // Render progress info
3434
+ const renderProgressInfo = () => {
3435
+ if (isUploading && !isCompleted)
3436
+ return ` • ${Math.round(progress)}% uploaded`;
3437
+ if (isFailed)
3438
+ return ' • Upload failed';
3439
+ return '';
3440
+ };
3441
+ return (jsxs(Box, { sx: {
3442
+ display: 'flex',
3443
+ alignItems: 'flex-start',
3444
+ padding: '16px',
3445
+ border: '1px solid',
3446
+ borderColor: borderColor,
3447
+ borderRadius: '8px',
3448
+ gap: '16px',
3449
+ position: 'relative',
3450
+ overflow: 'hidden',
3451
+ transition: 'background-color 0.3s ease',
3452
+ backgroundColor: 'background.paper',
3453
+ ...sx,
3454
+ }, children: [!isProcess && isUploading && !isCompleted && !isFailed && (jsx(Box, { sx: { ...SX_STYLES.progressBar, width: `${progress}%` } })), jsx(Box, { component: "img", src: iconSrc, alt: "file icon", sx: SX_STYLES.imageIcon }), jsxs(Box, { sx: SX_STYLES.contentBox, children: [jsxs(StackRowAlignCenterJustBetween, { children: [jsxs(Box, { sx: SX_STYLES.textBox, children: [jsx(Typography, { noWrap: true, title: file.name, sx: { ...TYPOGRAPHY.text14Medium, color: textColor }, children: file.name }), jsxs(Typography, { sx: { ...TYPOGRAPHY.text14Regular, color: subtitleColor }, children: [formatFileSize(file.size), renderProgressInfo()] })] }), jsxs(Box, { sx: SX_STYLES.actionBox, children: [!isProcess && isUploading && !isCompleted && (jsx(Box, { sx: { position: 'relative', display: 'inline-flex', mr: 1 }, children: jsx(CircularProgress, { variant: "determinate", value: progress, color: "success", size: 24 }) })), isCompleted && jsx(CheckCircleIcon, { color: "success", sx: { mr: 1 } }), isFailed && onRetry && (jsx(IconButton, { size: "small", onClick: onRetry, title: "Retry upload", children: jsx(RefreshIcon, { fontSize: "small", sx: { color: borderError } }) })), !isFailed && onDelete && (jsx(IconButton, { size: "small", onClick: onDelete, children: jsx(IconElement, { icon: "delete" }) }))] })] }), isProcess && isUploading && (jsx(Box, { sx: SX_STYLES.linearProgress, children: jsx(LinearProgress, { variant: "determinate", value: progress, color: "success", sx: { height: 8, borderRadius: 4 } }) }))] })] }));
3455
+ };
3456
+
3457
+ const UploaderComponent = ({ onFilesSelected, accept = '*', multiple = true, children, sx, labelSx, uploadLabel = 'Click to upload', appearance, files: externalFiles, onDeleteFile, onRetryFile, borderError, }) => {
3458
+ const fileInputRef = useRef(null);
3459
+ const [isDragging, setIsDragging] = useState(false);
3460
+ // Sử dụng external files nếu có, nếu không hiển thị rỗng
3461
+ const displayFiles = externalFiles || [];
3462
+ const handleClick = () => {
3463
+ fileInputRef.current?.click();
3464
+ };
3465
+ const handleFileChange = (event) => {
3466
+ const selectedFiles = event.target.files;
3467
+ if (selectedFiles) {
3468
+ const fileArray = Array.from(selectedFiles);
3469
+ onFilesSelected(fileArray);
3470
+ }
3471
+ event.target.value = '';
3472
+ };
3473
+ const handleDeleteFile = (file) => {
3474
+ if (onDeleteFile) {
3475
+ onDeleteFile(file);
3476
+ }
3477
+ };
3478
+ const handleDragOver = (e) => {
3479
+ e.preventDefault();
3480
+ e.stopPropagation();
3481
+ setIsDragging(true);
3482
+ };
3483
+ const handleDragLeave = (e) => {
3484
+ e.preventDefault();
3485
+ e.stopPropagation();
3486
+ setIsDragging(false);
3487
+ };
3488
+ const handleDrop = (e) => {
3489
+ e.preventDefault();
3490
+ e.stopPropagation();
3491
+ setIsDragging(false);
3492
+ const files = e.dataTransfer.files;
3493
+ if (files) {
3494
+ onFilesSelected(Array.from(files));
3495
+ }
3496
+ };
3497
+ return (jsxs(Box, { sx: {
3498
+ border: '2px solid',
3499
+ borderColor: appearance?.borderColor || 'action.selected',
3500
+ borderRadius: '8px',
3501
+ padding: '32px 24px',
3502
+ textAlign: 'center',
3503
+ cursor: 'pointer',
3504
+ transition: 'all 0.3s ease',
3505
+ backgroundColor: isDragging ? 'action.selected' : appearance?.background || 'background.paper',
3506
+ '&:hover': {
3507
+ borderColor: appearance?.borderColorHover || appearance?.borderColor || 'primary.main',
3508
+ filter: 'brightness(0.92)',
3509
+ },
3510
+ ...sx,
3511
+ }, onClick: handleClick, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, style: { display: 'none' } }), displayFiles.length === 0 && (jsx(Fragment, { children: children ? (jsxs(Box, { children: [children, jsxs(Box, { component: "p", sx: {
3512
+ color: 'primary.main',
3513
+ textDecoration: 'underline',
3514
+ fontWeight: 500,
3515
+ fontSize: '14px',
3516
+ marginTop: '12px',
3517
+ margin: '12px 0 0 0',
3518
+ ...labelSx,
3519
+ }, children: [uploadLabel, " or drag and drop"] })] })) : (jsxs(Box, { children: [jsx(Box, { component: "img", src: "/images/icon/uploader.svg", alt: "Upload icon", sx: {
3520
+ width: '46px',
3521
+ height: '46px',
3522
+ marginBottom: '12px',
3523
+ } }), jsxs(Box, { component: "p", sx: {
3524
+ color: 'primary.main',
3525
+ textDecoration: 'underline',
3526
+ fontWeight: 500,
3527
+ fontSize: '14px',
3528
+ margin: 0,
3529
+ ...labelSx,
3530
+ }, children: [uploadLabel, " or drag and drop"] })] })) })), displayFiles && displayFiles.length > 0 && (jsx(Box, { sx: {
3531
+ marginTop: '24px',
3532
+ display: 'flex',
3533
+ flexDirection: 'column',
3534
+ gap: '12px',
3535
+ textAlign: 'left',
3536
+ }, onClick: (e) => e.stopPropagation(), children: displayFiles.map((item, index) => (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))) }))] }));
3537
+ };
2013
3538
 
2014
- export { AVATAR_SIZES, AvatarColor, AvatarComponent, AvatarGroupComponent, AvatarLabelGroupComponent, AvatarProfileComponent, AvatarUserComponent, BADGE_FONT_SIZES, BADGE_SIZES, BadgeImage, BadgeLive, BadgeNumber, BadgeOnline, ButtonComponent, COLOR_ACCENT, COLOR_BRAND, COLOR_ERROR, COLOR_GRAY, COLOR_INFO, COLOR_NEUTRAL, COLOR_SUCCESS, COLOR_WARNING, CheckboxContentComponent, DialogWrapper, FONT_FAMILY, FONT_SIZE, FONT_STYLE, FONT_WEIGHT, IconElement, ImageElement, ImageSizeType, LINE_HEIGHT, LinkElement, LinkInternalElement, MAP_SIZE, ModalComponent as Modal, ModalCardComponent as ModalCard, ModalDescription, ModalIcon, ModalTitle, SIZE_EXTRA_LARGE, style_constant as STYLE, TYPOGRAPHY, TYPOGRAPHY_STYLES, TypographyOneLine, createTypography, getBadgePosition };
3539
+ export { AVATAR_SIZES, AvatarColor, AvatarComponent, AvatarGroupComponent, AvatarLabelGroupComponent, AvatarProfileComponent, AvatarUserComponent, BADGE_FONT_SIZES, BADGE_SIZES, BadgeImage, BadgeLive, BadgeNumber, BadgeOnline, BreadcrumbsComponent, ButtonBarComponent, ButtonComponent, COLOR_ACCENT, COLOR_BRAND, COLOR_ERROR, COLOR_GRAY, COLOR_INFO, COLOR_NEUTRAL, COLOR_SUCCESS, COLOR_WARNING, CheckboxComponent, CheckboxContentComponent, ChipComponent, DateFieldComponent, DateRangePickerComponent, DialogWrapper, DropdownFieldComponent, FONT_FAMILY, FONT_SIZE, FONT_STYLE, FONT_WEIGHT, GridComponent, IconElement, ImageElement, ImageSizeType, InputStepperComponent, LINE_HEIGHT, LinkElement, LinkFieldComponent, LinkInternalElement, MAP_SIZE, ModalComponent as Modal, ModalCardComponent as ModalCard, ModalDescription, ModalIcon, ModalTitle, MoneyFieldComponent, PINComponent, PhoneNumberFieldComponent, SIZE_EXTRA_LARGE, style_constant as STYLE, SearchDropdownComponent, SearchFieldComponent, SwitchComponent, TYPOGRAPHY, TYPOGRAPHY_STYLES, TabsComponent, TextAreaComponent, TextFieldComponent, TypographyOneLine, UploaderComponent, createTypography, getBadgePosition };
2015
3540
  //# sourceMappingURL=index.js.map