2021-06-29 04:03:36 -05:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2022-01-04 02:24:25 -06:00
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
2021-06-29 04:03:36 -05:00
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
/* Common form components used in pgAdmin */
2021-08-26 06:32:59 -05:00
import React , { forwardRef , useCallback , useEffect , useRef , useState } from 'react' ;
2021-06-29 04:03:36 -05:00
import { makeStyles } from '@material-ui/core/styles' ;
2022-03-21 02:59:26 -05:00
import {
Box , FormControl , OutlinedInput , FormHelperText ,
Grid , IconButton , FormControlLabel , Switch , Checkbox , useTheme , InputLabel , Paper , Select as MuiSelect , Radio ,
} from '@material-ui/core' ;
2021-06-29 04:03:36 -05:00
import { ToggleButton , ToggleButtonGroup } from '@material-ui/lab' ;
2021-12-02 04:35:52 -06:00
import ErrorRoundedIcon from '@material-ui/icons/ErrorOutlineRounded' ;
import InfoRoundedIcon from '@material-ui/icons/InfoRounded' ;
2021-06-29 04:03:36 -05:00
import CloseIcon from '@material-ui/icons/CloseRounded' ;
import CheckRoundedIcon from '@material-ui/icons/CheckRounded' ;
2021-12-02 04:35:52 -06:00
import WarningRoundedIcon from '@material-ui/icons/WarningRounded' ;
2021-06-29 04:03:36 -05:00
import FolderOpenRoundedIcon from '@material-ui/icons/FolderOpenRounded' ;
2021-07-29 07:36:15 -05:00
import DescriptionIcon from '@material-ui/icons/Description' ;
2022-03-21 02:59:26 -05:00
import AssignmentTurnedIn from '@material-ui/icons/AssignmentTurnedIn' ;
import Select , { components as RSComponents } from 'react-select' ;
2021-06-29 04:03:36 -05:00
import CreatableSelect from 'react-select/creatable' ;
import Pickr from '@simonwep/pickr' ;
import clsx from 'clsx' ;
import PropTypes from 'prop-types' ;
2021-07-29 07:36:15 -05:00
import HTMLReactParse from 'html-react-parser' ;
2022-03-21 02:59:26 -05:00
import { KeyboardDateTimePicker , KeyboardDatePicker , KeyboardTimePicker , MuiPickersUtilsProvider } from '@material-ui/pickers' ;
2021-09-29 03:13:05 -05:00
import DateFnsUtils from '@date-io/date-fns' ;
import * as DateFns from 'date-fns' ;
2021-06-29 04:03:36 -05:00
import CodeMirror from './CodeMirror' ;
import gettext from 'sources/gettext' ;
import _ from 'lodash' ;
import { DefaultButton , PrimaryButton , PgIconButton } from './Buttons' ;
import CustomPropTypes from '../custom_prop_types' ;
2022-03-21 02:59:26 -05:00
import KeyboardShortcuts from './KeyboardShortcuts' ;
import QueryThresholds from './QueryThresholds' ;
2022-03-23 02:58:35 -05:00
import SelectThemes from './SelectThemes' ;
2022-07-19 04:57:47 -05:00
import { showFileManager } from '../helpers/showFileManager' ;
2021-06-29 04:03:36 -05:00
const useStyles = makeStyles ( ( theme ) => ( {
formRoot : {
padding : '1rem'
} ,
img : {
maxWidth : '100%' ,
height : 'auto'
} ,
info : {
color : theme . palette . info . main ,
marginLeft : '0.25rem' ,
fontSize : '1rem' ,
} ,
formLabel : {
margin : theme . spacing ( 0.75 , 0.75 , 0.75 , 0.75 ) ,
display : 'flex' ,
2022-03-24 05:38:13 -05:00
wordBreak : 'break-word'
2021-06-29 04:03:36 -05:00
} ,
2022-03-21 02:59:26 -05:00
formLabelError : {
2021-06-29 04:03:36 -05:00
color : theme . palette . error . main ,
} ,
sql : {
2021-08-24 01:21:47 -05:00
border : '1px solid ' + theme . otherVars . inputBorderColor ,
borderRadius : theme . shape . borderRadius ,
2021-08-24 03:10:37 -05:00
height : '100%' ,
2021-06-29 04:03:36 -05:00
} ,
optionIcon : {
... theme . mixins . nodeIcon ,
} ,
colorBtn : {
height : theme . spacing ( 3.5 ) ,
minHeight : theme . spacing ( 3.5 ) ,
width : theme . spacing ( 3.5 ) ,
minWidth : theme . spacing ( 3.5 ) ,
2021-07-29 07:36:15 -05:00
} ,
noteRoot : {
display : 'flex' ,
backgroundColor : theme . otherVars . borderColor ,
padding : theme . spacing ( 1 ) ,
2021-09-27 23:54:25 -05:00
} ,
readOnlySwitch : {
opacity : 0.75 ,
'& .MuiSwitch-track' : {
opacity : theme . palette . action . disabledOpacity ,
}
2021-06-29 04:03:36 -05:00
}
} ) ) ;
export const MESSAGE _TYPE = {
SUCCESS : 'Success' ,
ERROR : 'Error' ,
INFO : 'Info' ,
CLOSE : 'Close' ,
2021-12-02 04:35:52 -06:00
WARNING : 'Warning'
2021-06-29 04:03:36 -05:00
} ;
/* Icon based on MESSAGE_TYPE */
2022-03-21 02:59:26 -05:00
function FormIcon ( { type , close = false , ... props } ) {
2021-06-29 04:03:36 -05:00
let TheIcon = null ;
2022-03-21 02:59:26 -05:00
if ( close ) {
2021-06-29 04:03:36 -05:00
TheIcon = CloseIcon ;
2022-03-21 02:59:26 -05:00
} else if ( type === MESSAGE _TYPE . SUCCESS ) {
2021-12-02 04:35:52 -06:00
TheIcon = CheckRoundedIcon ;
2022-03-21 02:59:26 -05:00
} else if ( type === MESSAGE _TYPE . ERROR ) {
2021-12-02 04:35:52 -06:00
TheIcon = ErrorRoundedIcon ;
2022-03-21 02:59:26 -05:00
} else if ( type === MESSAGE _TYPE . INFO ) {
2021-12-02 04:35:52 -06:00
TheIcon = InfoRoundedIcon ;
2022-03-21 02:59:26 -05:00
} else if ( type === MESSAGE _TYPE . WARNING ) {
2021-12-02 04:35:52 -06:00
TheIcon = WarningRoundedIcon ;
2021-06-29 04:03:36 -05:00
}
return < TheIcon fontSize = "small" { ...props } / > ;
}
FormIcon . propTypes = {
type : PropTypes . oneOf ( Object . values ( MESSAGE _TYPE ) ) ,
close : PropTypes . bool ,
} ;
/* Wrapper on any form component to add label, error indicator and help message */
2022-03-21 02:59:26 -05:00
export function FormInput ( { children , error , className , label , helpMessage , required , testcid } ) {
2021-06-29 04:03:36 -05:00
const classes = useStyles ( ) ;
const cid = testcid || _ . uniqueId ( 'c' ) ;
const helpid = ` h ${ cid } ` ;
return (
< Grid container spacing = { 0 } className = { className } >
< Grid item lg = { 3 } md = { 3 } sm = { 3 } xs = { 12 } >
2022-03-21 02:59:26 -05:00
< InputLabel htmlFor = { cid } className = { clsx ( classes . formLabel , error ? classes . formLabelError : null ) } required = { required } >
2021-06-29 04:03:36 -05:00
{ label }
2022-03-21 02:59:26 -05:00
< FormIcon type = { MESSAGE _TYPE . ERROR } style = { { marginLeft : 'auto' , visibility : error ? 'unset' : 'hidden' } } / >
2021-06-29 04:03:36 -05:00
< / InputLabel >
< / Grid >
< Grid item lg = { 9 } md = { 9 } sm = { 9 } xs = { 12 } >
< FormControl error = { Boolean ( error ) } fullWidth >
2022-03-21 02:59:26 -05:00
{ React . cloneElement ( children , { cid , helpid } ) }
2021-06-29 04:03:36 -05:00
< / FormControl >
2021-08-10 06:51:09 -05:00
< FormHelperText id = { helpid } variant = "outlined" > { HTMLReactParse ( helpMessage || '' ) } < / FormHelperText >
2021-06-29 04:03:36 -05:00
< / Grid >
< / Grid >
) ;
}
FormInput . propTypes = {
children : CustomPropTypes . children ,
error : PropTypes . bool ,
className : CustomPropTypes . className ,
label : PropTypes . string ,
helpMessage : PropTypes . string ,
required : PropTypes . bool ,
testcid : PropTypes . any ,
} ;
2022-04-28 03:39:27 -05:00
export function InputSQL ( { value , options , onChange , className , controlProps , inputRef , ... props } ) {
2021-06-29 04:03:36 -05:00
const classes = useStyles ( ) ;
2021-08-24 01:21:47 -05:00
const editor = useRef ( ) ;
2021-08-16 09:18:08 -05:00
2021-08-26 06:32:59 -05:00
return (
2021-06-29 04:03:36 -05:00
< CodeMirror
2022-04-28 03:39:27 -05:00
currEditor = { ( obj ) => {
editor . current = obj ;
inputRef ? . ( obj ) ;
} }
2022-03-21 02:59:26 -05:00
value = { value || '' }
options = { {
lineNumbers : true ,
mode : 'text/x-pgsql' ,
... options ,
} }
2021-08-24 03:10:37 -05:00
className = { clsx ( classes . sql , className ) }
2021-07-07 07:47:16 -05:00
events = { {
2022-03-21 02:59:26 -05:00
change : ( cm ) => {
2021-08-24 01:21:47 -05:00
onChange && onChange ( cm . getValue ( ) ) ;
2021-07-07 07:47:16 -05:00
} ,
} }
2022-02-10 23:06:24 -06:00
{ ... controlProps }
2021-07-07 07:47:16 -05:00
{ ... props }
2021-06-29 04:03:36 -05:00
/ >
2021-08-26 06:32:59 -05:00
) ;
2021-06-29 04:03:36 -05:00
}
InputSQL . propTypes = {
value : PropTypes . string ,
options : PropTypes . object ,
2021-08-16 09:18:08 -05:00
onChange : PropTypes . func ,
2021-08-26 06:32:59 -05:00
readonly : PropTypes . bool ,
className : CustomPropTypes . className ,
2022-02-10 23:06:24 -06:00
controlProps : PropTypes . object ,
2022-04-28 03:39:27 -05:00
inputRef : CustomPropTypes . ref ,
2021-06-29 04:03:36 -05:00
} ;
2022-03-21 02:59:26 -05:00
export function FormInputSQL ( { hasError , required , label , className , helpMessage , testcid , value , controlProps , noLabel , ... props } ) {
if ( noLabel ) {
return < InputSQL value = { value } options = { controlProps } { ...props } / > ;
2021-07-26 00:49:19 -05:00
} else {
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputSQL value = { value } options = { controlProps } { ...props } / >
2021-07-26 00:49:19 -05:00
< / FormInput >
) ;
}
2021-06-29 04:03:36 -05:00
}
FormInputSQL . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
value : PropTypes . string ,
2022-04-07 07:06:56 -05:00
controlProps : PropTypes . object ,
2021-08-02 01:08:40 -05:00
noLabel : PropTypes . bool ,
2021-07-07 07:47:16 -05:00
change : PropTypes . func ,
2021-06-29 04:03:36 -05:00
} ;
2021-09-29 03:13:05 -05:00
/* https://date-fns.org/v2.24.0/docs/format */
const DATE _TIME _FORMAT = {
DATE _TIME _12 : 'yyyy-MM-dd hh:mm:ss aa xxx' ,
2021-09-30 04:30:32 -05:00
DATE _TIME _24 : 'yyyy-MM-dd HH:mm:ss xxx' ,
2021-09-29 03:13:05 -05:00
DATE : 'yyyy-MM-dd' ,
2021-09-29 09:18:43 -05:00
TIME _12 : 'hh:mm:ss aa' ,
TIME _24 : 'HH:mm:ss' ,
2021-09-29 03:13:05 -05:00
} ;
2021-08-02 01:08:40 -05:00
2022-03-21 02:59:26 -05:00
export function InputDateTimePicker ( { value , onChange , readonly , controlProps , ... props } ) {
2021-09-29 03:13:05 -05:00
let format = '' ;
2021-09-30 04:30:32 -05:00
let placeholder = '' ;
2022-04-05 00:49:24 -05:00
let regExp = /[a-zA-Z]/ ;
2021-09-29 03:13:05 -05:00
if ( controlProps ? . pickerType === 'Date' ) {
format = controlProps . format || DATE _TIME _FORMAT . DATE ;
2021-09-30 04:30:32 -05:00
placeholder = controlProps . placeholder || 'YYYY-MM-DD' ;
2021-09-29 03:13:05 -05:00
} else if ( controlProps ? . pickerType === 'Time' ) {
format = controlProps . format || ( controlProps . ampm ? DATE _TIME _FORMAT . TIME _12 : DATE _TIME _FORMAT . TIME _24 ) ;
2021-09-30 04:30:32 -05:00
placeholder = controlProps . placeholder || 'HH:mm:ss' ;
2021-09-29 03:13:05 -05:00
} else {
format = controlProps . format || ( controlProps . ampm ? DATE _TIME _FORMAT . DATE _TIME _12 : DATE _TIME _FORMAT . DATE _TIME _24 ) ;
2021-09-30 04:30:32 -05:00
placeholder = controlProps . placeholder || 'YYYY-MM-DD HH:mm:ss Z' ;
2021-09-29 03:13:05 -05:00
}
2021-08-02 01:08:40 -05:00
2022-03-21 02:59:26 -05:00
const handleChange = ( dateVal , stringVal ) => {
2021-09-30 04:30:32 -05:00
onChange ( stringVal ) ;
2021-08-02 01:08:40 -05:00
} ;
2021-09-29 03:13:05 -05:00
/* Value should be a date object instead of string */
2022-04-05 00:49:24 -05:00
value = _ . isUndefined ( value ) || regExp . test ( value ) ? null : value ;
2022-03-21 02:59:26 -05:00
if ( ! _ . isNull ( value ) ) {
2021-09-29 03:13:05 -05:00
let parseValue = DateFns . parse ( value , format , new Date ( ) ) ;
2022-03-21 02:59:26 -05:00
if ( ! DateFns . isValid ( parseValue ) ) {
2021-09-29 03:13:05 -05:00
parseValue = DateFns . parseISO ( value ) ;
}
2021-09-30 04:30:32 -05:00
value = ! DateFns . isValid ( parseValue ) ? value : parseValue ;
2021-09-29 03:13:05 -05:00
}
2021-08-02 01:08:40 -05:00
2021-08-10 06:51:09 -05:00
if ( readonly ) {
2021-09-29 03:13:05 -05:00
return ( < InputText value = { value ? DateFns . format ( value , format ) : value }
2022-03-21 02:59:26 -05:00
readonly = { readonly } controlProps = { { placeholder : controlProps . placeholder } } { ... props } / > ) ;
2021-08-10 06:51:09 -05:00
}
2021-09-30 04:30:32 -05:00
let commonProps = {
... props ,
value : value ,
format : format ,
placeholder : placeholder ,
label : '' ,
variant : 'inline' ,
readOnly : Boolean ( readonly ) ,
autoOk : controlProps . autoOk || false ,
ampm : controlProps . ampm || false ,
disablePast : controlProps . disablePast || false ,
invalidDateMessage : '' ,
maxDateMessage : '' ,
minDateMessage : '' ,
onChange : handleChange ,
fullWidth : true ,
} ;
2021-09-29 03:13:05 -05:00
if ( controlProps ? . pickerType === 'Date' ) {
2021-08-02 01:08:40 -05:00
return (
2021-09-29 03:13:05 -05:00
< MuiPickersUtilsProvider utils = { DateFnsUtils } >
2022-03-21 02:59:26 -05:00
< KeyboardDatePicker { ...commonProps } / >
2021-08-02 01:08:40 -05:00
< / MuiPickersUtilsProvider >
) ;
2021-09-29 03:13:05 -05:00
} else if ( controlProps ? . pickerType === 'Time' ) {
2021-08-03 02:09:30 -05:00
return (
2021-09-29 03:13:05 -05:00
< MuiPickersUtilsProvider utils = { DateFnsUtils } >
2022-03-21 02:59:26 -05:00
< KeyboardTimePicker { ...commonProps } / >
2021-08-02 01:08:40 -05:00
< / MuiPickersUtilsProvider >
2021-08-03 02:09:30 -05:00
) ;
2021-08-02 01:08:40 -05:00
}
return (
2021-09-29 03:13:05 -05:00
< MuiPickersUtilsProvider utils = { DateFnsUtils } >
2022-03-21 02:59:26 -05:00
< KeyboardDateTimePicker { ...commonProps } / >
2021-08-02 01:08:40 -05:00
< / MuiPickersUtilsProvider >
) ;
}
InputDateTimePicker . propTypes = {
value : PropTypes . string ,
options : PropTypes . object ,
2021-08-03 02:09:30 -05:00
onChange : PropTypes . func ,
readonly : PropTypes . bool ,
controlProps : PropTypes . object ,
2021-08-02 01:08:40 -05:00
} ;
2022-03-21 02:59:26 -05:00
export function FormInputDateTimePicker ( { hasError , required , label , className , helpMessage , testcid , ... props } ) {
2021-08-02 01:08:40 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputDateTimePicker { ...props } / >
2021-08-02 01:08:40 -05:00
< / FormInput >
) ;
}
FormInputDateTimePicker . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
value : PropTypes . string ,
controlProps : PropTypes . object ,
change : PropTypes . func ,
} ;
2021-06-29 04:03:36 -05:00
/* Use forwardRef to pass ref prop to OutlinedInput */
export const InputText = forwardRef ( ( {
2022-07-19 04:57:47 -05:00
cid , helpid , readonly , disabled , value , onChange , controlProps , type , size , ... props } , ref ) => {
2022-06-22 07:05:22 -05:00
const maxlength = typeof ( controlProps ? . maxLength ) != 'undefined' ? controlProps . maxLength : 255 ;
2021-06-29 04:03:36 -05:00
2021-08-30 06:31:49 -05:00
const patterns = {
'numeric' : '^-?[0-9]\\d*\\.?\\d*$' ,
'int' : '^-?[0-9]\\d*$' ,
} ;
2022-03-21 02:59:26 -05:00
let onChangeFinal = ( e ) => {
2021-08-30 06:31:49 -05:00
let changeVal = e . target . value ;
2021-06-29 04:03:36 -05:00
2021-08-30 06:31:49 -05:00
/* For type number, we set type as tel with number regex to get validity.*/
2022-03-21 02:59:26 -05:00
if ( [ 'numeric' , 'int' , 'tel' ] . indexOf ( type ) > - 1 ) {
if ( ! e . target . validity . valid && changeVal !== '' && changeVal !== '-' ) {
2021-08-30 06:31:49 -05:00
return ;
}
}
2022-03-21 02:59:26 -05:00
if ( controlProps ? . formatter ) {
2021-07-08 23:56:02 -05:00
changeVal = controlProps . formatter . toRaw ( changeVal ) ;
}
onChange && onChange ( changeVal ) ;
2021-07-10 02:29:20 -05:00
} ;
2021-07-08 23:56:02 -05:00
let finalValue = ( _ . isNull ( value ) || _ . isUndefined ( value ) ) ? '' : value ;
2021-09-01 06:50:37 -05:00
2022-03-21 02:59:26 -05:00
if ( controlProps ? . formatter ) {
2021-07-08 23:56:02 -05:00
finalValue = controlProps . formatter . fromRaw ( finalValue ) ;
}
2022-07-19 04:57:47 -05:00
const filteredProps = _ . pickBy ( props , ( _v , key ) => (
/* When used in ButtonGroup, following props should be skipped */
! [ 'color' , 'disableElevation' , 'disableFocusRipple' , 'disableRipple' ] . includes ( key )
) ) ;
2022-03-21 02:59:26 -05:00
return (
2021-06-29 04:03:36 -05:00
< OutlinedInput
ref = { ref }
color = "primary"
fullWidth
2022-07-19 04:57:47 -05:00
margin = { size == 'small' ? 'dense' : 'none' }
2021-06-29 04:03:36 -05:00
inputProps = { {
id : cid ,
2021-11-07 00:21:54 -05:00
maxLength : controlProps ? . multiline ? null : maxlength ,
2021-06-29 04:03:36 -05:00
'aria-describedby' : helpid ,
2022-03-21 02:59:26 -05:00
... ( type ? { pattern : ! _ . isUndefined ( controlProps ) && ! _ . isUndefined ( controlProps . pattern ) ? controlProps . pattern : patterns [ type ] } : { } )
2021-06-29 04:03:36 -05:00
} }
readOnly = { Boolean ( readonly ) }
disabled = { Boolean ( disabled ) }
rows = { 4 }
notched = { false }
2021-07-08 23:56:02 -05:00
value = { ( _ . isNull ( finalValue ) || _ . isUndefined ( finalValue ) ) ? '' : finalValue }
onChange = { onChangeFinal }
2022-03-21 02:59:26 -05:00
{
... ( controlProps ? . onKeyDown && { onKeyDown : controlProps . onKeyDown } )
}
2021-06-29 04:03:36 -05:00
{ ... controlProps }
2022-07-19 04:57:47 -05:00
{ ... filteredProps }
2022-03-21 02:59:26 -05:00
{ ... ( [ 'numeric' , 'int' ] . indexOf ( type ) > - 1 ? { type : 'tel' } : { type : type } ) }
2021-06-29 04:03:36 -05:00
/ >
) ;
} ) ;
InputText . displayName = 'InputText' ;
InputText . propTypes = {
cid : PropTypes . string ,
helpid : PropTypes . string ,
label : PropTypes . string ,
readonly : PropTypes . bool ,
disabled : PropTypes . bool ,
2021-09-01 06:50:37 -05:00
value : PropTypes . any ,
2021-06-29 04:03:36 -05:00
onChange : PropTypes . func ,
controlProps : PropTypes . object ,
2021-08-30 06:31:49 -05:00
type : PropTypes . string ,
2022-07-19 04:57:47 -05:00
size : PropTypes . string ,
2021-06-29 04:03:36 -05:00
} ;
2022-03-21 02:59:26 -05:00
export function FormInputText ( { hasError , required , label , className , helpMessage , testcid , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputText label = { label } { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputText . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
} ;
2022-03-23 02:58:35 -05:00
export function InputFileSelect ( { controlProps , onChange , disabled , readonly , isvalidate = false , hideBrowseButton = false , validate , ... props } ) {
2021-06-29 04:03:36 -05:00
const inpRef = useRef ( ) ;
2022-03-24 05:38:13 -05:00
let textControlProps = { } ;
if ( controlProps ? . placeholder ) {
const { placeholder } = controlProps ;
textControlProps = { placeholder } ;
}
2022-07-19 04:57:47 -05:00
const showFileDialog = ( ) => {
let params = {
supported _types : controlProps . supportedTypes || [ ] ,
dialog _type : controlProps . dialogType || 'select_file' ,
dialog _title : controlProps . dialogTitle || '' ,
btn _primary : controlProps . btnPrimary || '' ,
} ;
showFileManager ( params , ( fileName ) => {
onChange && onChange ( decodeURI ( fileName ) ) ;
inpRef . current . focus ( ) ;
} ) ;
2021-06-29 04:03:36 -05:00
} ;
2022-07-19 04:57:47 -05:00
2021-06-29 04:03:36 -05:00
return (
2022-03-24 05:38:13 -05:00
< InputText ref = { inpRef } disabled = { disabled } readonly = { readonly } onChange = { onChange } controlProps = { textControlProps } { ...props } endAdornment = {
2022-03-21 02:59:26 -05:00
< >
2022-03-23 02:58:35 -05:00
{ ! hideBrowseButton &&
2022-07-19 04:57:47 -05:00
< IconButton onClick = { showFileDialog }
2022-03-21 02:59:26 -05:00
disabled = { disabled || readonly } aria - label = { gettext ( 'Select a file' ) } > < FolderOpenRoundedIcon / > < / IconButton >
2022-03-23 02:58:35 -05:00
}
2022-03-21 02:59:26 -05:00
{ isvalidate &&
< PgIconButton title = { gettext ( 'Validate' ) } style = { { border : 'none' } } disabled = { ! props . value } onClick = { ( ) => { validate ( props . value ) ; } } icon = { < AssignmentTurnedIn / > } > < / PgIconButton >
}
< / >
2021-06-29 04:03:36 -05:00
} / >
) ;
}
InputFileSelect . propTypes = {
controlProps : PropTypes . object ,
onChange : PropTypes . func ,
disabled : PropTypes . bool ,
readonly : PropTypes . bool ,
2022-03-21 02:59:26 -05:00
isvalidate : PropTypes . bool ,
validate : PropTypes . func ,
2022-03-23 02:58:35 -05:00
value : PropTypes . string ,
hideBrowseButton : PropTypes . bool
2021-06-29 04:03:36 -05:00
} ;
export function FormInputFileSelect ( {
2022-03-21 02:59:26 -05:00
hasError , required , label , className , helpMessage , testcid , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputFileSelect required = { required } label = { label } { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputFileSelect . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
} ;
2022-03-21 02:59:26 -05:00
export function InputSwitch ( { cid , helpid , value , onChange , readonly , controlProps , ... props } ) {
2021-09-27 23:54:25 -05:00
const classes = useStyles ( ) ;
2021-06-29 04:03:36 -05:00
return (
< Switch color = "primary"
checked = { Boolean ( value ) }
onChange = {
2022-03-21 02:59:26 -05:00
readonly ? ( ) => { /*This is intentional (SonarQube)*/ } : onChange
2021-06-29 04:03:36 -05:00
}
id = { cid }
inputProps = { {
'aria-describedby' : helpid ,
} }
{ ... controlProps }
{ ... props }
2021-09-27 23:54:25 -05:00
className = { ( readonly || props . disabled ) ? classes . readOnlySwitch : null }
2021-06-29 04:03:36 -05:00
/ >
) ;
}
InputSwitch . propTypes = {
cid : PropTypes . string ,
helpid : PropTypes . string ,
value : PropTypes . any ,
onChange : PropTypes . func ,
readonly : PropTypes . bool ,
2021-09-27 23:54:25 -05:00
disabled : PropTypes . bool ,
2021-06-29 04:03:36 -05:00
controlProps : PropTypes . object ,
} ;
2022-03-21 02:59:26 -05:00
export function FormInputSwitch ( { hasError , required , label , className , helpMessage , testcid , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputSwitch { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputSwitch . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
} ;
2022-03-21 02:59:26 -05:00
export function InputCheckbox ( { cid , helpid , value , onChange , controlProps , readonly , ... props } ) {
2021-06-29 04:03:36 -05:00
controlProps = controlProps || { } ;
return (
< FormControlLabel
control = {
< Checkbox
id = { cid }
checked = { Boolean ( value ) }
2022-03-21 02:59:26 -05:00
onChange = { readonly ? ( ) => { /*This is intentional (SonarQube)*/ } : onChange }
2021-06-29 04:03:36 -05:00
color = "primary"
2022-03-21 02:59:26 -05:00
inputProps = { { 'aria-describedby' : helpid } }
{ ... props } / >
2021-06-29 04:03:36 -05:00
}
label = { controlProps . label }
/ >
) ;
}
InputCheckbox . propTypes = {
cid : PropTypes . string ,
helpid : PropTypes . string ,
value : PropTypes . bool ,
controlProps : PropTypes . object ,
onChange : PropTypes . func ,
readonly : PropTypes . bool ,
} ;
2022-03-21 02:59:26 -05:00
export function FormInputCheckbox ( { hasError , required , label ,
className , helpMessage , testcid , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputCheckbox { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputCheckbox . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
} ;
2022-03-21 02:59:26 -05:00
export function InputRadio ( { helpid , value , onChange , controlProps , readonly , ... props } ) {
const classes = useStyles ( ) ;
controlProps = controlProps || { } ;
return (
< FormControlLabel
control = {
< Radio
color = "primary"
checked = { props ? . disabled ? false : value }
onChange = {
readonly ? ( ) => {
/*This is intentional (SonarQube)*/ } : onChange
}
value = { value }
name = "radio-button-demo"
inputProps = { { 'aria-label' : value , 'aria-describedby' : helpid } }
style = { { padding : 0 } }
disableRipple
{ ... props }
/ >
2022-04-07 07:06:56 -05:00
2022-03-21 02:59:26 -05:00
}
label = { controlProps . label }
className = { ( readonly || props . disabled ) ? classes . readOnlySwitch : null }
/ >
) ;
}
InputRadio . propTypes = {
helpid : PropTypes . string ,
value : PropTypes . bool ,
controlProps : PropTypes . object ,
onChange : PropTypes . func ,
readonly : PropTypes . bool ,
disabled : PropTypes . bool ,
labelPlacement : PropTypes . string
} ;
2021-06-29 04:03:36 -05:00
2022-03-21 02:59:26 -05:00
export const InputToggle = forwardRef ( ( { cid , value , onChange , options , disabled , readonly , ... props } , ref ) => {
2021-06-29 04:03:36 -05:00
return (
< ToggleButtonGroup
id = { cid }
value = { value }
exclusive
2022-03-21 02:59:26 -05:00
onChange = { ( e , val ) => { val !== null && onChange ( val ) ; } }
2021-06-29 04:03:36 -05:00
{ ... props }
>
{
2022-03-21 02:59:26 -05:00
( options || [ ] ) . map ( ( option , i ) => {
2021-06-29 04:03:36 -05:00
const isSelected = option . value === value ;
2021-09-23 04:16:10 -05:00
const isDisabled = disabled || option . disabled || ( readonly && ! isSelected ) ;
2021-06-29 04:03:36 -05:00
return (
2022-03-21 02:59:26 -05:00
< ToggleButton ref = { i == 0 ? ref : null } key = { option . label } value = { option . value } component = { isSelected ? PrimaryButton : DefaultButton }
2021-06-29 04:03:36 -05:00
disabled = { isDisabled } aria - label = { option . label } >
2022-03-21 02:59:26 -05:00
< CheckRoundedIcon style = { { visibility : isSelected ? 'visible' : 'hidden' } } / > & nbsp ; { option . label }
2021-06-29 04:03:36 -05:00
< / ToggleButton >
) ;
} )
}
< / ToggleButtonGroup >
) ;
2022-02-02 03:18:35 -06:00
} ) ;
InputToggle . displayName = 'InputToggle' ;
2021-06-29 04:03:36 -05:00
InputToggle . propTypes = {
cid : PropTypes . string ,
value : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . number , PropTypes . bool ] ) ,
options : PropTypes . array ,
controlProps : PropTypes . object ,
onChange : PropTypes . func ,
disabled : PropTypes . bool ,
readonly : PropTypes . bool ,
} ;
2022-03-21 02:59:26 -05:00
export function FormInputToggle ( { hasError , required , label ,
className , helpMessage , testcid , inputRef , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputToggle ref = { inputRef } { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputToggle . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
2022-02-02 03:18:35 -06:00
inputRef : CustomPropTypes . ref
2021-06-29 04:03:36 -05:00
} ;
/ * r e a c t - s e l e c t p a c k a g e i s u s e d f o r s e l e c t i n p u t
* Customizing the select styles to fit existing theme
* /
2022-03-21 02:59:26 -05:00
const customReactSelectStyles = ( theme , readonly ) => ( {
2021-06-29 04:03:36 -05:00
input : ( provided ) => {
2022-03-21 02:59:26 -05:00
return { ... provided , padding : 0 , margin : 0 , color : 'inherit' } ;
2021-06-29 04:03:36 -05:00
} ,
singleValue : ( provided ) => {
return {
... provided ,
color : 'inherit' ,
} ;
} ,
control : ( provided , state ) => ( {
... provided ,
minHeight : '0' ,
backgroundColor : readonly ? theme . otherVars . inputDisabledBg : theme . palette . background . default ,
2021-10-04 02:11:48 -05:00
color : readonly ? theme . palette . text . muted : theme . palette . text . primary ,
2021-06-29 04:03:36 -05:00
borderColor : theme . otherVars . inputBorderColor ,
... ( state . isFocused ? {
borderColor : theme . palette . primary . main ,
2022-03-21 02:59:26 -05:00
boxShadow : 'inset 0 0 0 1px ' + theme . palette . primary . main ,
2021-06-29 04:03:36 -05:00
'&:hover' : {
borderColor : theme . palette . primary . main ,
}
} : { } ) ,
} ) ,
2022-03-21 02:59:26 -05:00
dropdownIndicator : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
padding : '0rem 0.25rem' ,
} ) ,
2022-03-21 02:59:26 -05:00
indicatorsContainer : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
2022-03-21 02:59:26 -05:00
... ( readonly ? { display : 'none' } : { } )
2021-06-29 04:03:36 -05:00
} ) ,
2022-03-21 02:59:26 -05:00
clearIndicator : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
padding : '0rem 0.25rem' ,
} ) ,
2022-03-21 02:59:26 -05:00
valueContainer : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
padding : theme . otherVars . reactSelect . padding ,
} ) ,
2022-03-21 02:59:26 -05:00
groupHeading : ( provided ) => ( {
2022-01-18 07:14:55 -06:00
... provided ,
color : 'inherit' ,
fontSize : '0.85em' ,
textTransform : 'none' ,
} ) ,
2022-03-21 02:59:26 -05:00
menu : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
backgroundColor : theme . palette . background . default ,
color : theme . palette . text . primary ,
boxShadow : 'none' ,
border : '1px solid ' + theme . otherVars . inputBorderColor ,
2021-08-24 01:21:47 -05:00
marginTop : '2px' ,
2021-06-29 04:03:36 -05:00
} ) ,
2022-03-21 02:59:26 -05:00
menuPortal : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided , zIndex : 9999 ,
backgroundColor : 'inherit' ,
color : 'inherit' ,
} ) ,
2022-03-21 02:59:26 -05:00
option : ( provided , state ) => {
2022-01-18 03:19:54 -06:00
let bgColor = 'inherit' ;
if ( state . isFocused ) {
bgColor = theme . palette . grey [ 400 ] ;
} else if ( state . isSelected ) {
bgColor = theme . palette . primary . light ;
}
2021-08-24 01:21:47 -05:00
return {
... provided ,
padding : '0.5rem' ,
color : 'inherit' ,
2022-01-18 03:19:54 -06:00
backgroundColor : bgColor ,
2021-08-24 01:21:47 -05:00
} ;
} ,
2022-03-21 02:59:26 -05:00
multiValue : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
backgroundColor : theme . palette . grey [ 400 ] ,
} ) ,
2022-03-21 02:59:26 -05:00
multiValueLabel : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
fontSize : '1em' ,
2021-07-01 04:38:58 -05:00
zIndex : 99 ,
2021-06-29 04:03:36 -05:00
color : theme . palette . text . primary
} ) ,
2022-03-21 02:59:26 -05:00
multiValueRemove : ( provided ) => ( {
2021-06-29 04:03:36 -05:00
... provided ,
'&:hover' : {
backgroundColor : 'unset' ,
color : theme . palette . error . main ,
} ,
2022-03-21 02:59:26 -05:00
... ( readonly ? { display : 'none' } : { } )
2021-06-29 04:03:36 -05:00
} ) ,
} ) ;
2022-03-21 02:59:26 -05:00
function OptionView ( { image , label } ) {
2021-06-29 04:03:36 -05:00
const classes = useStyles ( ) ;
return (
< >
{ image && < span className = { clsx ( classes . optionIcon , image ) } > < / span > }
< span > { label } < / span >
< / >
) ;
}
OptionView . propTypes = {
image : PropTypes . string ,
label : PropTypes . string ,
} ;
function CustomSelectOption ( props ) {
return (
< RSComponents.Option { ...props } >
< OptionView image = { props . data . image } label = { props . data . label } / >
< / RSComponents.Option >
) ;
}
CustomSelectOption . propTypes = {
data : PropTypes . object ,
} ;
function CustomSelectSingleValue ( props ) {
return (
< RSComponents.SingleValue { ...props } >
< OptionView image = { props . data . image } label = { props . data . label } / >
< / RSComponents.SingleValue >
) ;
}
CustomSelectSingleValue . propTypes = {
data : PropTypes . object ,
} ;
2022-01-18 07:14:55 -06:00
export function flattenSelectOptions ( options ) {
2022-03-21 02:59:26 -05:00
return _ . flatMap ( options , ( option ) => {
if ( option . options ) {
2022-01-18 07:14:55 -06:00
return option . options ;
} else {
return option ;
}
} ) ;
}
2021-07-01 04:38:58 -05:00
function getRealValue ( options , value , creatable , formatter ) {
2021-06-29 04:03:36 -05:00
let realValue = null ;
2022-03-21 02:59:26 -05:00
if ( _ . isArray ( value ) ) {
2021-07-01 04:38:58 -05:00
realValue = [ ... value ] ;
/* If multi select options need to be in some format by UI, use formatter */
2022-03-21 02:59:26 -05:00
if ( formatter ) {
2021-08-10 06:51:09 -05:00
realValue = formatter . fromRaw ( realValue , options ) ;
2021-06-29 04:03:36 -05:00
} else {
2022-03-21 02:59:26 -05:00
if ( creatable ) {
realValue = realValue . map ( ( val ) => ( { label : val , value : val } ) ) ;
2021-08-10 06:51:09 -05:00
} else {
2022-03-21 02:59:26 -05:00
realValue = realValue . map ( ( val ) => ( _ . find ( options , ( option ) => _ . isEqual ( option . value , val ) ) ) ) ;
2021-08-10 06:51:09 -05:00
}
2021-06-29 04:03:36 -05:00
}
} else {
2022-01-18 07:14:55 -06:00
let flatOptions = flattenSelectOptions ( options ) ;
2022-03-21 02:59:26 -05:00
realValue = _ . find ( flatOptions , ( option ) => option . value == value ) ||
( creatable && ! _ . isUndefined ( value ) && ! _ . isNull ( value ) ? { label : value , value : value } : null ) ;
2021-06-29 04:03:36 -05:00
}
return realValue ;
}
2022-03-21 02:59:26 -05:00
export function InputSelectNonSearch ( { options , ... props } ) {
2022-02-10 23:06:24 -06:00
return < MuiSelect native { ...props } variant = "outlined" >
2022-03-21 02:59:26 -05:00
{ ( options || [ ] ) . map ( ( o ) => < option key = { o . value } value = { o . value } > { o . label } < / option > ) }
2022-02-10 23:06:24 -06:00
< / MuiSelect > ;
}
InputSelectNonSearch . propTypes = {
options : PropTypes . arrayOf ( PropTypes . shape ( {
label : PropTypes . shape ,
value : PropTypes . any ,
} ) ) ,
} ;
2021-06-29 04:03:36 -05:00
2022-02-02 03:18:35 -06:00
export const InputSelect = forwardRef ( ( {
2022-03-21 02:59:26 -05:00
cid , onChange , options , readonly = false , value , controlProps = { } , optionsLoaded , optionsReloadBasis , disabled , ... props } , ref ) => {
2021-06-29 04:03:36 -05:00
const [ [ finalOptions , isLoading ] , setFinalOptions ] = useState ( [ [ ] , true ] ) ;
const theme = useTheme ( ) ;
2022-04-07 07:06:56 -05:00
2021-07-08 23:56:02 -05:00
/ * R e a c t w i l l a l w a y s t a k e o p t i o n s v a r a s c h a n g e d p a r a m e t e r . S o ,
We cannot run the below effect with options dependency as it will keep on
loading the options . optionsReloadBasis is helpful to avoid repeated
options load . If optionsReloadBasis value changes , then options will be loaded again .
* /
2022-03-21 02:59:26 -05:00
useEffect ( ( ) => {
let optPromise = options , umounted = false ;
if ( typeof options === 'function' ) {
2021-06-29 04:03:36 -05:00
optPromise = options ( ) ;
}
2022-03-02 07:32:35 -06:00
setFinalOptions ( [ [ ] , true ] ) ;
2021-06-29 04:03:36 -05:00
Promise . resolve ( optPromise )
2022-03-21 02:59:26 -05:00
. then ( ( res ) => {
2021-06-29 04:03:36 -05:00
/* If component unmounted, dont update state */
2022-03-21 02:59:26 -05:00
if ( ! umounted ) {
2021-06-29 04:03:36 -05:00
optionsLoaded && optionsLoaded ( res , value ) ;
2022-01-18 07:14:55 -06:00
/* Auto select if any option has key as selected */
const flatRes = flattenSelectOptions ( res || [ ] ) ;
let selectedVal ;
2022-03-21 02:59:26 -05:00
if ( controlProps . multiple ) {
selectedVal = _ . filter ( flatRes , ( o ) => o . selected ) ? . map ( ( o ) => o . value ) ;
2022-01-18 07:14:55 -06:00
} else {
2022-03-21 02:59:26 -05:00
selectedVal = _ . find ( flatRes , ( o ) => o . selected ) ? . value ;
2022-01-18 07:14:55 -06:00
}
2022-01-31 02:11:22 -06:00
2022-03-21 02:59:26 -05:00
if ( ( ! _ . isUndefined ( selectedVal ) && ! _ . isArray ( selectedVal ) ) || ( _ . isArray ( selectedVal ) && selectedVal . length != 0 ) ) {
2022-01-18 07:14:55 -06:00
onChange && onChange ( selectedVal ) ;
}
2021-06-29 04:03:36 -05:00
setFinalOptions ( [ res || [ ] , false ] ) ;
}
} ) ;
2022-03-21 02:59:26 -05:00
return ( ) => umounted = true ;
2021-07-08 23:56:02 -05:00
} , [ optionsReloadBasis ] ) ;
2021-06-29 04:03:36 -05:00
2021-06-29 04:08:04 -05:00
/* Apply filter if any */
const filteredOptions = ( controlProps . filter && controlProps . filter ( finalOptions ) ) || finalOptions ;
2022-01-18 07:14:55 -06:00
const flatFiltered = flattenSelectOptions ( filteredOptions ) ;
let realValue = getRealValue ( flatFiltered , value , controlProps . creatable , controlProps . formatter ) ;
2022-03-21 02:59:26 -05:00
if ( realValue && _ . isPlainObject ( realValue ) && _ . isUndefined ( realValue . value ) ) {
2021-08-24 03:10:37 -05:00
console . error ( 'Undefined option value not allowed' , realValue , filteredOptions ) ;
}
2021-06-29 04:03:36 -05:00
const otherProps = {
isSearchable : ! readonly ,
isClearable : ! readonly && ( ! _ . isUndefined ( controlProps . allowClear ) ? controlProps . allowClear : true ) ,
isDisabled : Boolean ( disabled ) ,
} ;
const styles = customReactSelectStyles ( theme , readonly || disabled ) ;
2022-03-21 02:59:26 -05:00
const onChangeOption = useCallback ( ( selectVal ) => {
if ( _ . isArray ( selectVal ) ) {
2021-08-10 06:51:09 -05:00
// Check if select all option is selected
2022-06-22 06:48:51 -05:00
if ( ! _ . isUndefined ( selectVal . find ( x => x . label === '<Select All>' ) ) ) {
2021-08-10 06:51:09 -05:00
selectVal = filteredOptions ;
}
/* If multi select options need to be in some format by UI, use formatter */
2022-03-21 02:59:26 -05:00
if ( controlProps . formatter ) {
2021-08-10 06:51:09 -05:00
selectVal = controlProps . formatter . toRaw ( selectVal , filteredOptions ) ;
} else {
2022-03-21 02:59:26 -05:00
selectVal = selectVal . map ( ( option ) => option . value ) ;
2021-08-10 06:51:09 -05:00
}
2022-01-18 07:14:55 -06:00
onChange && onChange ( selectVal ) ;
2021-08-10 06:51:09 -05:00
} else {
2022-01-18 07:14:55 -06:00
onChange && onChange ( selectVal ? selectVal . value : null ) ;
2021-08-10 06:51:09 -05:00
}
} , [ onChange , filteredOptions ] ) ;
2021-06-29 04:03:36 -05:00
const commonProps = {
components : {
Option : CustomSelectOption ,
SingleValue : CustomSelectSingleValue ,
2022-04-09 01:19:02 -05:00
IndicatorSeparator : ( props ) => controlProps . noDropdown ? null : < RSComponents.IndicatorSeparator { ...props } / > ,
DropdownIndicator : ( props ) => controlProps . noDropdown ? null : < RSComponents.DropdownIndicator { ...props } / >
2021-06-29 04:03:36 -05:00
} ,
isMulti : Boolean ( controlProps . multiple ) ,
openMenuOnClick : ! readonly ,
onChange : onChangeOption ,
isLoading : isLoading ,
2022-06-22 06:48:51 -05:00
options : controlProps . allowSelectAll ? [ { label : gettext ( '<Select All>' ) , value : '*' } , ... filteredOptions ] : filteredOptions ,
2021-06-29 04:03:36 -05:00
value : realValue ,
menuPortalTarget : document . body ,
styles : styles ,
inputId : cid ,
2021-09-23 05:47:39 -05:00
placeholder : ( readonly || disabled ) ? '' : controlProps . placeholder || gettext ( 'Select an item...' ) ,
2021-06-29 04:03:36 -05:00
... otherProps ,
2022-01-18 07:14:55 -06:00
... props ,
2021-06-29 04:03:36 -05:00
} ;
2022-03-21 02:59:26 -05:00
if ( ! controlProps . creatable ) {
2021-06-29 04:03:36 -05:00
return (
2022-03-21 02:59:26 -05:00
< Select ref = { ref } { ...commonProps } / >
2021-06-29 04:03:36 -05:00
) ;
} else {
return (
2022-04-09 01:19:02 -05:00
< CreatableSelect
ref = { ref }
{ ... commonProps }
noOptionsMessage = { ( ) =>
! controlProps . noDropdown ? 'No options' : null
}
/ >
2021-06-29 04:03:36 -05:00
) ;
}
2022-02-02 03:18:35 -06:00
} ) ;
InputSelect . displayName = 'InputSelect' ;
2021-06-29 04:03:36 -05:00
InputSelect . propTypes = {
cid : PropTypes . string ,
2021-08-23 03:10:14 -05:00
value : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . number , PropTypes . array , PropTypes . bool ] ) ,
2021-06-29 04:03:36 -05:00
options : PropTypes . oneOfType ( [ PropTypes . array , PropTypes . instanceOf ( Promise ) , PropTypes . func ] ) ,
controlProps : PropTypes . object ,
optionsLoaded : PropTypes . func ,
2021-09-09 05:27:17 -05:00
optionsReloadBasis : PropTypes . any ,
2021-06-29 04:03:36 -05:00
onChange : PropTypes . func ,
disabled : PropTypes . bool ,
readonly : PropTypes . bool ,
} ;
export function FormInputSelect ( {
2022-03-21 02:59:26 -05:00
hasError , required , className , label , helpMessage , testcid , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputSelect ref = { props . inputRef } { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputSelect . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
2022-02-02 03:18:35 -06:00
inputRef : CustomPropTypes . ref
2021-06-29 04:03:36 -05:00
} ;
/* React wrapper on color pickr */
2022-03-21 02:59:26 -05:00
export function InputColor ( { value , controlProps , disabled , onChange , currObj } ) {
2021-06-29 04:03:36 -05:00
const pickrOptions = {
showPalette : true ,
allowEmpty : true ,
colorFormat : 'HEX' ,
defaultColor : null ,
position : 'right-middle' ,
clearText : gettext ( 'No color' ) ,
... controlProps ,
disabled : disabled ,
} ;
const eleRef = useRef ( ) ;
const pickrObj = useRef ( ) ;
const classes = useStyles ( ) ;
2022-03-21 02:59:26 -05:00
const setColor = ( newVal ) => {
2021-06-29 04:03:36 -05:00
pickrObj . current &&
2022-01-18 07:14:55 -06:00
pickrObj . current . setColor ( ( _ . isUndefined ( newVal ) || newVal == '' ) ? pickrOptions . defaultColor : newVal ) ;
2021-06-29 04:03:36 -05:00
} ;
2022-03-21 02:59:26 -05:00
const destroyPickr = ( ) => {
if ( pickrObj . current ) {
2021-06-29 04:03:36 -05:00
pickrObj . current . destroy ( ) ;
pickrObj . current = null ;
}
} ;
2022-03-21 02:59:26 -05:00
const initPickr = ( ) => {
2021-06-29 04:03:36 -05:00
/ * p i c k r d o e s n o t h a v e w a y t o u p d a t e o p t i o n s , n e e d t o
destroy and recreate pickr to reflect options * /
destroyPickr ( ) ;
pickrObj . current = new Pickr ( {
el : eleRef . current ,
useAsButton : true ,
theme : 'monolith' ,
swatches : [
'#000' , '#666' , '#ccc' , '#fff' , '#f90' , '#ff0' , '#0f0' ,
'#f0f' , '#f4cccc' , '#fce5cd' , '#d0e0e3' , '#cfe2f3' , '#ead1dc' , '#ea9999' ,
2022-03-21 02:59:26 -05:00
'#b6d7a8' , '#a2c4c9' , '#d5a6bd' , '#e06666' , '#93c47d' , '#76a5af' , '#c27ba0' ,
2021-06-29 04:03:36 -05:00
'#f1c232' , '#6aa84f' , '#45818e' , '#a64d79' , '#bf9000' , '#0c343d' , '#4c1130' ,
] ,
position : pickrOptions . position ,
strings : {
clear : pickrOptions . clearText ,
} ,
components : {
palette : pickrOptions . showPalette ,
preview : true ,
hue : pickrOptions . showPalette ,
interaction : {
clear : pickrOptions . allowEmpty ,
defaultRepresentation : pickrOptions . colorFormat ,
disabled : pickrOptions . disabled ,
} ,
} ,
} ) . on ( 'init' , instance => {
setColor ( value ) ;
disabled && instance . disable ( ) ;
2022-03-21 02:59:26 -05:00
const { lastColor } = instance . getRoot ( ) . preview ;
const { clear } = instance . getRoot ( ) . interaction ;
2021-06-29 04:03:36 -05:00
/* Cycle the keyboard navigation within the color picker */
2022-03-21 02:59:26 -05:00
clear . addEventListener ( 'keydown' , ( e ) => {
if ( e . keyCode === 9 ) {
2021-06-29 04:03:36 -05:00
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
lastColor . focus ( ) ;
}
} ) ;
2022-03-21 02:59:26 -05:00
lastColor . addEventListener ( 'keydown' , ( e ) => {
if ( e . keyCode === 9 && e . shiftKey ) {
2021-06-29 04:03:36 -05:00
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
clear . focus ( ) ;
}
} ) ;
} ) . on ( 'clear' , ( ) => {
onChange && onChange ( '' ) ;
} ) . on ( 'change' , ( color ) => {
onChange && onChange ( color . toHEXA ( ) . toString ( ) ) ;
} ) . on ( 'show' , ( color , instance ) => {
2022-03-21 02:59:26 -05:00
const { palette } = instance . getRoot ( ) . palette ;
2021-06-29 04:03:36 -05:00
palette . focus ( ) ;
} ) . on ( 'hide' , ( instance ) => {
const button = instance . getRoot ( ) . button ;
button . focus ( ) ;
} ) ;
2022-03-21 02:59:26 -05:00
if ( currObj ) {
2021-06-29 04:03:36 -05:00
currObj ( pickrObj . current ) ;
}
} ;
2022-03-21 02:59:26 -05:00
useEffect ( ( ) => {
2021-06-29 04:03:36 -05:00
initPickr ( ) ;
2022-03-21 02:59:26 -05:00
return ( ) => {
2021-06-29 04:03:36 -05:00
destroyPickr ( ) ;
} ;
} , [ ... Object . values ( pickrOptions ) ] ) ;
2022-03-21 02:59:26 -05:00
useEffect ( ( ) => {
if ( pickrObj . current ) {
2021-06-29 04:03:36 -05:00
setColor ( value ) ;
}
} , [ value ] ) ;
2022-03-21 02:59:26 -05:00
let btnStyles = { backgroundColor : value } ;
2021-06-29 04:03:36 -05:00
return (
< PgIconButton ref = { eleRef } title = { gettext ( 'Select the color' ) } className = { classes . colorBtn } style = { btnStyles } disabled = { pickrOptions . disabled }
icon = { ( _ . isUndefined ( value ) || _ . isNull ( value ) || value === '' ) && < CloseIcon / > }
/ >
) ;
}
InputColor . propTypes = {
value : PropTypes . string ,
controlProps : PropTypes . object ,
onChange : PropTypes . func ,
disabled : PropTypes . bool ,
currObj : PropTypes . func ,
} ;
export function FormInputColor ( {
2022-03-21 02:59:26 -05:00
hasError , required , className , label , helpMessage , testcid , ... props } ) {
2021-06-29 04:03:36 -05:00
return (
< FormInput required = { required } label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-21 02:59:26 -05:00
< InputColor { ...props } / >
2021-06-29 04:03:36 -05:00
< / FormInput >
) ;
}
FormInputColor . propTypes = {
hasError : PropTypes . bool ,
required : PropTypes . bool ,
className : CustomPropTypes . className ,
label : PropTypes . string ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
} ;
2022-03-21 02:59:26 -05:00
export function PlainString ( { controlProps , value } ) {
2021-08-10 05:01:16 -05:00
let finalValue = value ;
2022-03-21 02:59:26 -05:00
if ( controlProps ? . formatter ) {
2021-08-10 05:01:16 -05:00
finalValue = controlProps . formatter . fromRaw ( finalValue ) ;
}
return < span > { finalValue } < / span > ;
}
PlainString . propTypes = {
controlProps : PropTypes . object ,
value : PropTypes . any ,
} ;
2022-07-06 01:13:49 -05:00
export function FormNote ( { text , className , controlProps } ) {
2021-07-29 07:36:15 -05:00
const classes = useStyles ( ) ;
2022-07-06 01:13:49 -05:00
/* If raw, then remove the styles and icon */
2021-07-29 07:36:15 -05:00
return (
2021-07-29 08:00:20 -05:00
< Box className = { className } >
2022-07-06 01:13:49 -05:00
< Paper elevation = { 0 } className = { controlProps ? . raw ? '' : classes . noteRoot } >
{ ! controlProps ? . raw && < Box paddingRight = "0.25rem" > < DescriptionIcon fontSize = "small" / > < / Box > }
2021-07-29 08:01:50 -05:00
< Box > { HTMLReactParse ( text || '' ) } < / Box >
2021-07-29 08:00:20 -05:00
< / Paper >
< / Box >
2021-07-29 07:36:15 -05:00
) ;
}
FormNote . propTypes = {
text : PropTypes . string ,
2021-07-29 08:01:50 -05:00
className : CustomPropTypes . className ,
2022-07-06 01:13:49 -05:00
controlProps : PropTypes . object ,
2021-07-29 07:36:15 -05:00
} ;
2021-06-29 04:03:36 -05:00
2022-03-21 02:59:26 -05:00
const useStylesFormFooter = makeStyles ( ( theme ) => ( {
2021-06-29 04:03:36 -05:00
root : {
padding : theme . spacing ( 0.5 ) ,
position : 'absolute' ,
bottom : 0 ,
left : 0 ,
right : 0 ,
2021-07-15 04:34:11 -05:00
zIndex : 10 ,
2021-06-29 04:03:36 -05:00
} ,
container : {
borderWidth : '1px' ,
borderStyle : 'solid' ,
borderRadius : theme . shape . borderRadius ,
padding : theme . spacing ( 0.5 ) ,
display : 'flex' ,
alignItems : 'center' ,
} ,
containerSuccess : {
borderColor : theme . palette . success . main ,
backgroundColor : theme . palette . success . light ,
} ,
iconSuccess : {
color : theme . palette . success . main ,
} ,
containerError : {
borderColor : theme . palette . error . main ,
backgroundColor : theme . palette . error . light ,
} ,
iconError : {
color : theme . palette . error . main ,
} ,
2021-12-02 04:35:52 -06:00
containerInfo : {
borderColor : theme . palette . primary . main ,
backgroundColor : theme . palette . primary . light ,
} ,
iconInfo : {
color : theme . palette . primary . main ,
} ,
containerWarning : {
borderColor : theme . palette . warning . main ,
backgroundColor : theme . palette . warning . light ,
} ,
iconWarning : {
color : theme . palette . warning . main ,
} ,
2021-06-29 04:03:36 -05:00
message : {
marginLeft : theme . spacing ( 0.5 ) ,
} ,
2022-07-19 04:57:47 -05:00
messageCenter : {
margin : 'auto' ,
} ,
2021-06-29 04:03:36 -05:00
closeButton : {
marginLeft : 'auto' ,
} ,
} ) ) ;
/* The form footer used mostly for showing error */
2022-04-07 07:06:56 -05:00
export function FormFooterMessage ( { style , ... props } ) {
2021-06-29 04:03:36 -05:00
const classes = useStylesFormFooter ( ) ;
2022-03-21 02:59:26 -05:00
if ( ! props . message ) {
2021-06-29 04:03:36 -05:00
return < > < / > ;
}
return (
2022-04-07 07:06:56 -05:00
< Box className = { classes . root } style = { style } >
2021-12-02 04:35:52 -06:00
< NotifierMessage { ...props } > < / NotifierMessage >
2021-06-29 04:03:36 -05:00
< / Box >
) ;
}
FormFooterMessage . propTypes = {
2022-04-07 07:06:56 -05:00
style : PropTypes . object ,
2021-12-02 04:35:52 -06:00
message : PropTypes . string ,
} ;
2022-03-21 02:59:26 -05:00
const useStylesKeyboardShortcut = makeStyles ( ( ) => ( {
customRow : {
paddingTop : 5
}
} ) ) ;
2022-03-23 02:58:35 -05:00
export function FormInputKeyboardShortcut ( { hasError , label , className , helpMessage , onChange , ... props } ) {
2022-03-21 02:59:26 -05:00
const classes = useStylesKeyboardShortcut ( ) ;
return (
2022-03-23 02:58:35 -05:00
< FormInput label = { label } error = { hasError } className = { clsx ( classes . customRow , className ) } helpMessage = { helpMessage } >
< KeyboardShortcuts onChange = { onChange } { ...props } / >
2022-03-21 02:59:26 -05:00
< / FormInput >
) ;
}
FormInputKeyboardShortcut . propTypes = {
hasError : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
onChange : PropTypes . func
} ;
export function FormInputQueryThreshold ( { hasError , label , className , helpMessage , testcid , onChange , ... props } ) {
const cid = _ . uniqueId ( 'c' ) ;
const helpid = ` h ${ cid } ` ;
return (
< FormInput label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
< QueryThresholds cid = { cid } helpid = { helpid } onChange = { onChange } { ...props } / >
< / FormInput >
) ;
}
FormInputQueryThreshold . propTypes = {
hasError : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
onChange : PropTypes . func
} ;
2022-03-23 02:58:35 -05:00
export function FormInputSelectThemes ( { hasError , label , className , helpMessage , testcid , onChange , ... props } ) {
2022-03-21 02:59:26 -05:00
const cid = _ . uniqueId ( 'c' ) ;
const helpid = ` h ${ cid } ` ;
return (
< FormInput label = { label } error = { hasError } className = { className } helpMessage = { helpMessage } testcid = { testcid } >
2022-03-23 02:58:35 -05:00
< SelectThemes cid = { cid } helpid = { helpid } onChange = { onChange } { ...props } / >
2022-03-21 02:59:26 -05:00
< / FormInput >
) ;
}
2022-03-23 02:58:35 -05:00
FormInputSelectThemes . propTypes = {
2022-03-21 02:59:26 -05:00
hasError : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
testcid : PropTypes . string ,
onChange : PropTypes . func
} ;
2022-07-19 04:57:47 -05:00
export function NotifierMessage ( { type = MESSAGE _TYPE . SUCCESS , message , closable = true , showIcon = true , textCenter = false , onClose = ( ) => { /*This is intentional (SonarQube)*/ } } ) {
2021-12-02 04:35:52 -06:00
const classes = useStylesFormFooter ( ) ;
return (
< Box className = { clsx ( classes . container , classes [ ` container ${ type } ` ] ) } >
2022-07-19 04:57:47 -05:00
{ showIcon && < FormIcon type = { type } className = { classes [ ` icon ${ type } ` ] } / > }
< Box className = { textCenter ? classes . messageCenter : classes . message } > { HTMLReactParse ( message || '' ) } < / Box >
2021-12-02 04:35:52 -06:00
{ closable && < IconButton className = { clsx ( classes . closeButton , classes [ ` icon ${ type } ` ] ) } onClick = { onClose } >
2022-03-21 02:59:26 -05:00
< FormIcon close = { true } / >
2021-12-02 04:35:52 -06:00
< / IconButton > }
< / Box >
) ;
}
NotifierMessage . propTypes = {
2021-06-29 04:03:36 -05:00
type : PropTypes . oneOf ( Object . values ( MESSAGE _TYPE ) ) . isRequired ,
message : PropTypes . string ,
closable : PropTypes . bool ,
2022-07-19 04:57:47 -05:00
showIcon : PropTypes . bool ,
textCenter : PropTypes . bool ,
2021-06-29 04:03:36 -05:00
onClose : PropTypes . func ,
} ;
2022-06-15 00:52:42 -05:00
export function FormButton ( { required , label ,
className , helpMessage , onClick , disabled , ... props } ) {
return (
< FormInput required = { required } label = { label } className = { className } helpMessage = { helpMessage } >
< PrimaryButton onClick = { onClick } disabled = { disabled } > { gettext ( props . btnName ) } < / PrimaryButton >
< / FormInput >
) ;
}
FormButton . propTypes = {
required : PropTypes . bool ,
label : PropTypes . string ,
className : CustomPropTypes . className ,
helpMessage : PropTypes . string ,
onClick : PropTypes . func ,
disabled : PropTypes . bool ,
btnName : PropTypes . string
} ;