dinocollab-core 2.2.27 → 2.2.28
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/src/filter-bar/menu/create-form-field-select-multiple.js +1 -1
- package/dist/src/filter-bar/menu/create-form-field-select-multiple.js.map +1 -1
- package/dist/src/filter-bar/menu/create-form-field-select.js +1 -1
- package/dist/src/filter-bar/menu/create-form-field-select.js.map +1 -1
- package/dist/types/filter-bar/menu/create-form-field-select-multiple.d.ts +5 -0
- package/dist/types/filter-bar/menu/create-form-field-select.d.ts +3 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{slicedToArray as e,defineProperty as l,toConsumableArray as
|
|
1
|
+
import{slicedToArray as e,defineProperty as l,toConsumableArray as n}from"../../../_virtual/_rollupPluginBabelHelpers.js";import{jsx as i,jsxs as o}from"react/jsx-runtime";import{useMemo as a,useState as t}from"react";import{styled as r,Typography as u,FormGroup as c,FormControlLabel as s,Checkbox as d,Button as v,Box as m}from"@mui/material";import{createChipViewers as f}from"../components/chip-viewer.js";import{PopperContent as p,PopperBody as b,PopperFooter as g}from"../components/popper-custom.js";import{ButtonBack as h,ChipDark as x,FilterLogicToggle as C}from"../components/ui.units.js";import{getErrorMessage as y}from"../../form/helpers.js";function k(r){var k=f(),V=(r||{}).options,j=void 0===V?[]:V;return function(f){var V,z,S,L=a(function(){return Object.assign({},f.currentConfig,null==r?void 0:r.config)},[null==r?void 0:r.config,f.currentConfig]),w=f.value,B=void 0===w?{values:[],logic:null!==(V=null==L?void 0:L.defaultLogic)&&void 0!==V?V:"and"}:w,R=t(B.logic),T=e(R,2),F=T[0],I=T[1],M=null!==(z=null==r?void 0:r.forceLogic)&&void 0!==z?z:F,N=null!==(S=null==L?void 0:L.label)&&void 0!==S?S:L.field.toString(),O=a(function(){var e=Array.isArray(B.values)?B.values:[B.values];return j.filter(function(l){return e.includes(l.value)}).map(function(e){return e.value})},[]),P=t(O),_=e(P,2),D=_[0],E=_[1],H=t({}),U=e(H,2),q=U[0],G=U[1],J=function(e){f.onSubmit(L.field,e,L)},K=y(q,L.field),Q=a(function(){var e=Array.isArray(B.values)?B.values:[B.values];return{field:L.field,items:e.map(function(e){var l;return{value:e,label:null===(l=j.find(function(l){return l.value===e}))||void 0===l?void 0:l.label}})}},[L.field,B]),W=null!=(null==r?void 0:r.maxValueCount)&&D.length>r.maxValueCount,X=[];return f.isLoading&&X.push("disabled"),i(A,{className:X.join(" "),noValidate:!0,onSubmit:function(e){var n;if(e.preventDefault(),!W){var i=l({},L.field,D),o=null===(n=f.validator)||void 0===n?void 0:n.run(i);if(G(o||{}),!o||0===Object.keys(o).length)J({values:D,logic:M})}},children:o(p,{title:"Filter by ".concat(N),onClose:f.onClose,slots:{beforeTitle:i(h,{size:"small",onClick:f.onBack}),afterTitle:L.singleValue?i(x,{sx:{ml:1.5},size:"small",label:"Last value only"}):null!=r&&r.forceLogic||!B.values||B.values.length<2?null:i(C,{sx:{ml:1},value:M,onChange:function(e,l){return function(e){I(e);var l={values:B.values,logic:e};J(l)}(l)}})},children:[o(b,{children:[L.description&&i(u,{variant:"caption",color:"text.secondary",sx:{mb:1,display:"block"},children:L.description}),null!=(null==r?void 0:r.maxValueCount)&&i(u,{variant:"caption",color:W?"warning.main":"text.secondary",sx:{mb:.5,display:"block"},children:null!=r&&r.maxValueCount?W?"Maximum ".concat(r.maxValueCount," value").concat(r.maxValueCount>1?"s":""," selected (limit reached)"):"Up to ".concat(r.maxValueCount," value").concat(r.maxValueCount>1?"s":""," can be selected"):""}),i(k,{sx:{mb:1,borderBottom:"none!important"},label:"Applied",placement:"horizontal",enableMinimalesticView:!0,value:Q,onRemove:f.onRemove}),i(c,{className:K.error?"error":"",children:j.map(function(e,l){var o,a=D.includes(e.value),t=Q.items.some(function(l){return l.value===e.value}),u=t;return i(s,{value:e.value,disabled:t?!0===(null==r?void 0:r.disabledAfterSubmit):u,label:null!==(o=e.label)&&void 0!==o?o:e.value,control:i(d,{name:L.field.toString(),checked:a,onChange:function(l){return i=e.value,o=l.target.checked,void E(function(e){return o?[].concat(n(e),[i]):e.filter(function(e){return e!==i})});var i,o}})},e.value.toString()+l)})})]}),o(g,{children:[i(v,{size:"small",color:"error",variant:"text",disabled:!B.values||0===B.values.length,onClick:function(){var e,l;null===(e=f.onRemoveField)||void 0===e||e.call(f,L.field),!1!==(null==r||null===(l=r.config)||void 0===l?void 0:l.closeAfterClear)&&f.onClose()},children:"Clear All"}),i(m,{sx:{flex:1}}),i(v,{size:"small",color:"inherit",variant:"text",onClick:f.onClose,children:"Cancel"}),i(v,{size:"small",type:"submit",color:"primary",variant:"contained",children:"Apply"})]})]})})}}var A=r("form")({position:"relative","&::after":{content:'""',display:"block",position:"absolute",inset:0,backgroundColor:"rgba(0, 0, 0, 0.2)",filter:"blur(2px)",zIndex:-1,opacity:0,transition:"opacity 0.3s",visibility:"hidden"},"&.disabled":{pointerEvents:"none","&::after":{zIndex:1,opacity:1,visibility:"visible"}}});export{k as default};
|
|
2
2
|
//# sourceMappingURL=create-form-field-select-multiple.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-form-field-select-multiple.js","sources":["../../../../src/filter-bar/menu/create-form-field-select-multiple.tsx"],"sourcesContent":["// Copyright (c) 2024-present, Dinocollab Technologies, Inc. and its affiliates. All rights reserved.\r\n\r\n// imports\r\nimport { useMemo, useState } from 'react'\r\nimport { Box, Button, Checkbox, FormControlLabel, FormGroup, formGroupClasses, styled } from '@mui/material'\r\nimport { getErrorMessage } from '../../form'\r\nimport { createChipViewers, TChipViewerGroup } from '../components/chip-viewer'\r\nimport { PopperBody, PopperContent, PopperFooter } from '../components/popper-custom'\r\nimport { ButtonBack, ChipDark, FilterLogicToggle } from '../components/ui.units'\r\n// types\r\nimport type { FC } from 'react'\r\nimport type { IPartialError } from '../../form/validator'\r\nimport type { IFieldSelectOption } from './create-form-field-select'\r\nimport type { TFieldModelValid, TFieldValid, TFieldValue, TLogic } from '../types'\r\nimport type { IFieldMenuConfig, IFilterMenuFormProps } from './types'\r\n\r\n/** Props for the `FormFieldSelectMultiple` component returned by `createFormFieldSelectMultiple`. Extends the base filter-menu form props. */\r\nexport interface IFormFieldSelectMultipleProps<T> extends IFilterMenuFormProps<T> {}\r\n\r\n/** Parameters passed to `createFormFieldSelectMultiple` to configure the generated component. */\r\nexport interface IFormFieldSelectMultipleParam<T> {\r\n /** Optional configuration for the form field */\r\n config?: IFieldMenuConfig<T>\r\n /** List of options for the select field */\r\n options: IFieldSelectOption[]\r\n /** If true, disables the field after submission. @default false */\r\n disabledAfterSubmit?: boolean\r\n}\r\n\r\n/**\r\n * Factory function that creates a `FormFieldSelectMultiple` filter-menu component.\r\n *\r\n * The generated component renders a checkbox list of options inside a\r\n * popper/menu panel, allowing the user to select **multiple values** at once.\r\n * It supports:\r\n * - Controlled checkbox state to prevent uncontrolled→controlled React warnings\r\n * - OR / AND logic toggle when more than one value is applied\r\n * - Chip viewers showing the currently applied values\r\n * - Built-in validation via an optional `validator` prop\r\n * - A loading overlay that disables interaction while `isLoading` is true\r\n *\r\n * @param params - Static configuration (option list, optional field config override)\r\n * @returns A React FC ready to be used as a multi-select filter-menu field component\r\n */\r\nfunction createFormFieldSelectMultiple<T>(params?: IFormFieldSelectMultipleParam<T>) {\r\n const ChipViewers = createChipViewers<T>()\r\n const { options = [] } = params || {}\r\n\r\n const FormFieldSelectMultiple: FC<IFormFieldSelectMultipleProps<T>> = (props) => {\r\n /** Merge `props.currentConfig` with `params.config` (if provided).\r\n * Fields from `params.config` override the corresponding keys in `props.currentConfig`.\r\n * Any keys not present in `params.config` are preserved from `props.currentConfig`.\r\n */\r\n const mergedConfig = useMemo(() => Object.assign({}, props.currentConfig, params?.config), [params?.config, props.currentConfig])\r\n\r\n const { value = { values: [], logic: mergedConfig?.defaultLogic ?? 'and' } } = props\r\n const [filterLogic, setFilterLogic] = useState<TLogic>(value.logic!)\r\n\r\n const label = mergedConfig?.label ?? mergedConfig.field.toString()\r\n\r\n // Track checked values as controlled state to avoid the uncontrolled→controlled MUI warning\r\n const initialChecked = useMemo<TFieldValid[]>(() => {\r\n const values = Array.isArray(value.values) ? value.values : [value.values]\r\n return options.filter((opt) => values.includes(opt.value)).map((opt) => opt.value)\r\n }, [])\r\n const [checkedValues, setCheckedValues] = useState<TFieldValid[]>(initialChecked)\r\n\r\n const [errorData, setErrorData] = useState<IPartialError<TFieldModelValid<T>>>({})\r\n\r\n const handleCheckboxChange = (optionValue: TFieldValid, checked: boolean) => {\r\n setCheckedValues((prev) => (checked ? [...prev, optionValue] : prev.filter((v) => v !== optionValue)))\r\n }\r\n\r\n const handleSubmit = (newValue: TFieldValue) => {\r\n props.onSubmit(mergedConfig.field, newValue, mergedConfig)\r\n }\r\n\r\n const handleSubmitForm = (event: React.FormEvent<HTMLFormElement>) => {\r\n event.preventDefault()\r\n const obj = { [mergedConfig.field]: checkedValues } as Partial<TFieldModelValid<T>>\r\n let errorData = props.validator?.run(obj) as IPartialError<TFieldModelValid<T>>\r\n\r\n setErrorData(errorData || {})\r\n\r\n if (!errorData || Object.keys(errorData).length === 0) {\r\n const newValue: TFieldValue = { values: checkedValues, logic: filterLogic }\r\n handleSubmit(newValue)\r\n }\r\n }\r\n\r\n const errorResult = getErrorMessage(errorData, mergedConfig.field)\r\n const filterViewerValue = useMemo<TChipViewerGroup<T>>(() => {\r\n const items = Array.isArray(value.values) ? value.values : [value.values]\r\n return {\r\n field: mergedConfig.field,\r\n items: items.map((v) => ({ value: v, label: options.find((o) => o.value === v)?.label }))\r\n }\r\n }, [mergedConfig.field, value])\r\n\r\n const handleChangeLogic = (newLogic: TLogic) => {\r\n setFilterLogic(newLogic)\r\n const newValue: TFieldValue = { values: value.values, logic: newLogic }\r\n handleSubmit(newValue)\r\n }\r\n\r\n const handleClearAll = () => {\r\n props.onRemoveField?.(mergedConfig.field)\r\n if (params?.config?.closeAfterClear !== false) props.onClose()\r\n }\r\n\r\n const renderAfterTitle = () => {\r\n if (mergedConfig.singleValue) return <ChipDark sx={{ ml: 1.5 }} size='small' label='Last value only' />\r\n if (!value.values || value.values.length < 2) return null\r\n return <FilterLogicToggle sx={{ ml: 1 }} value={filterLogic} onChange={(_, nVal) => handleChangeLogic(nVal)} />\r\n }\r\n\r\n const rootClasses: string[] = []\r\n if (props.isLoading) rootClasses.push('disabled')\r\n\r\n return (\r\n <RootStyled className={rootClasses.join(' ')} noValidate onSubmit={handleSubmitForm}>\r\n <PopperContent\r\n title={`Filter by ${label}`}\r\n onClose={props.onClose}\r\n slots={{\r\n beforeTitle: <ButtonBack size='small' onClick={props.onBack} />,\r\n afterTitle: renderAfterTitle()\r\n }}\r\n >\r\n <PopperBody>\r\n <ChipViewers\r\n sx={{ mb: 1, borderBottom: 'none!important' }}\r\n label='Applied'\r\n placement='horizontal'\r\n enableMinimalesticView\r\n value={filterViewerValue}\r\n onRemove={props.onRemove}\r\n />\r\n <FormGroup className={errorResult.error ? 'error' : ''}>\r\n {options.map((x, i) => {\r\n const isChecked = checkedValues.includes(x.value as TFieldValid)\r\n const disabled = filterViewerValue.items.length > 0 && filterViewerValue.items.some((item) => item.value === x.value)\r\n return (\r\n <FormControlLabel\r\n key={x.value.toString() + i}\r\n value={x.value}\r\n disabled={disabled && params?.disabledAfterSubmit === true}\r\n label={x.label ?? x.value}\r\n control={\r\n <Checkbox\r\n name={mergedConfig.field.toString()}\r\n checked={isChecked}\r\n onChange={(e) => handleCheckboxChange(x.value as TFieldValid, e.target.checked)}\r\n />\r\n }\r\n />\r\n )\r\n })}\r\n </FormGroup>\r\n </PopperBody>\r\n <PopperFooter>\r\n <Button size='small' color='error' variant='text' disabled={!value.values || value.values.length === 0} onClick={handleClearAll}>\r\n Clear All\r\n </Button>\r\n <Box sx={{ flex: 1 }} />\r\n <Button size='small' color='inherit' variant='text' onClick={props.onClose}>\r\n Cancel\r\n </Button>\r\n <Button size='small' type='submit' color='primary' variant='contained'>\r\n Apply\r\n </Button>\r\n </PopperFooter>\r\n </PopperContent>\r\n </RootStyled>\r\n )\r\n }\r\n\r\n return FormFieldSelectMultiple\r\n}\r\n\r\nexport default createFormFieldSelectMultiple\r\n\r\nconst RootStyled = styled('form')({\r\n position: 'relative',\r\n '&::after': {\r\n content: '\"\"',\r\n display: 'block',\r\n position: 'absolute',\r\n inset: 0, // top: 0, left: 0, right: 0, bottom: 0\r\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\r\n filter: 'blur(2px)',\r\n zIndex: -1,\r\n opacity: 0,\r\n transition: 'opacity 0.3s',\r\n visibility: 'hidden'\r\n },\r\n '&.disabled': {\r\n pointerEvents: 'none',\r\n '&::after': {\r\n zIndex: 1,\r\n opacity: 1,\r\n visibility: 'visible'\r\n }\r\n }\r\n // [`.${formGroupClasses.root}`]: {}\r\n})\r\n"],"names":["createFormFieldSelectMultiple","params","ChipViewers","createChipViewers","_ref$options","options","props","_mergedConfig$default","_mergedConfig$label","mergedConfig","useMemo","Object","assign","currentConfig","config","_props$value","value","values","logic","defaultLogic","_useState","useState","_useState2","_slicedToArray","filterLogic","setFilterLogic","label","field","toString","initialChecked","Array","isArray","filter","opt","includes","map","_useState3","_useState4","checkedValues","setCheckedValues","_useState5","_useState6","errorData","setErrorData","handleSubmit","newValue","onSubmit","errorResult","getErrorMessage","filterViewerValue","items","v","_options$find","find","o","rootClasses","isLoading","push","_jsx","RootStyled","className","join","noValidate","event","_props$validator","preventDefault","obj","_defineProperty","validator","run","keys","length","children","_jsxs","PopperContent","title","concat","onClose","slots","beforeTitle","ButtonBack","size","onClick","onBack","afterTitle","singleValue","ChipDark","sx","ml","FilterLogicToggle","onChange","_","nVal","newLogic","handleChangeLogic","PopperBody","mb","borderBottom","placement","enableMinimalesticView","onRemove","FormGroup","error","x","i","_x$label","isChecked","disabled","some","item","FormControlLabel","disabledAfterSubmit","control","Checkbox","name","checked","e","optionValue","target","prev","_toConsumableArray","PopperFooter","Button","color","variant","_props$onRemoveField","_params$config","onRemoveField","call","closeAfterClear","Box","flex","type","styled","position","content","display","inset","backgroundColor","zIndex","opacity","transition","visibility","pointerEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-form-field-select-multiple.js","sources":["../../../../src/filter-bar/menu/create-form-field-select-multiple.tsx"],"sourcesContent":["// Copyright (c) 2024-present, Dinocollab Technologies, Inc. and its affiliates. All rights reserved.\r\n\r\n// imports\r\nimport { useMemo, useState } from 'react'\r\nimport { Box, Button, Checkbox, FormControlLabel, FormGroup, formGroupClasses, styled, Typography } from '@mui/material'\r\nimport { getErrorMessage } from '../../form'\r\nimport { createChipViewers, TChipViewerGroup } from '../components/chip-viewer'\r\nimport { PopperBody, PopperContent, PopperFooter } from '../components/popper-custom'\r\nimport { ButtonBack, ChipDark, FilterLogicToggle } from '../components/ui.units'\r\n// types\r\nimport type { FC } from 'react'\r\nimport type { IPartialError } from '../../form/validator'\r\nimport type { IFieldSelectOption } from './create-form-field-select'\r\nimport type { TFieldModelValid, TFieldValid, TFieldValue, TLogic } from '../types'\r\nimport type { IFieldMenuConfig, IFilterMenuFormProps } from './types'\r\n\r\n/** Props for the `FormFieldSelectMultiple` component returned by `createFormFieldSelectMultiple`. Extends the base filter-menu form props. */\r\nexport interface IFormFieldSelectMultipleProps<T> extends IFilterMenuFormProps<T> {}\r\n\r\n/** Parameters passed to `createFormFieldSelectMultiple` to configure the generated component. */\r\nexport interface IFormFieldSelectMultipleParam<T> {\r\n /** Optional configuration for the form field */\r\n config?: IFieldMenuConfig<T>\r\n /** List of options for the select field */\r\n options: IFieldSelectOption[]\r\n /** If true, disables the field after submission. @default false */\r\n disabledAfterSubmit?: boolean\r\n /** Force a fixed logic value, hiding the logic toggle */\r\n forceLogic?: TLogic\r\n /** Maximum number of values that can be selected */\r\n maxValueCount?: number\r\n}\r\n\r\n/**\r\n * Factory function that creates a `FormFieldSelectMultiple` filter-menu component.\r\n *\r\n * The generated component renders a checkbox list of options inside a\r\n * popper/menu panel, allowing the user to select **multiple values** at once.\r\n * It supports:\r\n * - Controlled checkbox state to prevent uncontrolled→controlled React warnings\r\n * - OR / AND logic toggle when more than one value is applied\r\n * - Chip viewers showing the currently applied values\r\n * - Built-in validation via an optional `validator` prop\r\n * - A loading overlay that disables interaction while `isLoading` is true\r\n *\r\n * @param params - Static configuration (option list, optional field config override)\r\n * @returns A React FC ready to be used as a multi-select filter-menu field component\r\n */\r\nfunction createFormFieldSelectMultiple<T>(params?: IFormFieldSelectMultipleParam<T>) {\r\n const ChipViewers = createChipViewers<T>()\r\n const { options = [] } = params || {}\r\n\r\n const FormFieldSelectMultiple: FC<IFormFieldSelectMultipleProps<T>> = (props) => {\r\n /** Merge `props.currentConfig` with `params.config` (if provided).\r\n * Fields from `params.config` override the corresponding keys in `props.currentConfig`.\r\n * Any keys not present in `params.config` are preserved from `props.currentConfig`.\r\n */\r\n const mergedConfig = useMemo(() => Object.assign({}, props.currentConfig, params?.config), [params?.config, props.currentConfig])\r\n\r\n const { value = { values: [], logic: mergedConfig?.defaultLogic ?? 'and' } } = props\r\n const [filterLogic, setFilterLogic] = useState<TLogic>(value.logic!)\r\n const effectiveLogic = params?.forceLogic ?? filterLogic\r\n\r\n const label = mergedConfig?.label ?? mergedConfig.field.toString()\r\n\r\n // Track checked values as controlled state to avoid the uncontrolled→controlled MUI warning\r\n const initialChecked = useMemo<TFieldValid[]>(() => {\r\n const values = Array.isArray(value.values) ? value.values : [value.values]\r\n return options.filter((opt) => values.includes(opt.value)).map((opt) => opt.value)\r\n }, [])\r\n const [checkedValues, setCheckedValues] = useState<TFieldValid[]>(initialChecked)\r\n\r\n const [errorData, setErrorData] = useState<IPartialError<TFieldModelValid<T>>>({})\r\n\r\n const handleCheckboxChange = (optionValue: TFieldValid, checked: boolean) => {\r\n setCheckedValues((prev) => (checked ? [...prev, optionValue] : prev.filter((v) => v !== optionValue)))\r\n }\r\n\r\n const handleSubmit = (newValue: TFieldValue) => {\r\n props.onSubmit(mergedConfig.field, newValue, mergedConfig)\r\n }\r\n\r\n const handleSubmitForm = (event: React.FormEvent<HTMLFormElement>) => {\r\n event.preventDefault()\r\n if (isMaxReached) return\r\n const obj = { [mergedConfig.field]: checkedValues } as Partial<TFieldModelValid<T>>\r\n let errorData = props.validator?.run(obj) as IPartialError<TFieldModelValid<T>>\r\n\r\n setErrorData(errorData || {})\r\n\r\n if (!errorData || Object.keys(errorData).length === 0) {\r\n const newValue: TFieldValue = { values: checkedValues, logic: effectiveLogic }\r\n handleSubmit(newValue)\r\n }\r\n }\r\n\r\n const errorResult = getErrorMessage(errorData, mergedConfig.field)\r\n const filterViewerValue = useMemo<TChipViewerGroup<T>>(() => {\r\n const items = Array.isArray(value.values) ? value.values : [value.values]\r\n return {\r\n field: mergedConfig.field,\r\n items: items.map((v) => ({ value: v, label: options.find((o) => o.value === v)?.label }))\r\n }\r\n }, [mergedConfig.field, value])\r\n\r\n const isMaxReached = params?.maxValueCount != null && checkedValues.length > params.maxValueCount\r\n\r\n const handleChangeLogic = (newLogic: TLogic) => {\r\n setFilterLogic(newLogic)\r\n const newValue: TFieldValue = { values: value.values, logic: newLogic }\r\n handleSubmit(newValue)\r\n }\r\n\r\n const handleClearAll = () => {\r\n props.onRemoveField?.(mergedConfig.field)\r\n if (params?.config?.closeAfterClear !== false) props.onClose()\r\n }\r\n\r\n const getMaxReachedText = () => {\r\n if (!params?.maxValueCount) return ''\r\n if (isMaxReached) {\r\n return `Maximum ${params.maxValueCount} value${params.maxValueCount > 1 ? 's' : ''} selected (limit reached)`\r\n } else {\r\n return `Up to ${params.maxValueCount} value${params.maxValueCount > 1 ? 's' : ''} can be selected`\r\n }\r\n }\r\n\r\n const renderAfterTitle = () => {\r\n if (mergedConfig.singleValue) return <ChipDark sx={{ ml: 1.5 }} size='small' label='Last value only' />\r\n if (params?.forceLogic || !value.values || value.values.length < 2) return null\r\n return <FilterLogicToggle sx={{ ml: 1 }} value={effectiveLogic} onChange={(_, nVal) => handleChangeLogic(nVal)} />\r\n }\r\n\r\n const rootClasses: string[] = []\r\n if (props.isLoading) rootClasses.push('disabled')\r\n\r\n return (\r\n <RootStyled className={rootClasses.join(' ')} noValidate onSubmit={handleSubmitForm}>\r\n <PopperContent\r\n title={`Filter by ${label}`}\r\n onClose={props.onClose}\r\n slots={{\r\n beforeTitle: <ButtonBack size='small' onClick={props.onBack} />,\r\n afterTitle: renderAfterTitle()\r\n }}\r\n >\r\n <PopperBody>\r\n {mergedConfig.description && (\r\n <Typography variant='caption' color='text.secondary' sx={{ mb: 1, display: 'block' }}>\r\n {mergedConfig.description}\r\n </Typography>\r\n )}\r\n {params?.maxValueCount != null && (\r\n <Typography variant='caption' color={isMaxReached ? 'warning.main' : 'text.secondary'} sx={{ mb: 0.5, display: 'block' }}>\r\n {getMaxReachedText()}\r\n </Typography>\r\n )}\r\n <ChipViewers\r\n sx={{ mb: 1, borderBottom: 'none!important' }}\r\n label='Applied'\r\n placement='horizontal'\r\n enableMinimalesticView\r\n value={filterViewerValue}\r\n onRemove={props.onRemove}\r\n />\r\n <FormGroup className={errorResult.error ? 'error' : ''}>\r\n {options.map((x, i) => {\r\n const isChecked = checkedValues.includes(x.value as TFieldValid)\r\n const isSelected = filterViewerValue.items.some((item) => item.value === x.value)\r\n const disabled = isSelected\r\n return (\r\n <FormControlLabel\r\n key={x.value.toString() + i}\r\n value={x.value}\r\n disabled={isSelected ? params?.disabledAfterSubmit === true : disabled}\r\n label={x.label ?? x.value}\r\n control={\r\n <Checkbox\r\n name={mergedConfig.field.toString()}\r\n checked={isChecked}\r\n onChange={(e) => handleCheckboxChange(x.value as TFieldValid, e.target.checked)}\r\n />\r\n }\r\n />\r\n )\r\n })}\r\n </FormGroup>\r\n </PopperBody>\r\n <PopperFooter>\r\n <Button size='small' color='error' variant='text' disabled={!value.values || value.values.length === 0} onClick={handleClearAll}>\r\n Clear All\r\n </Button>\r\n <Box sx={{ flex: 1 }} />\r\n <Button size='small' color='inherit' variant='text' onClick={props.onClose}>\r\n Cancel\r\n </Button>\r\n <Button size='small' type='submit' color='primary' variant='contained'>\r\n Apply\r\n </Button>\r\n </PopperFooter>\r\n </PopperContent>\r\n </RootStyled>\r\n )\r\n }\r\n\r\n return FormFieldSelectMultiple\r\n}\r\n\r\nexport default createFormFieldSelectMultiple\r\n\r\nconst RootStyled = styled('form')({\r\n position: 'relative',\r\n '&::after': {\r\n content: '\"\"',\r\n display: 'block',\r\n position: 'absolute',\r\n inset: 0, // top: 0, left: 0, right: 0, bottom: 0\r\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\r\n filter: 'blur(2px)',\r\n zIndex: -1,\r\n opacity: 0,\r\n transition: 'opacity 0.3s',\r\n visibility: 'hidden'\r\n },\r\n '&.disabled': {\r\n pointerEvents: 'none',\r\n '&::after': {\r\n zIndex: 1,\r\n opacity: 1,\r\n visibility: 'visible'\r\n }\r\n }\r\n // [`.${formGroupClasses.root}`]: {}\r\n})\r\n"],"names":["createFormFieldSelectMultiple","params","ChipViewers","createChipViewers","_ref$options","options","props","_mergedConfig$default","_params$forceLogic","_mergedConfig$label","mergedConfig","useMemo","Object","assign","currentConfig","config","_props$value","value","values","logic","defaultLogic","_useState","useState","_useState2","_slicedToArray","filterLogic","setFilterLogic","effectiveLogic","forceLogic","label","field","toString","initialChecked","Array","isArray","filter","opt","includes","map","_useState3","_useState4","checkedValues","setCheckedValues","_useState5","_useState6","errorData","setErrorData","handleSubmit","newValue","onSubmit","errorResult","getErrorMessage","filterViewerValue","items","v","_options$find","find","o","isMaxReached","maxValueCount","length","rootClasses","isLoading","push","_jsx","RootStyled","className","join","noValidate","event","_props$validator","preventDefault","obj","_defineProperty","validator","run","keys","children","_jsxs","PopperContent","title","concat","onClose","slots","beforeTitle","ButtonBack","size","onClick","onBack","afterTitle","singleValue","ChipDark","sx","ml","FilterLogicToggle","onChange","_","nVal","newLogic","handleChangeLogic","PopperBody","description","Typography","variant","color","mb","display","borderBottom","placement","enableMinimalesticView","onRemove","FormGroup","error","x","i","_x$label","isChecked","isSelected","some","item","disabled","FormControlLabel","disabledAfterSubmit","control","Checkbox","name","checked","e","optionValue","target","prev","_toConsumableArray","PopperFooter","Button","_props$onRemoveField","_params$config","onRemoveField","call","closeAfterClear","Box","flex","type","styled","position","content","inset","backgroundColor","zIndex","opacity","transition","visibility","pointerEvents"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{defineProperty as e,slicedToArray as l}from"../../../_virtual/_rollupPluginBabelHelpers.js";import{jsx as o,jsxs as i}from"react/jsx-runtime";import{useMemo as
|
|
1
|
+
import{defineProperty as e,slicedToArray as l}from"../../../_virtual/_rollupPluginBabelHelpers.js";import{jsx as o,jsxs as i}from"react/jsx-runtime";import{useMemo as n,useState as r}from"react";import{styled as a,radioGroupClasses as t,Typography as u,RadioGroup as c,FormControlLabel as s,Radio as d,Button as m,Box as v}from"@mui/material";import{getErrorMessage as f,convertFormDataToJson as p}from"../../form/helpers.js";import{createChipViewers as b}from"../components/chip-viewer.js";import{ButtonBack as g,ChipDark as x,FilterLogicToggle as h}from"../components/ui.units.js";import{PopperContent as C,PopperBody as y,PopperFooter as V}from"../components/popper-custom.js";function k(e){var a=b(),t=e.options;return function(b){var k,z,A,L=n(function(){return Object.assign({},b.currentConfig,null==e?void 0:e.config)},[null==e?void 0:e.config,b.currentConfig]),F=b.value,M=void 0===F?{values:[],logic:null!==(k=null==L?void 0:L.defaultLogic)&&void 0!==k?k:"and"}:F,S=r(M.logic),w=l(S,2),R=w[0],T=w[1],B=null!==(z=e.forceLogic)&&void 0!==z?z:R,D=null!==(A=null==L?void 0:L.label)&&void 0!==A?A:L.field.toString(),I=r({}),N=l(I,2),O=N[0],P=N[1],_=function(e){b.onSubmit(L.field,e,L)},E=f(O,L.field),H=n(function(){var e=Array.isArray(M.values)?M.values:[M.values];return{field:L.field,items:e.map(function(e){var l;return{value:e,label:null===(l=t.find(function(l){return l.value===e}))||void 0===l?void 0:l.label}})}},[L.field,M]),U=null!=e.maxValueCount&&H.items.length>=e.maxValueCount,q=[];return b.isLoading&&q.push("disabled"),o(j,{className:q.join(" "),noValidate:!0,onSubmit:function(e){var l;if(e.preventDefault(),!U){var o=new FormData(e.currentTarget),i=p(o),n=null===(l=b.validator)||void 0===l?void 0:l.run(i);if(P(n||{}),!n||0===Object.keys(n).length){var r=L.field,a=Array.isArray(i[r])?i[r]:[i[r]];_({values:a,logic:B})}}},children:i(C,{title:"Filter by ".concat(D),onClose:b.onClose,slots:{beforeTitle:o(g,{size:"small",onClick:b.onBack}),afterTitle:L.singleValue?o(x,{sx:{ml:1.5},size:"small",label:"Last value only"}):e.forceLogic||!M.values||M.values.length<2?null:o(h,{sx:{ml:1},value:B,onChange:function(e,l){return function(e){T(e);var l={values:M.values,logic:e};_(l)}(l)}})},children:[i(y,{children:[L.description&&o(u,{variant:"caption",color:"text.secondary",sx:{mb:1,display:"block"},children:L.description}),null!=e.maxValueCount&&o(u,{variant:"caption",color:U?"warning.main":"text.secondary",sx:{mb:.5,display:"block"},children:e.maxValueCount?U?"Maximum ".concat(e.maxValueCount," value").concat(e.maxValueCount>1?"s":""," selected (limit reached)"):"Up to ".concat(e.maxValueCount," value").concat(e.maxValueCount>1?"s":""," can be selected"):""}),o(a,{sx:{mb:1,borderBottom:"none!important"},label:"Applied",placement:"horizontal",enableMinimalesticView:!0,value:H,onRemove:b.onRemove}),o(c,{sx:{mx:-1},name:L.field.toString(),className:E.error?"error":"",children:t.map(function(e,l){var i,n=H.items.some(function(l){return l.value===e.value});return o(s,{disabled:n||U,value:e.value,control:o(d,{size:"small"}),label:o(u,{variant:"body2",children:null!==(i=e.label)&&void 0!==i?i:e.value})},e.value.toString()+l)})}),E.error&&o(u,{variant:"caption",color:"error",sx:{mt:.5},children:E.message})]}),i(V,{children:[o(m,{size:"small",color:"error",variant:"text",disabled:!M.values||0===M.values.length,onClick:function(){var l,o;null===(l=b.onRemoveField)||void 0===l||l.call(b,L.field),!1!==(null==e||null===(o=e.config)||void 0===o?void 0:o.closeAfterClear)&&b.onClose()},children:"Clear All"}),o(v,{sx:{flex:1}}),o(m,{size:"small",color:"inherit",variant:"text",onClick:b.onClose,children:"Cancel"}),o(m,{size:"small",type:"submit",color:"primary",variant:"contained",children:"Apply"})]})]})})}}var j=a("form")(e({position:"relative","&::after":{content:'""',display:"block",position:"absolute",inset:0,backgroundColor:"rgba(0, 0, 0, 0.2)",filter:"blur(2px)",zIndex:-1,opacity:0,transition:"opacity 0.3s",visibility:"hidden"},"&.disabled":{pointerEvents:"none","&::after":{zIndex:1,opacity:1,visibility:"visible"}}},".".concat(t.root),{"&.error .MuiRadio-root":{color:"#d32f2f"},".MuiFormControlLabel-root":{margin:0},".MuiFormControlLabel-root:hover":{backgroundColor:"rgba(25, 118, 210, 0.04)"}}));export{k as default};
|
|
2
2
|
//# sourceMappingURL=create-form-field-select.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-form-field-select.js","sources":["../../../../src/filter-bar/menu/create-form-field-select.tsx"],"sourcesContent":["import { FC, useMemo, useState } from 'react'\r\nimport { Box, Button, FormControlLabel, Radio, RadioGroup, radioGroupClasses, styled, Typography } from '@mui/material'\r\nimport { convertFormDataToJson, getErrorMessage } from '../../form/helpers'\r\nimport { createChipViewers, TChipViewerGroup } from '../components/chip-viewer'\r\nimport { ButtonBack, ChipDark, FilterLogicToggle } from '../components/ui.units'\r\nimport { PopperBody, PopperContent, PopperFooter } from '../components/popper-custom'\r\nimport type { IPartialError } from '../../form/validator'\r\nimport type { IFieldMenuConfig, IFilterMenuFormProps } from './types'\r\nimport type { TFieldModelValid, TFieldValid, TFieldValue, TLogic } from '../types'\r\n\r\n/** Props for the `FormFieldSelect` component returned by `createFormFieldSelect`. Extends the base filter-menu form props. */\r\nexport interface IFormFieldSelectProps<T> extends IFilterMenuFormProps<T> {}\r\n\r\n/** A single option item rendered as a radio button inside the select filter menu. */\r\nexport interface IFieldSelectOption {\r\n value: TFieldValid\r\n label?: string\r\n}\r\n\r\n/** Parameters passed to `createFormFieldSelect` to configure the generated component. */\r\nexport interface IFormFieldSelectParams<T> {\r\n /** Optional configuration for the form field */\r\n config?: IFieldMenuConfig<T>\r\n /** List of options for the select field */\r\n options: IFieldSelectOption[]\r\n}\r\n\r\n/**\r\n * Factory function that creates a `FormFieldSelect` filter-menu component.\r\n *\r\n * The generated component renders a radio-button list of options inside a\r\n * popper/menu panel. It supports:\r\n * - Single or multi-value selection (controlled by `config.singleValue`)\r\n * - OR / AND logic toggle when more than one value is selected\r\n * - Chip viewers showing the currently applied values\r\n * - Built-in validation via an optional `validator` prop\r\n * - A loading overlay that disables interaction while `isLoading` is true\r\n *\r\n * @param params - Static configuration (option list, optional field config override)\r\n * @returns A React FC ready to be used as a filter-menu field component\r\n */\r\nfunction createFormFieldSelect<T>(params: IFormFieldSelectParams<T>) {\r\n const ChipViewers = createChipViewers<T>()\r\n const { options } = params\r\n\r\n const FormFieldSelect: FC<IFormFieldSelectProps<T>> = (props) => {\r\n /** Merge `props.currentConfig` with `params.config` (if provided).\r\n * Fields from `params.config` override the corresponding keys in `props.currentConfig`.\r\n * Any keys not present in `params.config` are preserved from `props.currentConfig`.\r\n */\r\n const mergedConfig = useMemo(() => Object.assign({}, props.currentConfig, params?.config), [params?.config, props.currentConfig])\r\n\r\n const { value = { values: [], logic: mergedConfig?.defaultLogic ?? 'and' } } = props\r\n const [filterLogic, setFilterLogic] = useState<TLogic>(value.logic!)\r\n\r\n const label = mergedConfig?.label ?? mergedConfig.field.toString()\r\n\r\n const [errorData, setErrorData] = useState<IPartialError<TFieldModelValid<T>>>({})\r\n const handleSubmit = (newValue: TFieldValue) => {\r\n props.onSubmit(mergedConfig.field, newValue, mergedConfig)\r\n }\r\n\r\n const handleSubmitForm = (event: React.FormEvent<HTMLFormElement>) => {\r\n event.preventDefault()\r\n const formData = new FormData(event.currentTarget)\r\n const obj = convertFormDataToJson<TFieldModelValid<T>>(formData)\r\n let errorData = props.validator?.run(obj) as IPartialError<TFieldModelValid<T>>\r\n\r\n setErrorData(errorData || {})\r\n\r\n if (!errorData || Object.keys(errorData).length === 0) {\r\n const { field } = mergedConfig\r\n const newValues = (Array.isArray(obj[field]) ? obj[field] : [obj[field]]) as TFieldValid[]\r\n const newValue: TFieldValue = { values: newValues, logic: filterLogic }\r\n handleSubmit(newValue)\r\n }\r\n }\r\n\r\n const errorResult = getErrorMessage(errorData, mergedConfig.field)\r\n const filterViewerValue = useMemo<TChipViewerGroup<T>>(() => {\r\n const items = Array.isArray(value.values) ? value.values : [value.values]\r\n return {\r\n field: mergedConfig.field,\r\n items: items.map((v) => ({ value: v, label: options.find((o) => o.value === v)?.label }))\r\n }\r\n }, [mergedConfig.field, value])\r\n\r\n const handleChangeLogic = (newLogic: TLogic) => {\r\n setFilterLogic(newLogic)\r\n const newValue: TFieldValue = { values: value.values, logic: newLogic }\r\n handleSubmit(newValue)\r\n }\r\n\r\n const handleClearAll = () => {\r\n props.onRemoveField?.(mergedConfig.field)\r\n if (params?.config?.closeAfterClear !== false) props.onClose()\r\n }\r\n\r\n const renderAfterTitle = () => {\r\n if (mergedConfig.singleValue) return <ChipDark sx={{ ml: 1.5 }} size='small' label='Last value only' />\r\n if (!value.values || value.values.length < 2) return null\r\n return <FilterLogicToggle sx={{ ml: 1 }} value={filterLogic} onChange={(_, nVal) => handleChangeLogic(nVal)} />\r\n }\r\n\r\n const rootClasses: string[] = []\r\n if (props.isLoading) rootClasses.push('disabled')\r\n\r\n return (\r\n <RootStyled className={rootClasses.join(' ')} noValidate onSubmit={handleSubmitForm}>\r\n <PopperContent\r\n title={`Filter by ${label}`}\r\n onClose={props.onClose}\r\n slots={{\r\n beforeTitle: <ButtonBack size='small' onClick={props.onBack} />,\r\n afterTitle: renderAfterTitle()\r\n }}\r\n >\r\n <PopperBody>\r\n <ChipViewers\r\n sx={{ mb: 1, borderBottom: 'none!important' }}\r\n label='Applied'\r\n placement='horizontal'\r\n enableMinimalesticView\r\n value={filterViewerValue}\r\n onRemove={props.onRemove}\r\n />\r\n <RadioGroup sx={{ mx: -1 }} name={mergedConfig.field.toString()} className={errorResult.error ? 'error' : ''}>\r\n {options.map((x, i) => {\r\n const disabled = filterViewerValue.items.length > 0 && filterViewerValue.items.some((item) => item.value === x.value)\r\n return (\r\n <FormControlLabel\r\n disabled={disabled}\r\n key={x.value.toString() + i}\r\n value={x.value}\r\n control={<Radio size='small' />}\r\n label={<Typography variant='body2'>{x.label ?? x.value}</Typography>}\r\n />\r\n )\r\n })}\r\n </RadioGroup>\r\n {errorResult.error && (\r\n <Typography variant='caption' color='error' sx={{ mt: 0.5 }}>\r\n {errorResult.message}\r\n </Typography>\r\n )}\r\n </PopperBody>\r\n <PopperFooter>\r\n <Button size='small' color='error' variant='text' disabled={!value.values || value.values.length === 0} onClick={handleClearAll}>\r\n Clear All\r\n </Button>\r\n <Box sx={{ flex: 1 }} />\r\n <Button size='small' color='inherit' variant='text' onClick={props.onClose}>\r\n Cancel\r\n </Button>\r\n <Button size='small' type='submit' color='primary' variant='contained'>\r\n Apply\r\n </Button>\r\n </PopperFooter>\r\n </PopperContent>\r\n </RootStyled>\r\n )\r\n }\r\n\r\n return FormFieldSelect\r\n}\r\n\r\nexport default createFormFieldSelect\r\n\r\nconst RootStyled = styled('form')({\r\n position: 'relative',\r\n '&::after': {\r\n content: '\"\"',\r\n display: 'block',\r\n position: 'absolute',\r\n inset: 0, // top: 0, left: 0, right: 0, bottom: 0\r\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\r\n filter: 'blur(2px)',\r\n zIndex: -1,\r\n opacity: 0,\r\n transition: 'opacity 0.3s',\r\n visibility: 'hidden'\r\n },\r\n '&.disabled': {\r\n pointerEvents: 'none',\r\n '&::after': {\r\n zIndex: 1,\r\n opacity: 1,\r\n visibility: 'visible'\r\n }\r\n },\r\n [`.${radioGroupClasses.root}`]: {\r\n '&.error .MuiRadio-root': { color: '#d32f2f' },\r\n '.MuiFormControlLabel-root': { margin: 0 },\r\n '.MuiFormControlLabel-root:hover': { backgroundColor: 'rgba(25, 118, 210, 0.04)' }\r\n }\r\n})\r\n"],"names":["createFormFieldSelect","params","ChipViewers","createChipViewers","options","props","_mergedConfig$default","_mergedConfig$label","mergedConfig","useMemo","Object","assign","currentConfig","config","_props$value","value","values","logic","defaultLogic","_useState","useState","_useState2","_slicedToArray","filterLogic","setFilterLogic","label","field","toString","_useState3","_useState4","errorData","setErrorData","handleSubmit","newValue","onSubmit","errorResult","getErrorMessage","filterViewerValue","items","Array","isArray","map","v","_options$find","find","o","rootClasses","isLoading","push","_jsx","RootStyled","className","join","noValidate","event","_props$validator","preventDefault","formData","FormData","currentTarget","obj","convertFormDataToJson","validator","run","keys","length","newValues","children","_jsxs","PopperContent","title","concat","onClose","slots","beforeTitle","ButtonBack","size","onClick","onBack","afterTitle","singleValue","ChipDark","sx","ml","FilterLogicToggle","onChange","_","nVal","newLogic","handleChangeLogic","PopperBody","mb","borderBottom","placement","enableMinimalesticView","onRemove","RadioGroup","mx","name","error","x","i","_x$label","disabled","some","item","FormControlLabel","control","Radio","Typography","variant","color","mt","message","PopperFooter","Button","_props$onRemoveField","_params$config","onRemoveField","call","closeAfterClear","Box","flex","type","styled","_defineProperty","position","content","display","inset","backgroundColor","filter","zIndex","opacity","transition","visibility","pointerEvents","radioGroupClasses","root","margin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-form-field-select.js","sources":["../../../../src/filter-bar/menu/create-form-field-select.tsx"],"sourcesContent":["import { FC, useMemo, useState } from 'react'\r\nimport { Box, Button, FormControlLabel, Radio, RadioGroup, radioGroupClasses, styled, Typography } from '@mui/material'\r\nimport { convertFormDataToJson, getErrorMessage } from '../../form/helpers'\r\nimport { createChipViewers, TChipViewerGroup } from '../components/chip-viewer'\r\nimport { ButtonBack, ChipDark, FilterLogicToggle } from '../components/ui.units'\r\nimport { PopperBody, PopperContent, PopperFooter } from '../components/popper-custom'\r\nimport type { IPartialError } from '../../form/validator'\r\nimport type { IFieldMenuConfig, IFilterMenuFormProps } from './types'\r\nimport type { TFieldModelValid, TFieldValid, TFieldValue, TLogic } from '../types'\r\n\r\n/** Props for the `FormFieldSelect` component returned by `createFormFieldSelect`. Extends the base filter-menu form props. */\r\nexport interface IFormFieldSelectProps<T> extends IFilterMenuFormProps<T> {}\r\n\r\n/** A single option item rendered as a radio button inside the select filter menu. */\r\nexport interface IFieldSelectOption {\r\n value: TFieldValid\r\n label?: string\r\n}\r\n\r\n/** Parameters passed to `createFormFieldSelect` to configure the generated component. */\r\nexport interface IFormFieldSelectParams<T> {\r\n /** Optional configuration for the form field */\r\n config?: IFieldMenuConfig<T>\r\n /** List of options for the select field */\r\n options: IFieldSelectOption[]\r\n forceLogic?: TLogic\r\n maxValueCount?: number\r\n}\r\n\r\n/**\r\n * Factory function that creates a `FormFieldSelect` filter-menu component.\r\n *\r\n * The generated component renders a radio-button list of options inside a\r\n * popper/menu panel. It supports:\r\n * - Single or multi-value selection (controlled by `config.singleValue`)\r\n * - OR / AND logic toggle when more than one value is selected\r\n * - Chip viewers showing the currently applied values\r\n * - Built-in validation via an optional `validator` prop\r\n * - A loading overlay that disables interaction while `isLoading` is true\r\n *\r\n * @param params - Static configuration (option list, optional field config override)\r\n * @returns A React FC ready to be used as a filter-menu field component\r\n */\r\nfunction createFormFieldSelect<T>(params: IFormFieldSelectParams<T>) {\r\n const ChipViewers = createChipViewers<T>()\r\n const { options } = params\r\n\r\n const FormFieldSelect: FC<IFormFieldSelectProps<T>> = (props) => {\r\n /** Merge `props.currentConfig` with `params.config` (if provided).\r\n * Fields from `params.config` override the corresponding keys in `props.currentConfig`.\r\n * Any keys not present in `params.config` are preserved from `props.currentConfig`.\r\n */\r\n const mergedConfig = useMemo(() => Object.assign({}, props.currentConfig, params?.config), [params?.config, props.currentConfig])\r\n\r\n const { value = { values: [], logic: mergedConfig?.defaultLogic ?? 'and' } } = props\r\n const [filterLogic, setFilterLogic] = useState<TLogic>(value.logic!)\r\n const effectiveLogic = params.forceLogic ?? filterLogic\r\n\r\n const label = mergedConfig?.label ?? mergedConfig.field.toString()\r\n\r\n const [errorData, setErrorData] = useState<IPartialError<TFieldModelValid<T>>>({})\r\n const handleSubmit = (newValue: TFieldValue) => {\r\n props.onSubmit(mergedConfig.field, newValue, mergedConfig)\r\n }\r\n\r\n const handleSubmitForm = (event: React.FormEvent<HTMLFormElement>) => {\r\n event.preventDefault()\r\n if (isMaxReached) return\r\n const formData = new FormData(event.currentTarget)\r\n const obj = convertFormDataToJson<TFieldModelValid<T>>(formData)\r\n let errorData = props.validator?.run(obj) as IPartialError<TFieldModelValid<T>>\r\n\r\n setErrorData(errorData || {})\r\n\r\n if (!errorData || Object.keys(errorData).length === 0) {\r\n const { field } = mergedConfig\r\n const newValues = (Array.isArray(obj[field]) ? obj[field] : [obj[field]]) as TFieldValid[]\r\n const newValue: TFieldValue = { values: newValues, logic: effectiveLogic }\r\n handleSubmit(newValue)\r\n }\r\n }\r\n\r\n const errorResult = getErrorMessage(errorData, mergedConfig.field)\r\n const filterViewerValue = useMemo<TChipViewerGroup<T>>(() => {\r\n const items = Array.isArray(value.values) ? value.values : [value.values]\r\n return {\r\n field: mergedConfig.field,\r\n items: items.map((v) => ({ value: v, label: options.find((o) => o.value === v)?.label }))\r\n }\r\n }, [mergedConfig.field, value])\r\n\r\n const isMaxReached = params.maxValueCount != null && filterViewerValue.items.length >= params.maxValueCount\r\n\r\n const handleChangeLogic = (newLogic: TLogic) => {\r\n setFilterLogic(newLogic)\r\n const newValue: TFieldValue = { values: value.values, logic: newLogic }\r\n handleSubmit(newValue)\r\n }\r\n\r\n const handleClearAll = () => {\r\n props.onRemoveField?.(mergedConfig.field)\r\n if (params?.config?.closeAfterClear !== false) props.onClose()\r\n }\r\n\r\n const renderAfterTitle = () => {\r\n if (mergedConfig.singleValue) return <ChipDark sx={{ ml: 1.5 }} size='small' label='Last value only' />\r\n if (params.forceLogic || !value.values || value.values.length < 2) return null\r\n return <FilterLogicToggle sx={{ ml: 1 }} value={effectiveLogic} onChange={(_, nVal) => handleChangeLogic(nVal)} />\r\n }\r\n\r\n const getMaxReachedText = () => {\r\n if (!params.maxValueCount) return ''\r\n if (isMaxReached) {\r\n return `Maximum ${params.maxValueCount} value${params.maxValueCount > 1 ? 's' : ''} selected (limit reached)`\r\n } else {\r\n return `Up to ${params.maxValueCount} value${params.maxValueCount > 1 ? 's' : ''} can be selected`\r\n }\r\n }\r\n\r\n const rootClasses: string[] = []\r\n if (props.isLoading) rootClasses.push('disabled')\r\n\r\n return (\r\n <RootStyled className={rootClasses.join(' ')} noValidate onSubmit={handleSubmitForm}>\r\n <PopperContent\r\n title={`Filter by ${label}`}\r\n onClose={props.onClose}\r\n slots={{\r\n beforeTitle: <ButtonBack size='small' onClick={props.onBack} />,\r\n afterTitle: renderAfterTitle()\r\n }}\r\n >\r\n <PopperBody>\r\n {mergedConfig.description && (\r\n <Typography variant='caption' color='text.secondary' sx={{ mb: 1, display: 'block' }}>\r\n {mergedConfig.description}\r\n </Typography>\r\n )}\r\n {params.maxValueCount != null && (\r\n <Typography variant='caption' color={isMaxReached ? 'warning.main' : 'text.secondary'} sx={{ mb: 0.5, display: 'block' }}>\r\n {getMaxReachedText()}\r\n </Typography>\r\n )}\r\n <ChipViewers\r\n sx={{ mb: 1, borderBottom: 'none!important' }}\r\n label='Applied'\r\n placement='horizontal'\r\n enableMinimalesticView\r\n value={filterViewerValue}\r\n onRemove={props.onRemove}\r\n />\r\n <RadioGroup sx={{ mx: -1 }} name={mergedConfig.field.toString()} className={errorResult.error ? 'error' : ''}>\r\n {options.map((x, i) => {\r\n const isSelected = filterViewerValue.items.some((item) => item.value === x.value)\r\n const disabled = isSelected || isMaxReached\r\n return (\r\n <FormControlLabel\r\n disabled={disabled}\r\n key={x.value.toString() + i}\r\n value={x.value}\r\n control={<Radio size='small' />}\r\n label={<Typography variant='body2'>{x.label ?? x.value}</Typography>}\r\n />\r\n )\r\n })}\r\n </RadioGroup>\r\n {errorResult.error && (\r\n <Typography variant='caption' color='error' sx={{ mt: 0.5 }}>\r\n {errorResult.message}\r\n </Typography>\r\n )}\r\n </PopperBody>\r\n <PopperFooter>\r\n <Button size='small' color='error' variant='text' disabled={!value.values || value.values.length === 0} onClick={handleClearAll}>\r\n Clear All\r\n </Button>\r\n <Box sx={{ flex: 1 }} />\r\n <Button size='small' color='inherit' variant='text' onClick={props.onClose}>\r\n Cancel\r\n </Button>\r\n <Button size='small' type='submit' color='primary' variant='contained'>\r\n Apply\r\n </Button>\r\n </PopperFooter>\r\n </PopperContent>\r\n </RootStyled>\r\n )\r\n }\r\n\r\n return FormFieldSelect\r\n}\r\n\r\nexport default createFormFieldSelect\r\n\r\nconst RootStyled = styled('form')({\r\n position: 'relative',\r\n '&::after': {\r\n content: '\"\"',\r\n display: 'block',\r\n position: 'absolute',\r\n inset: 0, // top: 0, left: 0, right: 0, bottom: 0\r\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\r\n filter: 'blur(2px)',\r\n zIndex: -1,\r\n opacity: 0,\r\n transition: 'opacity 0.3s',\r\n visibility: 'hidden'\r\n },\r\n '&.disabled': {\r\n pointerEvents: 'none',\r\n '&::after': {\r\n zIndex: 1,\r\n opacity: 1,\r\n visibility: 'visible'\r\n }\r\n },\r\n [`.${radioGroupClasses.root}`]: {\r\n '&.error .MuiRadio-root': { color: '#d32f2f' },\r\n '.MuiFormControlLabel-root': { margin: 0 },\r\n '.MuiFormControlLabel-root:hover': { backgroundColor: 'rgba(25, 118, 210, 0.04)' }\r\n }\r\n})\r\n"],"names":["createFormFieldSelect","params","ChipViewers","createChipViewers","options","props","_mergedConfig$default","_params$forceLogic","_mergedConfig$label","mergedConfig","useMemo","Object","assign","currentConfig","config","_props$value","value","values","logic","defaultLogic","_useState","useState","_useState2","_slicedToArray","filterLogic","setFilterLogic","effectiveLogic","forceLogic","label","field","toString","_useState3","_useState4","errorData","setErrorData","handleSubmit","newValue","onSubmit","errorResult","getErrorMessage","filterViewerValue","items","Array","isArray","map","v","_options$find","find","o","isMaxReached","maxValueCount","length","rootClasses","isLoading","push","_jsx","RootStyled","className","join","noValidate","event","_props$validator","preventDefault","formData","FormData","currentTarget","obj","convertFormDataToJson","validator","run","keys","newValues","children","_jsxs","PopperContent","title","concat","onClose","slots","beforeTitle","ButtonBack","size","onClick","onBack","afterTitle","singleValue","ChipDark","sx","ml","FilterLogicToggle","onChange","_","nVal","newLogic","handleChangeLogic","PopperBody","description","Typography","variant","color","mb","display","borderBottom","placement","enableMinimalesticView","onRemove","RadioGroup","mx","name","error","x","i","_x$label","isSelected","some","item","FormControlLabel","disabled","control","Radio","mt","message","PopperFooter","Button","_props$onRemoveField","_params$config","onRemoveField","call","closeAfterClear","Box","flex","type","styled","_defineProperty","position","content","inset","backgroundColor","filter","zIndex","opacity","transition","visibility","pointerEvents","radioGroupClasses","root","margin"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
2
|
import type { IFieldSelectOption } from './create-form-field-select';
|
|
3
|
+
import type { TLogic } from '../types';
|
|
3
4
|
import type { IFieldMenuConfig, IFilterMenuFormProps } from './types';
|
|
4
5
|
/** Props for the `FormFieldSelectMultiple` component returned by `createFormFieldSelectMultiple`. Extends the base filter-menu form props. */
|
|
5
6
|
export interface IFormFieldSelectMultipleProps<T> extends IFilterMenuFormProps<T> {
|
|
@@ -12,6 +13,10 @@ export interface IFormFieldSelectMultipleParam<T> {
|
|
|
12
13
|
options: IFieldSelectOption[];
|
|
13
14
|
/** If true, disables the field after submission. @default false */
|
|
14
15
|
disabledAfterSubmit?: boolean;
|
|
16
|
+
/** Force a fixed logic value, hiding the logic toggle */
|
|
17
|
+
forceLogic?: TLogic;
|
|
18
|
+
/** Maximum number of values that can be selected */
|
|
19
|
+
maxValueCount?: number;
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
17
22
|
* Factory function that creates a `FormFieldSelectMultiple` filter-menu component.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
import type { IFieldMenuConfig, IFilterMenuFormProps } from './types';
|
|
3
|
-
import type { TFieldValid } from '../types';
|
|
3
|
+
import type { TFieldValid, TLogic } from '../types';
|
|
4
4
|
/** Props for the `FormFieldSelect` component returned by `createFormFieldSelect`. Extends the base filter-menu form props. */
|
|
5
5
|
export interface IFormFieldSelectProps<T> extends IFilterMenuFormProps<T> {
|
|
6
6
|
}
|
|
@@ -15,6 +15,8 @@ export interface IFormFieldSelectParams<T> {
|
|
|
15
15
|
config?: IFieldMenuConfig<T>;
|
|
16
16
|
/** List of options for the select field */
|
|
17
17
|
options: IFieldSelectOption[];
|
|
18
|
+
forceLogic?: TLogic;
|
|
19
|
+
maxValueCount?: number;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Factory function that creates a `FormFieldSelect` filter-menu component.
|