tp-react-elements 2.0.7 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index.esm11.js +2 -2
- package/dist/_virtual/index.esm12.js +2 -2
- package/dist/_virtual/index.esm13.js +2 -2
- package/dist/_virtual/index.esm14.js +2 -2
- package/dist/_virtual/index.esm4.js +2 -2
- package/dist/_virtual/index.esm5.js +2 -2
- package/dist/_virtual/index.esm6.js +2 -2
- package/dist/components/Button/Button.esm.js +9 -5
- package/dist/components/DatePicker/index.d.ts +1 -0
- package/dist/components/DatePicker/index.esm.js +2 -1
- package/dist/components/DeleteComponent/DeleteField.esm.js +20 -14
- package/dist/components/Form/FormActiveSwitch.esm.js +5 -1
- package/dist/components/Form/FormConstants.esm.js +1 -0
- package/dist/components/Form/FormRender.esm.js +14 -10
- package/dist/components/Form/FormRenderWrapper.esm.js +2 -2
- package/dist/components/FormComponents/DatePicker/MonthPickerMultiRender.d.ts +6 -0
- package/dist/components/FormComponents/DatePicker/MonthPickerMultiRender.esm.js +117 -0
- package/dist/components/FormComponents/DatePicker/Monthpickerrender.esm.js +6 -4
- package/dist/components/FormComponents/FileUpload/MultiFileWithPreview.esm.js +38 -6
- package/dist/components/FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js +11 -8
- package/dist/components/FormComponents/FormRadioGroup/FormRadioGroup.esm.js +14 -12
- package/dist/components/FormComponents/Select/MultiSelectTable.esm.js +176 -160
- package/dist/components/FormComponents/Select/MultiSelectv1.esm.js +2 -2
- package/dist/components/FormComponents/index.esm.js +16 -22
- package/dist/components/ModalField/ConfirmationDialog.esm.js +37 -37
- package/dist/components/ModalField/ModalField.esm.js +19 -16
- package/dist/components/PaginatedGrid/DynamicFilterBar.d.ts +12 -4
- package/dist/components/PaginatedGrid/DynamicFilterBar.esm.js +48 -16
- package/dist/components/PaginatedGrid/GenericPaginatedGrid.d.ts +8 -1
- package/dist/components/PaginatedGrid/GenericPaginatedGrid.esm.js +80 -20
- package/dist/components/PaginatedGrid/types.d.ts +25 -0
- package/dist/components/SelectField/MultiSelectFieldComponent.esm.js +19 -21
- package/dist/node_modules/react-date-range/dist/components/Calendar/index.esm.js +2 -2
- package/dist/node_modules/react-date-range/dist/components/DateInput/index.esm.js +3 -3
- package/dist/node_modules/react-date-range/dist/components/DateRange/index.esm.js +3 -3
- package/dist/node_modules/react-date-range/dist/components/DateRangePicker/index.esm.js +3 -3
- package/dist/node_modules/react-date-range/dist/components/DayCell/index.esm.js +3 -3
- package/dist/node_modules/react-date-range/dist/components/DefinedRange/index.esm.js +3 -3
- package/dist/node_modules/react-date-range/dist/components/InputRangeField/index.esm.js +3 -3
- package/dist/node_modules/react-date-range/dist/components/Month/index.esm.js +3 -3
- package/dist/node_modules/react-list/react-list.esm.js +2 -2
- package/dist/utils/Interface/FormInterface.d.ts +1 -1
- package/package.json +2 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DayCell = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DayCell as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var InputRangeField = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { InputRangeField as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateInput = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateInput as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var Month = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { Month as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateRange = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateRange as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateRangePicker = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateRangePicker as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DefinedRange = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DefinedRange as __exports };
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useAuth } from '../../auth/authContext.esm.js';
|
|
3
3
|
import { Button } from '@mui/material';
|
|
4
|
+
import { useMemo, useCallback } from 'react';
|
|
4
5
|
|
|
5
6
|
const ButtonStyled = ({ name }) => {
|
|
6
7
|
const { projectTheme } = useAuth();
|
|
7
8
|
const ButtonConfig = projectTheme?.Styles.Component.TPButton.find((item) => item.name === name);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const commonSx = projectTheme?.Styles.Component.TPButtonCommonSx;
|
|
10
|
+
const sx = useMemo(() => ({ ...commonSx }), [commonSx]);
|
|
11
|
+
const handleClick = useCallback(() => {
|
|
12
|
+
if (ButtonConfig?.isEnableConfirmDialog) {
|
|
13
|
+
alert('Congirm');
|
|
14
|
+
}
|
|
15
|
+
}, [ButtonConfig?.isEnableConfirmDialog]);
|
|
16
|
+
return (jsx(Button, { sx: sx, onClick: handleClick, children: name }));
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
export { ButtonStyled as default };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { default as DatepickerWrapperV2 } from '../FormComponents/DatePicker/DatepickerWrapperV2';
|
|
2
2
|
export { default as Monthpickerrender } from '../FormComponents/DatePicker/Monthpickerrender';
|
|
3
|
+
export { default as MonthPickerMultiRender } from '../FormComponents/DatePicker/MonthPickerMultiRender';
|
|
3
4
|
export { default as YearPickerField } from '../FormComponents/YearPickerField/YearPickerField';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import DatepickerWrapperV2 from '../FormComponents/DatePicker/DatepickerWrapperV2.esm.js';
|
|
2
2
|
import Monthpickerrender from '../FormComponents/DatePicker/Monthpickerrender.esm.js';
|
|
3
|
+
import MonthPickerMultiRender from '../FormComponents/DatePicker/MonthPickerMultiRender.esm.js';
|
|
3
4
|
export { default as YearPickerField } from '../FormComponents/YearPickerField/YearPickerField.esm.js';
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
export { DatepickerWrapperV2, Monthpickerrender };
|
|
8
|
+
export { DatepickerWrapperV2, MonthPickerMultiRender, Monthpickerrender };
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Tooltip, Grid, Typography } from '@mui/material';
|
|
3
3
|
import ConfirmationDialog from '../ModalField/ConfirmationDialog.esm.js';
|
|
4
|
-
import { useState } from 'react';
|
|
4
|
+
import { useState, useCallback } from 'react';
|
|
5
5
|
|
|
6
|
+
// ─── Module-level static style objects ─────────────────────────────────────
|
|
7
|
+
const TOOLTIP_TEXT_SX = { fontSize: '8px' };
|
|
8
|
+
const GRID_SX = { ':hover': { cursor: 'pointer' } };
|
|
9
|
+
const TRASH_ICON_STYLE = {
|
|
10
|
+
lineHeight: 1,
|
|
11
|
+
fontSize: '11px',
|
|
12
|
+
color: 'var(--mui-palette-error-main, #B3261E)',
|
|
13
|
+
marginTop: '10px',
|
|
14
|
+
};
|
|
6
15
|
const DeleteField = ({ onClickFn, tooltip = 'Delete', text }) => {
|
|
7
16
|
const [openDialog, setOpenDialog] = useState(false);
|
|
8
|
-
const handleCancel = () => {
|
|
17
|
+
const handleCancel = useCallback(() => {
|
|
9
18
|
setOpenDialog(false);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
handleCancel();
|
|
20
|
-
onClickFn();
|
|
21
|
-
}, text: text })] }));
|
|
19
|
+
}, []);
|
|
20
|
+
const handleOpen = useCallback(() => {
|
|
21
|
+
setOpenDialog(true);
|
|
22
|
+
}, []);
|
|
23
|
+
const handleSubmit = useCallback(() => {
|
|
24
|
+
handleCancel();
|
|
25
|
+
onClickFn();
|
|
26
|
+
}, [handleCancel, onClickFn]);
|
|
27
|
+
return (jsxs(Fragment, { children: [jsx(Tooltip, { title: jsx(Typography, { sx: TOOLTIP_TEXT_SX, children: tooltip }), children: jsx(Grid, { sx: GRID_SX, onClick: handleOpen, children: jsx("i", { className: "flaticon-trash", style: TRASH_ICON_STYLE }) }) }), jsx(ConfirmationDialog, { openConfirmDialog: openDialog, handleCancel: handleCancel, onClickSubmit: handleSubmit, text: text })] }));
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
export { DeleteField as default };
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Controller } from 'react-hook-form';
|
|
3
3
|
|
|
4
|
+
// Static — hoisted so they aren't rebuilt on every render.
|
|
5
|
+
const FIELDSET_STYLE = { border: 'none', padding: 0, margin: 0 };
|
|
6
|
+
const LEGEND_STYLE = { fontSize: '12px', paddingRight: 10 };
|
|
7
|
+
const LABEL_STYLE = { textTransform: 'none' };
|
|
4
8
|
const FormActiveSwitch = ({ props }) => {
|
|
5
9
|
const { control, item } = props;
|
|
6
10
|
const valueActive = item.value1 ?? 'A';
|
|
7
11
|
const valueInactive = item.value2 ?? 'I';
|
|
8
|
-
return (jsxs("fieldset", { className: "m-form__input", style:
|
|
12
|
+
return (jsxs("fieldset", { className: "m-form__input", style: FIELDSET_STYLE, children: [item.label && (jsx("legend", { id: `${item.name}-label`, style: LEGEND_STYLE, children: item.label })), jsx(Controller, { name: item.name, control: control, render: ({ field }) => (jsxs("span", { className: "switch prestashop-switch fixed-width-lg", role: "radiogroup", "aria-labelledby": `${item.name}-label`, children: [jsx("input", { id: `${item.name}_on`, name: item.name, type: "radio", value: valueActive, checked: field.value === valueActive, onChange: () => field.onChange(valueActive), "aria-checked": field.value === valueActive }), jsx("label", { htmlFor: `${item.name}_on`, style: LABEL_STYLE, children: item.label1 ?? 'Active' }), jsx("input", { id: `${item.name}_off`, name: item.name, type: "radio", value: valueInactive, checked: field.value === valueInactive, onChange: () => field.onChange(valueInactive), "aria-checked": field.value === valueInactive }), jsx("label", { htmlFor: `${item.name}_off`, style: LABEL_STYLE, children: item.label2 ?? 'In Active' }), jsx("span", { className: "slide-button btn", "aria-hidden": "true" })] })) })] }));
|
|
9
13
|
};
|
|
10
14
|
|
|
11
15
|
export { FormActiveSwitch as default };
|
|
@@ -282,6 +282,7 @@ const useFormValidatingContext = (formArray) => {
|
|
|
282
282
|
renderCustomError(field);
|
|
283
283
|
break;
|
|
284
284
|
case 'monthpicker':
|
|
285
|
+
case 'monthpicker-multi':
|
|
285
286
|
initialValues[field.name] = null;
|
|
286
287
|
if (field.required && field.errorMessage) {
|
|
287
288
|
validationShape[field.name] = (validationShape[field.name] || Yup.string())
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
3
|
-
import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
|
|
2
|
+
import require$$0__default, { lazy, Suspense } from 'react';
|
|
4
3
|
import FormCheckBoxGroup from '../FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
|
|
5
|
-
import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.esm.js';
|
|
6
|
-
import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
|
|
7
|
-
import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
|
|
8
|
-
import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
|
|
9
|
-
import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
|
|
10
|
-
import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
|
|
11
4
|
import SingleSelect from '../FormComponents/Select/SingleSelect.esm.js';
|
|
12
5
|
import MultiSelectAutocomplete from '../FormComponents/Select/MultiSelectAutocomplete.esm.js';
|
|
13
6
|
import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
|
|
@@ -18,6 +11,13 @@ import DateRangePickerComponent from '../DateRangePicker/DateRangePicker.esm.js'
|
|
|
18
11
|
import RichTextEditorWrapper from '../FormComponents/RichTextEditor/RichTextEditorWrapper.esm.js';
|
|
19
12
|
import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
|
|
20
13
|
import DatetimepickerWrapper from '../FormComponents/DatePicker/DatetimepickerWrapper.esm.js';
|
|
14
|
+
import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
|
|
15
|
+
import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
|
|
16
|
+
import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
|
|
17
|
+
import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
|
|
18
|
+
import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.esm.js';
|
|
19
|
+
import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
|
|
20
|
+
import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
|
|
21
21
|
|
|
22
22
|
// Lazy-loaded heavy components
|
|
23
23
|
// const RichTextEditorWrapper = lazy(() =>
|
|
@@ -30,6 +30,7 @@ const FormRenderMultiFileUpload = lazy(() => import('../FileUpload/index.esm.js'
|
|
|
30
30
|
const MultiFileWithPreview = lazy(() => import('../FileUpload/index.esm.js').then((m) => ({ default: m.MultiFileWithPreview })));
|
|
31
31
|
const DatepickerWrapperV2 = lazy(() => import('../DatePicker/index.esm.js').then((m) => ({ default: m.DatepickerWrapperV2 })));
|
|
32
32
|
const Monthpickerrender = lazy(() => import('../DatePicker/index.esm.js').then((m) => ({ default: m.Monthpickerrender })));
|
|
33
|
+
const MonthPickerMultiRender = lazy(() => import('../DatePicker/index.esm.js').then((m) => ({ default: m.MonthPickerMultiRender })));
|
|
33
34
|
const TimePickerFieldWrapper = lazy(() => import('../TimePicker/index.esm.js').then((m) => ({ default: m.TimePicker })));
|
|
34
35
|
// Loading fallback component
|
|
35
36
|
const LoadingFallback = () => jsx("div", { style: { minHeight: '36px' }, children: "Loading..." });
|
|
@@ -119,9 +120,12 @@ const RenderForm = (props) => {
|
|
|
119
120
|
// Time picker
|
|
120
121
|
case 'timepicker':
|
|
121
122
|
return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(TimePickerFieldWrapper, { props: props, variant: variant }) }));
|
|
122
|
-
// Month picker
|
|
123
|
+
// Month picker (single month)
|
|
123
124
|
case 'monthpicker':
|
|
124
125
|
return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(Monthpickerrender, { props: props, variant: variant }) }));
|
|
126
|
+
// Month picker (multiple months)
|
|
127
|
+
case 'monthpicker-multi':
|
|
128
|
+
return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(MonthPickerMultiRender, { props: props, variant: variant }) }));
|
|
125
129
|
// Multi-select dropdown
|
|
126
130
|
case 'multiselect':
|
|
127
131
|
return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(MultiSelectV1, { props: props, variant: variant }) }));
|
|
@@ -161,6 +165,6 @@ const RenderForm = (props) => {
|
|
|
161
165
|
*
|
|
162
166
|
* @see FormRenderWrapper
|
|
163
167
|
*/
|
|
164
|
-
var RenderForm$1 =
|
|
168
|
+
var RenderForm$1 = require$$0__default.memo(RenderForm);
|
|
165
169
|
|
|
166
170
|
export { RenderForm$1 as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
2
|
+
import require$$0__default from 'react';
|
|
3
3
|
import { useFormState } from 'react-hook-form';
|
|
4
4
|
import { FormComponent, Formitem } from './Form.styles.esm.js';
|
|
5
5
|
import RenderForm from './FormRender.esm.js';
|
|
@@ -28,6 +28,6 @@ const FormRenderWrapper = ({ formArray, name = '', numberOfColumns = 3, form, va
|
|
|
28
28
|
* Export the FormRenderWrapper component for use in form containers
|
|
29
29
|
* This component is the main entry point for rendering form fields
|
|
30
30
|
*/
|
|
31
|
-
var FormRenderWrapper_default =
|
|
31
|
+
var FormRenderWrapper_default = require$$0__default.memo(FormRenderWrapper);
|
|
32
32
|
|
|
33
33
|
export { FormRenderWrapper_default as default };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
3
|
+
import { FormControl, Box, TextField, InputAdornment, Popover, IconButton, Typography, Button } from '@mui/material';
|
|
4
|
+
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
|
5
|
+
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
|
6
|
+
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
|
7
|
+
import { useWatch } from 'react-hook-form';
|
|
8
|
+
import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
|
|
9
|
+
import 'dayjs';
|
|
10
|
+
import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
|
|
11
|
+
|
|
12
|
+
const MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
13
|
+
const parseMY = (s) => {
|
|
14
|
+
const m = /^(\d{2})\/(\d{4})$/.exec(s.trim());
|
|
15
|
+
return m ? [Number(m[1]), Number(m[2])] : null; // [month, year]
|
|
16
|
+
};
|
|
17
|
+
const toKey = (month, year) => year * 100 + month; // sortable comparable int
|
|
18
|
+
const toMY = (month, year) => `${String(month).padStart(2, '0')}/${year}`;
|
|
19
|
+
const parseValue = (value) => value ? value.split(',').map((s) => s.trim()).filter(Boolean) : [];
|
|
20
|
+
const MonthPickerMultiRender = ({ props, variant, }) => {
|
|
21
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
22
|
+
const [viewYear, setViewYear] = useState(() => new Date().getFullYear());
|
|
23
|
+
const [draft, setDraft] = useState([]);
|
|
24
|
+
const value = useWatch({ control: props.control, name: props.item.name });
|
|
25
|
+
const committed = useMemo(() => parseValue(value), [value]);
|
|
26
|
+
const isError = !!props.errors?.[props.item.name];
|
|
27
|
+
const minKey = useMemo(() => {
|
|
28
|
+
const parsed = props.item.minDate ? parseMY(props.item.minDate) : null;
|
|
29
|
+
return parsed ? toKey(parsed[0], parsed[1]) : null;
|
|
30
|
+
}, [props.item.minDate]);
|
|
31
|
+
const maxKey = useMemo(() => {
|
|
32
|
+
const parsed = props.item.maxDate ? parseMY(props.item.maxDate) : null;
|
|
33
|
+
return parsed ? toKey(parsed[0], parsed[1]) : null;
|
|
34
|
+
}, [props.item.maxDate]);
|
|
35
|
+
const handleOpen = useCallback((e) => {
|
|
36
|
+
if (props.item.disable)
|
|
37
|
+
return;
|
|
38
|
+
const committedNow = parseValue(value);
|
|
39
|
+
setDraft(committedNow);
|
|
40
|
+
if (committedNow.length > 0) {
|
|
41
|
+
const years = committedNow.map((s) => Number(s.split('/')[1]));
|
|
42
|
+
setViewYear(Math.min(...years));
|
|
43
|
+
}
|
|
44
|
+
setAnchorEl(e.currentTarget);
|
|
45
|
+
}, [props.item.disable, value]);
|
|
46
|
+
const handleClose = useCallback(() => setAnchorEl(null), []);
|
|
47
|
+
const toggleMonth = useCallback((my) => {
|
|
48
|
+
setDraft((prev) => (prev.includes(my) ? prev.filter((v) => v !== my) : [...prev, my]));
|
|
49
|
+
}, []);
|
|
50
|
+
const handleApply = useCallback(() => {
|
|
51
|
+
const sorted = [...draft].sort((a, b) => {
|
|
52
|
+
const [am, ay] = a.split('/').map(Number);
|
|
53
|
+
const [bm, by] = b.split('/').map(Number);
|
|
54
|
+
return toKey(am, ay) - toKey(bm, by);
|
|
55
|
+
});
|
|
56
|
+
const next = sorted.join(',');
|
|
57
|
+
props.setValue(props.item.name, next);
|
|
58
|
+
props.item.onChangeFn && props.item.onChangeFn(next);
|
|
59
|
+
props.clearErrors && props.clearErrors(props.item.name);
|
|
60
|
+
setAnchorEl(null);
|
|
61
|
+
}, [draft, props]);
|
|
62
|
+
const displayValue = useMemo(() => {
|
|
63
|
+
if (committed.length === 0)
|
|
64
|
+
return '';
|
|
65
|
+
if (committed.length <= 3) {
|
|
66
|
+
return committed
|
|
67
|
+
.map((my) => {
|
|
68
|
+
const [m, y] = my.split('/').map(Number);
|
|
69
|
+
return `${MONTH_LABELS[m - 1]} ${y}`;
|
|
70
|
+
})
|
|
71
|
+
.join(', ');
|
|
72
|
+
}
|
|
73
|
+
return `${committed.length} months selected`;
|
|
74
|
+
}, [committed]);
|
|
75
|
+
const muiLabel = variant !== 'standard' ? `${props.item.label}${props.item.required ? ' *' : ''}` : '';
|
|
76
|
+
return (jsxs(FormControl, { fullWidth: true, sx: { position: 'relative' }, disabled: props.item.disable, children: [renderLabel(variant, props), jsx(Box, { onClick: handleOpen, sx: { cursor: props.item.disable ? 'default' : 'pointer' }, children: jsx(TextField, { fullWidth: true, value: displayValue, label: muiLabel, placeholder: props.item.placeholder || 'Select month(s)…', error: isError, size: "small", InputProps: {
|
|
77
|
+
readOnly: true,
|
|
78
|
+
endAdornment: (jsx(InputAdornment, { position: "end", children: jsx(CalendarTodayIcon, { sx: { fontSize: 15, color: 'text.disabled' } }) })),
|
|
79
|
+
sx: { cursor: 'inherit' },
|
|
80
|
+
}, inputProps: {
|
|
81
|
+
'aria-labelledby': `${props.item.name}-label`,
|
|
82
|
+
'aria-describedby': `${props.item.name}-helper ${props.item.name}-error`,
|
|
83
|
+
'aria-required': props.item.required ? true : undefined,
|
|
84
|
+
style: { cursor: 'inherit' },
|
|
85
|
+
}, sx: {
|
|
86
|
+
'& .MuiFormLabel-root': { top: -10 },
|
|
87
|
+
'& .MuiInputLabel-shrink': { top: 0 },
|
|
88
|
+
} }) }), jsxs(Popover, { open: Boolean(anchorEl), anchorEl: anchorEl, onClose: handleClose, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: { sx: { mt: 0.5, borderRadius: 2, boxShadow: 6, width: 280 } }, children: [jsxs(Box, { sx: {
|
|
89
|
+
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
|
90
|
+
px: 1.5, py: 0.75,
|
|
91
|
+
borderBottom: '1px solid', borderColor: 'divider',
|
|
92
|
+
}, children: [jsx(IconButton, { size: "small", onClick: () => setViewYear((y) => y - 1), children: jsx(ChevronLeftIcon, { sx: { fontSize: 18 } }) }), jsx(Typography, { fontWeight: 600, sx: { fontSize: 14, userSelect: 'none' }, children: viewYear }), jsx(IconButton, { size: "small", onClick: () => setViewYear((y) => y + 1), children: jsx(ChevronRightIcon, { sx: { fontSize: 18 } }) })] }), jsx(Box, { sx: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 0.75, p: 1.5 }, children: MONTH_LABELS.map((mon, idx) => {
|
|
93
|
+
const monthNum = idx + 1;
|
|
94
|
+
const my = toMY(monthNum, viewYear);
|
|
95
|
+
const key = toKey(monthNum, viewYear);
|
|
96
|
+
const outOfRange = (minKey !== null && key < minKey) || (maxKey !== null && key > maxKey);
|
|
97
|
+
const isSelected = draft.includes(my);
|
|
98
|
+
return (jsx(Button, { variant: isSelected ? 'contained' : 'outlined', size: "small", disableElevation: true, disabled: outOfRange, onClick: () => toggleMonth(my), sx: {
|
|
99
|
+
fontSize: 12,
|
|
100
|
+
textTransform: 'none',
|
|
101
|
+
borderRadius: 2,
|
|
102
|
+
py: 0.75,
|
|
103
|
+
borderColor: isSelected ? 'primary.main' : 'divider',
|
|
104
|
+
fontWeight: isSelected ? 700 : 400,
|
|
105
|
+
}, children: mon }, my));
|
|
106
|
+
}) }), draft.length > 0 && (jsxs(Box, { sx: {
|
|
107
|
+
px: 2, py: 0.75,
|
|
108
|
+
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
|
109
|
+
borderTop: '1px solid', borderColor: 'divider',
|
|
110
|
+
}, children: [jsxs(Typography, { sx: { fontSize: 11, color: 'primary.main', fontWeight: 600 }, children: [draft.length, " month", draft.length > 1 ? 's' : '', " selected"] }), jsx(Button, { size: "small", color: "inherit", sx: { fontSize: 11, py: 0, textTransform: 'none', minWidth: 'auto' }, onClick: () => setDraft([]), children: "Clear" })] })), jsx(Box, { sx: {
|
|
111
|
+
px: 1.5, py: 1,
|
|
112
|
+
display: 'flex', justifyContent: 'flex-end',
|
|
113
|
+
borderTop: '1px solid', borderColor: 'divider',
|
|
114
|
+
}, children: jsx(Button, { size: "small", variant: "contained", disableElevation: true, onClick: handleApply, children: "Apply" }) })] }), jsx(FormBottomField, { ...props })] }, props.item.name));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export { MonthPickerMultiRender as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import { useRef, useState, useEffect } from 'react';
|
|
2
|
+
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
3
3
|
import { getDatePicker } from '../../../utils/Constants/date-pickers.esm.js';
|
|
4
4
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
|
5
5
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
@@ -23,13 +23,15 @@ const Monthpickerrender = ({ props, variant, }) => {
|
|
|
23
23
|
};
|
|
24
24
|
loadComponents();
|
|
25
25
|
}, []);
|
|
26
|
-
|
|
26
|
+
// Stable toggle — mirrors the DatepickerWrapperV2 pattern so onClick/onOpen
|
|
27
|
+
// don't hand the picker a new function reference on every render.
|
|
28
|
+
const handleToggle = useCallback(() => {
|
|
27
29
|
if (!open) {
|
|
28
30
|
ref.current?.blur();
|
|
29
31
|
}
|
|
30
32
|
document.activeElement?.blur();
|
|
31
33
|
setOpen(!open);
|
|
32
|
-
};
|
|
34
|
+
}, [open]);
|
|
33
35
|
const isError = !!props.errors?.[props.item.name];
|
|
34
36
|
const isShowBorderError = isError;
|
|
35
37
|
if (isLoading) {
|
|
@@ -38,7 +40,7 @@ const Monthpickerrender = ({ props, variant, }) => {
|
|
|
38
40
|
return (jsx(Fragment, { children: DatePicker && (jsx(Controller, { control: props.control, name: props.item.name, render: ({ field }) => (jsxs(Fragment, { children: [jsxs(LocalizationProvider, { dateAdapter: AdapterDayjs, children: [renderLabel(variant, props), jsx(DatePicker, { ref: ref, label: variant !== 'standard' &&
|
|
39
41
|
`${props.item.label}${props.item.required ? ' *' : ''}`, views: ['year', 'month'], openTo: "month", view: view, onViewChange: (newView) => setView(newView), disabled: props.item.disable, value: field.value ? dayjs(formatDateMonthAndYear(field.value || null)) : null, slotProps: {
|
|
40
42
|
textField: {
|
|
41
|
-
onClick:
|
|
43
|
+
onClick: handleToggle,
|
|
42
44
|
inputRef: ref,
|
|
43
45
|
error: isShowBorderError,
|
|
44
46
|
inputProps: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Box, Button, IconButton, Modal, Typography } from '@mui/material';
|
|
3
|
-
import { useRef, useState, useEffect, useMemo
|
|
3
|
+
import { useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
4
4
|
import { useWatch } from 'react-hook-form';
|
|
5
5
|
import 'dayjs';
|
|
6
6
|
import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
|
|
@@ -51,11 +51,6 @@ const isWordFile = (file) => {
|
|
|
51
51
|
const name = typeof file === 'string' ? file : (file.name || '');
|
|
52
52
|
return /\.(doc|docx)$/.test(name.toLowerCase());
|
|
53
53
|
};
|
|
54
|
-
const getObjectUrl = (file) => {
|
|
55
|
-
if (!file)
|
|
56
|
-
return '';
|
|
57
|
-
return typeof file === 'string' ? file : URL.createObjectURL(file);
|
|
58
|
-
};
|
|
59
54
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
60
55
|
const MultiFileWithPreview = ({ props, variant, }) => {
|
|
61
56
|
const inputRef = useRef(null);
|
|
@@ -64,6 +59,43 @@ const MultiFileWithPreview = ({ props, variant, }) => {
|
|
|
64
59
|
const [open, setOpen] = useState(false);
|
|
65
60
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
66
61
|
const [currentFileIndex, setCurrentFileIndex] = useState(0);
|
|
62
|
+
// Blob URL cache, keyed by File reference — createObjectURL was previously
|
|
63
|
+
// called fresh on every render for the same File, leaking a blob URL each
|
|
64
|
+
// time (never revoked). Now a URL is created once per File and reused;
|
|
65
|
+
// stale/unmounted entries are revoked below.
|
|
66
|
+
const objectUrlCacheRef = useRef(new Map());
|
|
67
|
+
const getObjectUrl = useCallback((file) => {
|
|
68
|
+
if (!file)
|
|
69
|
+
return '';
|
|
70
|
+
if (typeof file === 'string')
|
|
71
|
+
return file;
|
|
72
|
+
const cache = objectUrlCacheRef.current;
|
|
73
|
+
const cached = cache.get(file);
|
|
74
|
+
if (cached)
|
|
75
|
+
return cached;
|
|
76
|
+
const url = URL.createObjectURL(file);
|
|
77
|
+
cache.set(file, url);
|
|
78
|
+
return url;
|
|
79
|
+
}, []);
|
|
80
|
+
// Revoke blob URLs for files that are no longer selected (removed/replaced).
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const cache = objectUrlCacheRef.current;
|
|
83
|
+
const currentFiles = new Set(selectedFiles.filter((f) => f instanceof File));
|
|
84
|
+
cache.forEach((url, file) => {
|
|
85
|
+
if (!currentFiles.has(file)) {
|
|
86
|
+
URL.revokeObjectURL(url);
|
|
87
|
+
cache.delete(file);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}, [selectedFiles]);
|
|
91
|
+
// Revoke everything on unmount.
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const cache = objectUrlCacheRef.current;
|
|
94
|
+
return () => {
|
|
95
|
+
cache.forEach((url) => URL.revokeObjectURL(url));
|
|
96
|
+
cache.clear();
|
|
97
|
+
};
|
|
98
|
+
}, []);
|
|
67
99
|
// Clamp preview index when selection changes
|
|
68
100
|
useEffect(() => {
|
|
69
101
|
if (selectedFiles.length === 0) {
|
|
@@ -5,6 +5,16 @@ import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
|
|
|
5
5
|
import 'dayjs';
|
|
6
6
|
import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
|
|
7
7
|
|
|
8
|
+
// Identical for every checkbox in the group — hoisted so it isn't rebuilt
|
|
9
|
+
// per option on every render.
|
|
10
|
+
const CHECKBOX_LABEL_SX = {
|
|
11
|
+
'.MuiCheckbox-root': {
|
|
12
|
+
padding: '6px 2px 6px 8px',
|
|
13
|
+
'.css-imrjgg-MuiButtonBase-root-MuiCheckbox-root': {
|
|
14
|
+
color: 'rgb(0, 0, 0) !important',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
8
18
|
/**
|
|
9
19
|
* FormCheckBoxGroup Component
|
|
10
20
|
*
|
|
@@ -27,14 +37,7 @@ const FormCheckBoxGroup = ({ props, variant, }) => {
|
|
|
27
37
|
return (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(FormControl, { component: "fieldset", children: jsx(FormGroup, { row: true, children: props.item?.settings &&
|
|
28
38
|
props.item.settings.map((settings, i) => {
|
|
29
39
|
return (jsx(Controller, { name: settings.name, control: props.control, render: ({ field }) => {
|
|
30
|
-
return (jsx(FormControlLabel, { control: jsx(Checkbox, { ...field, checked: field.value }), sx: {
|
|
31
|
-
'.MuiCheckbox-root': {
|
|
32
|
-
padding: '6px 2px 6px 8px',
|
|
33
|
-
'.css-imrjgg-MuiButtonBase-root-MuiCheckbox-root': {
|
|
34
|
-
color: 'rgb(0, 0, 0) !important',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
}, label: jsx(Typography, { variant: "subtitle2", fontSize: '11px', fontWeight: 'normal !important', children: settings.label }), labelPlacement: "end" }));
|
|
40
|
+
return (jsx(FormControlLabel, { control: jsx(Checkbox, { ...field, checked: field.value }), sx: CHECKBOX_LABEL_SX, label: jsx(Typography, { variant: "subtitle2", fontSize: '11px', fontWeight: 'normal !important', children: settings.label }), labelPlacement: "end" }));
|
|
38
41
|
} }, i));
|
|
39
42
|
}) }) }), jsx(FormBottomField, { ...props })] }));
|
|
40
43
|
};
|
|
@@ -5,6 +5,19 @@ import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
|
|
|
5
5
|
import 'dayjs';
|
|
6
6
|
import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
|
|
7
7
|
|
|
8
|
+
// Static — same for the group and for every radio option — hoisted so they
|
|
9
|
+
// aren't rebuilt on every render/option.
|
|
10
|
+
const RADIO_GROUP_SX = { gap: '8px' };
|
|
11
|
+
const RADIO_SX = {
|
|
12
|
+
padding: '6px 2px 6px 8px',
|
|
13
|
+
color: 'black',
|
|
14
|
+
'&.Mui-checked': {
|
|
15
|
+
color: '#1976d2', // blue when selected
|
|
16
|
+
},
|
|
17
|
+
'& .MuiSvgIcon-root': {
|
|
18
|
+
fontSize: 16, // controls the radio icon size
|
|
19
|
+
},
|
|
20
|
+
};
|
|
8
21
|
/**
|
|
9
22
|
* FormRadioGroup Component
|
|
10
23
|
*
|
|
@@ -27,18 +40,7 @@ const FormRadioGroup = ({ props, variant }) => {
|
|
|
27
40
|
return (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(FormControl, { component: "fieldset", children: jsx(Controller, { name: props.item.name, control: props.control, render: ({ field }) => (jsx(RadioGroup, { value: field.value || '', onChange: (e) => {
|
|
28
41
|
field.onChange(e.target.value); // update form state
|
|
29
42
|
props.item.onChangeFn && props.item.onChangeFn(e.target.value); // optional handler
|
|
30
|
-
}, row: true, sx: {
|
|
31
|
-
gap: '8px',
|
|
32
|
-
}, children: props.item?.settings?.map((option, i) => (jsx(FormControlLabel, { value: option.name, control: jsx(Radio, { sx: {
|
|
33
|
-
padding: '6px 2px 6px 8px',
|
|
34
|
-
color: 'black',
|
|
35
|
-
'&.Mui-checked': {
|
|
36
|
-
color: '#1976d2', // blue when selected
|
|
37
|
-
},
|
|
38
|
-
'& .MuiSvgIcon-root': {
|
|
39
|
-
fontSize: 16, // controls the radio icon size
|
|
40
|
-
},
|
|
41
|
-
} }), label: jsx(Typography, { fontSize: '11px', fontWeight: 'normal !important', children: option.label }) }, i))) })) }) }), jsx(FormBottomField, { ...props })] }));
|
|
43
|
+
}, row: true, sx: RADIO_GROUP_SX, children: props.item?.settings?.map((option, i) => (jsx(FormControlLabel, { value: option.name, control: jsx(Radio, { sx: RADIO_SX }), label: jsx(Typography, { fontSize: '11px', fontWeight: 'normal !important', children: option.label }) }, i))) })) }) }), jsx(FormBottomField, { ...props })] }));
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
export { FormRadioGroup as default };
|