From 3f4737c152d2d3afdbdebf2ce83952131826bb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 30 Apr 2026 14:04:20 -0300 Subject: [PATCH 1/9] feat: add new mui formik color picker component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../formik-inputs/mui-formik-color-input.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/components/mui/formik-inputs/mui-formik-color-input.js diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js new file mode 100644 index 0000000..1ead826 --- /dev/null +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -0,0 +1,42 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; +import { TextField } from "@mui/material"; +import { useField } from "formik"; + +const MuiFormikColorInput = ({ name, ...rest }) => { + const [field, meta, helpers] = useField(name); + const [localValue, setLocalValue] = useState(field.value || "#000000"); + + const handleChange = (e) => { + setLocalValue(e.target.value); + }; + + const handleBlur = (e) => { + helpers.setValue(e.target.value); + helpers.setTouched(true); + }; + + return ( + + ); +}; + +MuiFormikColorInput.propTypes = { + name: PropTypes.string.isRequired +}; + +export default MuiFormikColorInput; From c31bbc034ddb6e7322c5ed1fc209ab30ecc39ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 30 Apr 2026 14:04:49 -0300 Subject: [PATCH 2/9] fix: add prop to fetch only initial options on async-select MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../mui/formik-inputs/mui-formik-async-select.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/mui/formik-inputs/mui-formik-async-select.js b/src/components/mui/formik-inputs/mui-formik-async-select.js index 8f724fb..55865b3 100644 --- a/src/components/mui/formik-inputs/mui-formik-async-select.js +++ b/src/components/mui/formik-inputs/mui-formik-async-select.js @@ -31,7 +31,8 @@ const MuiFormikAsyncAutocomplete = ({ formatOption = (item) => ({ value: item.id.toString(), label: item.name }), formatSelectedValue = null, queryParams = [], - isMulti = false + isMulti = false, + defaultOptions }) => { const [field, meta, helpers] = useField(name); const [options, setOptions] = useState([]); @@ -58,7 +59,7 @@ const MuiFormikAsyncAutocomplete = ({ }; useEffect(() => { - if (searchTerm) { + if (!defaultOptions && searchTerm) { const delayDebounce = setTimeout(() => { fetchOptions(searchTerm); }, DEBOUNCE_WAIT_250); @@ -99,7 +100,16 @@ const MuiFormikAsyncAutocomplete = ({ fullWidth getOptionLabel={(option) => option.label || ""} isOptionEqualToValue={(option, value) => option.value === value.value} - onInputChange={(e, newInput) => setSearchTerm(newInput)} + onInputChange={!defaultOptions ? (e, newInput) => setSearchTerm(newInput) : undefined} + filterOptions={ + // only apply filterOptions for "local" search + defaultOptions + ? (options, { inputValue }) => + options.filter((opt) => + opt.label.toLowerCase().includes(inputValue.toLowerCase()) + ) + : undefined + } renderInput={(params) => ( Date: Thu, 30 Apr 2026 15:16:26 -0300 Subject: [PATCH 3/9] fix: add entry point and export component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/components/index.js | 1 + webpack.common.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/index.js b/src/components/index.js index c6fc905..bcf680a 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -84,6 +84,7 @@ export {TotalRow as MuiTotalRow, NotesRow as MuiNotesRow} from './mui/table/extr export {default as MuiFormikAsyncSelect} from './mui/formik-inputs/mui-formik-async-select' export {default as MuiFormikCheckboxGroup} from './mui/formik-inputs/mui-formik-checkbox-group' export {default as MuiFormikCheckbox} from './mui/formik-inputs/mui-formik-checkbox' +export {default as MuiFormikColorInput} from './mui/formik-inputs/mui-formik-color-input' export {default as MuiFormikDatepicker} from './mui/formik-inputs/mui-formik-datepicker' export {default as MuiFormikDiscountField} from './mui/formik-inputs/mui-formik-discountfield' export {default as MuiFormikDropdownCheckbox} from './mui/formik-inputs/mui-formik-dropdown-checkbox' diff --git a/webpack.common.js b/webpack.common.js index 6c198ca..86ee01d 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -107,6 +107,7 @@ module.exports = { 'components/mui/formik-inputs/async-select': './src/components/mui/formik-inputs/mui-formik-async-select.js', 'components/mui/formik-inputs/checkbox-group': './src/components/mui/formik-inputs/mui-formik-checkbox-group.js', 'components/mui/formik-inputs/checkbox': './src/components/mui/formik-inputs/mui-formik-checkbox.js', + 'components/mui/formik-inputs/color-input': './src/components/mui/formik-inputs/mui-formik-color-input.js', 'components/mui/formik-inputs/datepicker': './src/components/mui/formik-inputs/mui-formik-datepicker.js', 'components/mui/formik-inputs/discount-field': './src/components/mui/formik-inputs/mui-formik-discountfield.js', 'components/mui/formik-inputs/dropdown-checkbox': './src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js', From ab8685a94d19d3c1e4014a2b5c161ba9a08a8f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 30 Apr 2026 15:26:40 -0300 Subject: [PATCH 4/9] fix: add useEffect to sync data with formik MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/components/mui/formik-inputs/mui-formik-color-input.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js index 1ead826..396d019 100644 --- a/src/components/mui/formik-inputs/mui-formik-color-input.js +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { TextField } from "@mui/material"; import { useField } from "formik"; @@ -7,6 +7,10 @@ const MuiFormikColorInput = ({ name, ...rest }) => { const [field, meta, helpers] = useField(name); const [localValue, setLocalValue] = useState(field.value || "#000000"); + useEffect(() => { + setLocalValue(field.value || "#000000"); + }, [field.value]); + const handleChange = (e) => { setLocalValue(e.target.value); }; From ae683f6a1a9affb622096029992e015f181c834c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Tue, 5 May 2026 12:48:11 -0300 Subject: [PATCH 5/9] fix: adjust prop names and proptypes, color input ref and blur adjustments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../formik-inputs/mui-formik-async-select.js | 24 +++++++++++++++---- .../formik-inputs/mui-formik-color-input.js | 17 +++++++------ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/components/mui/formik-inputs/mui-formik-async-select.js b/src/components/mui/formik-inputs/mui-formik-async-select.js index 55865b3..4db11e6 100644 --- a/src/components/mui/formik-inputs/mui-formik-async-select.js +++ b/src/components/mui/formik-inputs/mui-formik-async-select.js @@ -20,7 +20,15 @@ import { } from "@mui/material"; import { useField } from "formik"; import { DEBOUNCE_WAIT_250 } from "../../../utils/constants"; +import PropTypes from "prop-types"; +/** + * Async Autocomplete with two modes: + * - Remote (default): fetches options from API on each user input (debounced). + * - Local (localFilter=true): fetches once on mount and filters options client-side. + * Note: localFilter mode assumes stable queryParams (set once on mount). + * If queryParams need to change, remount the component instead. + */ const MuiFormikAsyncAutocomplete = ({ name, queryFunction, @@ -32,7 +40,7 @@ const MuiFormikAsyncAutocomplete = ({ formatSelectedValue = null, queryParams = [], isMulti = false, - defaultOptions + localFilter = false }) => { const [field, meta, helpers] = useField(name); const [options, setOptions] = useState([]); @@ -59,7 +67,7 @@ const MuiFormikAsyncAutocomplete = ({ }; useEffect(() => { - if (!defaultOptions && searchTerm) { + if (!localFilter && searchTerm) { const delayDebounce = setTimeout(() => { fetchOptions(searchTerm); }, DEBOUNCE_WAIT_250); @@ -100,10 +108,10 @@ const MuiFormikAsyncAutocomplete = ({ fullWidth getOptionLabel={(option) => option.label || ""} isOptionEqualToValue={(option, value) => option.value === value.value} - onInputChange={!defaultOptions ? (e, newInput) => setSearchTerm(newInput) : undefined} + onInputChange={!localFilter ? (e, newInput) => setSearchTerm(newInput) : undefined} filterOptions={ // only apply filterOptions for "local" search - defaultOptions + localFilter ? (options, { inputValue }) => options.filter((opt) => opt.label.toLowerCase().includes(inputValue.toLowerCase()) @@ -147,4 +155,12 @@ const MuiFormikAsyncAutocomplete = ({ ); }; +MuiFormikAsyncAutocomplete.propTypes = { + name: PropTypes.string.isRequired, + queryFunction: PropTypes.func.isRequired, + formatOption: PropTypes.func, + queryParams: PropTypes.array, + localFilter: PropTypes.bool, +}; + export default MuiFormikAsyncAutocomplete; diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js index 396d019..9a71ed6 100644 --- a/src/components/mui/formik-inputs/mui-formik-color-input.js +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useRef, useState } from "react"; import PropTypes from "prop-types"; import { TextField } from "@mui/material"; import { useField } from "formik"; @@ -6,23 +6,26 @@ import { useField } from "formik"; const MuiFormikColorInput = ({ name, ...rest }) => { const [field, meta, helpers] = useField(name); const [localValue, setLocalValue] = useState(field.value || "#000000"); - - useEffect(() => { - setLocalValue(field.value || "#000000"); - }, [field.value]); + const isDirtyRef = useRef(false); const handleChange = (e) => { setLocalValue(e.target.value); + isDirtyRef.current = true; }; const handleBlur = (e) => { - helpers.setValue(e.target.value); + field.onBlur(e); helpers.setTouched(true); + if (isDirtyRef.current) { + helpers.setValue(localValue); + isDirtyRef.current = false; + } }; return ( { fullWidth sx={{ "& input[type='color']::-webkit-color-swatch-wrapper": { - padding: "2px", + padding: "2px" } }} {...rest} From 5241fff5630bcd503235fd2f61823f0740348c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 6 May 2026 10:50:45 -0300 Subject: [PATCH 6/9] fix: add debounceRef to update formik value onChange MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../mui/formik-inputs/mui-formik-color-input.js | 17 ++++++++++++----- src/utils/constants.js | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js index 9a71ed6..71b7c7e 100644 --- a/src/components/mui/formik-inputs/mui-formik-color-input.js +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -2,23 +2,30 @@ import React, { useRef, useState } from "react"; import PropTypes from "prop-types"; import { TextField } from "@mui/material"; import { useField } from "formik"; +import { DEBOUNCE_WAIT_150 } from "../../../utils/constants"; const MuiFormikColorInput = ({ name, ...rest }) => { const [field, meta, helpers] = useField(name); const [localValue, setLocalValue] = useState(field.value || "#000000"); - const isDirtyRef = useRef(false); + const debounceRef = useRef(null); const handleChange = (e) => { - setLocalValue(e.target.value); - isDirtyRef.current = true; + const value = e.target.value; + setLocalValue(value); + if (debounceRef.current) clearTimeout(debounceRef.current); + debounceRef.current = setTimeout(() => { + helpers.setValue(value); + debounceRef.current = null; + }, DEBOUNCE_WAIT_150); }; const handleBlur = (e) => { field.onBlur(e); helpers.setTouched(true); - if (isDirtyRef.current) { + if (debounceRef.current) { + clearTimeout(debounceRef.current); + debounceRef.current = null; helpers.setValue(localValue); - isDirtyRef.current = false; } }; diff --git a/src/utils/constants.js b/src/utils/constants.js index a70f1a9..b0c761c 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -6,6 +6,7 @@ export const ZERO_INT = 0; export const CODE_200 = 200; +export const DEBOUNCE_WAIT_150 = 150; export const DEBOUNCE_WAIT_250 = 250; export const DEBOUNCE_WAIT = 500; From aafe69347e4b2462fe274dda7ee2a9793f90fa20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Fri, 8 May 2026 13:41:40 -0300 Subject: [PATCH 7/9] fix: address PR comments, change props and sync values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../formik-inputs/mui-formik-async-select.js | 20 ++++++++++--------- .../formik-inputs/mui-formik-color-input.js | 11 +++++++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/components/mui/formik-inputs/mui-formik-async-select.js b/src/components/mui/formik-inputs/mui-formik-async-select.js index 4db11e6..13d7218 100644 --- a/src/components/mui/formik-inputs/mui-formik-async-select.js +++ b/src/components/mui/formik-inputs/mui-formik-async-select.js @@ -32,7 +32,6 @@ import PropTypes from "prop-types"; const MuiFormikAsyncAutocomplete = ({ name, queryFunction, - multiple = false, placeholder = "Select...", plainValue = false, hiddenOptions = [], @@ -47,7 +46,7 @@ const MuiFormikAsyncAutocomplete = ({ const [loading, setLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(""); - const value = field.value || (multiple ? [] : null); + const value = field.value || (isMulti ? [] : null); const error = meta.touched && meta.error; const fetchOptions = async (input = "") => { @@ -81,7 +80,7 @@ const MuiFormikAsyncAutocomplete = ({ }, []); const handleChange = (event, selected) => { - if (!multiple) { + if (!isMulti) { const selectedValue = plainValue ? selected?.value || "" : selected; helpers.setValue(selectedValue); return; @@ -90,10 +89,10 @@ const MuiFormikAsyncAutocomplete = ({ const selectedItems = plainValue ? selected.map((s) => s.value) : selected.map((s) => - formatSelectedValue - ? formatSelectedValue(s) - : { id: parseInt(s.value), name: s.label } - ); + formatSelectedValue + ? formatSelectedValue(s) + : { id: parseInt(s.value), name: s.label } + ); helpers.setValue(selectedItems); }; @@ -114,7 +113,9 @@ const MuiFormikAsyncAutocomplete = ({ localFilter ? (options, { inputValue }) => options.filter((opt) => - opt.label.toLowerCase().includes(inputValue.toLowerCase()) + String(opt.label ?? "").toLowerCase().includes( + String(inputValue ?? "").toLowerCase() + ) ) : undefined } @@ -147,7 +148,7 @@ const MuiFormikAsyncAutocomplete = ({ )} renderOption={(props, option, { selected }) => (
  • - {multiple && } + {isMulti && } {option.label}
  • )} @@ -157,6 +158,7 @@ const MuiFormikAsyncAutocomplete = ({ MuiFormikAsyncAutocomplete.propTypes = { name: PropTypes.string.isRequired, + isMulti: PropTypes.bool, queryFunction: PropTypes.func.isRequired, formatOption: PropTypes.func, queryParams: PropTypes.array, diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js index 71b7c7e..58fa18a 100644 --- a/src/components/mui/formik-inputs/mui-formik-color-input.js +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -1,4 +1,4 @@ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import PropTypes from "prop-types"; import { TextField } from "@mui/material"; import { useField } from "formik"; @@ -9,6 +9,15 @@ const MuiFormikColorInput = ({ name, ...rest }) => { const [localValue, setLocalValue] = useState(field.value || "#000000"); const debounceRef = useRef(null); + useEffect(() => { + if (field.value !== localValue) setLocalValue(field.value || "#000000"); + }, [field.value]); + + useEffect(() => () => { + if (!field.value) helpers.setValue("#000000"); + if (debounceRef.current) clearTimeout(debounceRef.current); + }, []); + const handleChange = (e) => { const value = e.target.value; setLocalValue(value); From efc3a93f62952ebbd73c153d02921c7805e88d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 13 May 2026 01:09:42 -0300 Subject: [PATCH 8/9] fix: adjust color input, display no color selected, allow to clear selected value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../formik-inputs/mui-formik-color-input.js | 86 ++++++++++++++----- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js index 58fa18a..491b43b 100644 --- a/src/components/mui/formik-inputs/mui-formik-color-input.js +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -1,26 +1,29 @@ import React, { useEffect, useRef, useState } from "react"; import PropTypes from "prop-types"; -import { TextField } from "@mui/material"; +import { Box, IconButton, InputAdornment, TextField } from "@mui/material"; +import ClearIcon from "@mui/icons-material/Clear"; import { useField } from "formik"; import { DEBOUNCE_WAIT_150 } from "../../../utils/constants"; -const MuiFormikColorInput = ({ name, ...rest }) => { +const MuiFormikColorInput = ({ name, placeholder = "Select a color", ...rest }) => { const [field, meta, helpers] = useField(name); + const [hasValue, setHasValue] = useState(Boolean(field.value)); const [localValue, setLocalValue] = useState(field.value || "#000000"); const debounceRef = useRef(null); useEffect(() => { - if (field.value !== localValue) setLocalValue(field.value || "#000000"); + setHasValue(Boolean(field.value)); + if (field.value && field.value !== localValue) setLocalValue(field.value); }, [field.value]); useEffect(() => () => { - if (!field.value) helpers.setValue("#000000"); if (debounceRef.current) clearTimeout(debounceRef.current); }, []); const handleChange = (e) => { const value = e.target.value; setLocalValue(value); + setHasValue(true); if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => { helpers.setValue(value); @@ -34,32 +37,71 @@ const MuiFormikColorInput = ({ name, ...rest }) => { if (debounceRef.current) { clearTimeout(debounceRef.current); debounceRef.current = null; - helpers.setValue(localValue); + helpers.setValue(hasValue ? localValue : null); } }; + const handleClear = (e) => { + e.stopPropagation(); + setHasValue(false); + helpers.setValue(""); + helpers.setTouched(true); + }; + return ( - + + + + + + + ), + } : undefined} + sx={{ + "& input[type='color']::-webkit-color-swatch-wrapper": { padding: "2px" }, + }} + {...rest} + /> + {!hasValue && ( + + + {placeholder} + + + )} + ); }; MuiFormikColorInput.propTypes = { - name: PropTypes.string.isRequired + name: PropTypes.string.isRequired, + placeholder: PropTypes.string }; export default MuiFormikColorInput; From 4e85755e4ac5eda57ebe3bfa23c769d4f0c97cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 13 May 2026 01:27:16 -0300 Subject: [PATCH 9/9] fix: adjust empty value, clear debounce on handleClear function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/components/mui/formik-inputs/mui-formik-color-input.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/mui/formik-inputs/mui-formik-color-input.js b/src/components/mui/formik-inputs/mui-formik-color-input.js index 491b43b..aca9ac8 100644 --- a/src/components/mui/formik-inputs/mui-formik-color-input.js +++ b/src/components/mui/formik-inputs/mui-formik-color-input.js @@ -37,12 +37,16 @@ const MuiFormikColorInput = ({ name, placeholder = "Select a color", ...rest }) if (debounceRef.current) { clearTimeout(debounceRef.current); debounceRef.current = null; - helpers.setValue(hasValue ? localValue : null); + helpers.setValue(hasValue ? localValue : ""); } }; const handleClear = (e) => { e.stopPropagation(); + if (debounceRef.current) { + clearTimeout(debounceRef.current); + debounceRef.current = null; + } setHasValue(false); helpers.setValue(""); helpers.setTouched(true);