feat(ui-components) Add Input Component (#11882)
This commit is contained in:
parent
0a385c561d
commit
c5115f99f0
|
@ -5,6 +5,7 @@ import React from 'react';
|
|||
import Icon from '../../icons/components/Icon';
|
||||
import { BUTTON_TYPES } from '../../react/constants';
|
||||
import { withPixelLineHeight } from '../../styles/functions.web';
|
||||
import { Theme } from '../../ui/types';
|
||||
|
||||
import { ButtonProps } from './types';
|
||||
|
||||
|
@ -31,7 +32,7 @@ interface IButtonProps extends ButtonProps {
|
|||
size?: 'small' | 'medium' | 'large';
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: any) => {
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
return {
|
||||
button: {
|
||||
backgroundColor: theme.palette.action01,
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
import { makeStyles } from '@material-ui/core';
|
||||
import clsx from 'clsx';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { isMobileBrowser } from '../../environment/utils';
|
||||
import Icon from '../../icons/components/Icon';
|
||||
import { IconCloseCircle } from '../../icons/svg/index';
|
||||
import { withPixelLineHeight } from '../../styles/functions.web';
|
||||
import { Theme } from '../../ui/types';
|
||||
|
||||
import { InputProps } from './types';
|
||||
|
||||
interface IInputProps extends InputProps {
|
||||
bottomLabel?: string;
|
||||
className?: string;
|
||||
type?: 'text' | 'email' | 'number' | 'password';
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
return {
|
||||
inputContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
label: {
|
||||
color: theme.palette.text01,
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
marginBottom: `${theme.spacing(2)}px`,
|
||||
|
||||
'&.is-mobile': {
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
|
||||
}
|
||||
},
|
||||
|
||||
fieldContainer: {
|
||||
position: 'relative',
|
||||
display: 'flex'
|
||||
},
|
||||
|
||||
input: {
|
||||
backgroundColor: theme.palette.ui03,
|
||||
color: theme.palette.text01,
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
padding: '10px 16px',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: 0,
|
||||
height: '40px',
|
||||
boxSizing: 'border-box',
|
||||
width: '100%',
|
||||
|
||||
'&::placeholder': {
|
||||
color: theme.palette.text02
|
||||
},
|
||||
|
||||
'&:focus': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
color: theme.palette.text03
|
||||
},
|
||||
|
||||
'&.is-mobile': {
|
||||
height: '48px',
|
||||
padding: '13px 16px',
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
|
||||
}
|
||||
},
|
||||
|
||||
icon: {
|
||||
position: 'absolute',
|
||||
top: '10px',
|
||||
left: '16px'
|
||||
},
|
||||
|
||||
iconInput: {
|
||||
paddingLeft: '46px'
|
||||
},
|
||||
|
||||
clearableInput: {
|
||||
paddingRight: '46px'
|
||||
},
|
||||
|
||||
clearButton: {
|
||||
position: 'absolute',
|
||||
right: '16px',
|
||||
top: '10px',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: theme.palette.action03,
|
||||
border: 0,
|
||||
padding: 0
|
||||
},
|
||||
|
||||
bottomLabel: {
|
||||
marginTop: `${theme.spacing(2)}px`,
|
||||
...withPixelLineHeight(theme.typography.labelRegular),
|
||||
color: theme.palette.text02,
|
||||
|
||||
'&.is-mobile': {
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular)
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
color: theme.palette.textError
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const Input = ({
|
||||
bottomLabel,
|
||||
className,
|
||||
clearable = false,
|
||||
disabled,
|
||||
error,
|
||||
icon,
|
||||
label,
|
||||
onChange,
|
||||
placeholder,
|
||||
type = 'text',
|
||||
value
|
||||
}: IInputProps) => {
|
||||
const styles = useStyles();
|
||||
const isMobile = isMobileBrowser();
|
||||
|
||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
onChange(e.target.value), []);
|
||||
|
||||
const clearInput = useCallback(() => onChange(''), []);
|
||||
|
||||
return (<div className = { clsx(styles.inputContainer, className) }>
|
||||
{label && <span className = { clsx(styles.label, isMobile && 'is-mobile') }>{label}</span>}
|
||||
<div className = { styles.fieldContainer }>
|
||||
{icon && <Icon
|
||||
className = { styles.icon }
|
||||
size = { 20 }
|
||||
src = { icon } />}
|
||||
<input
|
||||
className = { clsx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && styles.iconInput) }
|
||||
disabled = { disabled }
|
||||
onChange = { handleChange }
|
||||
placeholder = { placeholder }
|
||||
type = { type }
|
||||
value = { value } />
|
||||
{clearable && !disabled && value !== '' && <button className = { styles.clearButton }>
|
||||
<Icon
|
||||
onClick = { clearInput }
|
||||
size = { 20 }
|
||||
src = { IconCloseCircle } />
|
||||
</button>}
|
||||
</div>
|
||||
{bottomLabel && (
|
||||
<span className = { clsx(styles.bottomLabel, isMobile && 'is-mobile', error && 'error') }>
|
||||
{bottomLabel}
|
||||
</span>
|
||||
)}
|
||||
</div>);
|
||||
};
|
||||
|
||||
export default Input;
|
|
@ -27,3 +27,46 @@ export interface ButtonProps {
|
|||
*/
|
||||
type?: BUTTON_TYPES;
|
||||
}
|
||||
|
||||
export interface InputProps {
|
||||
|
||||
/**
|
||||
* Whether the input is be clearable. (show clear button).
|
||||
*/
|
||||
clearable?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the input is be disabled.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the input is in error state.
|
||||
*/
|
||||
error?: boolean;
|
||||
|
||||
/**
|
||||
* The icon to be displayed on the input.
|
||||
*/
|
||||
icon?: Function;
|
||||
|
||||
/**
|
||||
* The label of the input.
|
||||
*/
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* Change callback.
|
||||
*/
|
||||
onChange: (value: string) => void;
|
||||
|
||||
/**
|
||||
* The input placeholder text.
|
||||
*/
|
||||
placeholder?: string;
|
||||
|
||||
/**
|
||||
* The value of the input.
|
||||
*/
|
||||
value: string | number;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// @flow
|
||||
|
||||
// @ts-ignore
|
||||
import Platform from '../react/Platform';
|
||||
|
||||
/**
|
||||
|
@ -28,8 +27,8 @@ export function isIosMobileBrowser() {
|
|||
*
|
||||
* @returns {Promise[]}
|
||||
*/
|
||||
export function checkChromeExtensionsInstalled(config: Object = {}) {
|
||||
const isExtensionInstalled = info => new Promise(resolve => {
|
||||
export function checkChromeExtensionsInstalled(config: any = {}) {
|
||||
const isExtensionInstalled = (info: any) => new Promise(resolve => {
|
||||
const img = new Image();
|
||||
|
||||
img.src = `chrome-extension://${info.id}/${info.path}`;
|
||||
|
@ -41,9 +40,9 @@ export function checkChromeExtensionsInstalled(config: Object = {}) {
|
|||
resolve(false);
|
||||
};
|
||||
});
|
||||
const extensionInstalledFunction = info => isExtensionInstalled(info);
|
||||
const extensionInstalledFunction = (info: any) => isExtensionInstalled(info);
|
||||
|
||||
return Promise.all(
|
||||
(config.chromeExtensionsInfo || []).map(info => extensionInstalledFunction(info))
|
||||
(config.chromeExtensionsInfo || []).map((info: any) => extensionInstalledFunction(info))
|
||||
);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
NativeSyntheticEvent,
|
||||
StyleProp,
|
||||
Text,
|
||||
TextInput,
|
||||
TextInputChangeEventData,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewStyle
|
||||
} from 'react-native';
|
||||
|
||||
import { InputProps } from '../../../components/common/types';
|
||||
import Icon from '../../../icons/components/Icon';
|
||||
import { IconCloseCircle } from '../../../icons/svg';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import BaseTheme from '../../../ui/components/BaseTheme.native';
|
||||
|
||||
import styles from './inputStyles';
|
||||
|
||||
interface IInputProps extends InputProps {
|
||||
|
||||
/**
|
||||
* Custom styles to be applied to the component.
|
||||
*/
|
||||
customStyles?: CustomStyles;
|
||||
}
|
||||
|
||||
interface CustomStyles {
|
||||
container?: Object;
|
||||
input?: Object;
|
||||
}
|
||||
|
||||
const Input = ({
|
||||
clearable,
|
||||
customStyles,
|
||||
disabled,
|
||||
error,
|
||||
icon,
|
||||
label,
|
||||
onChange,
|
||||
placeholder,
|
||||
value
|
||||
}: IInputProps) => {
|
||||
const [ focused, setFocused ] = useState(false);
|
||||
const handleChange = useCallback((e: NativeSyntheticEvent<TextInputChangeEventData>) => {
|
||||
const { nativeEvent: { text } } = e;
|
||||
|
||||
onChange(text);
|
||||
}, []);
|
||||
|
||||
const clearInput = useCallback(() => {
|
||||
onChange('');
|
||||
}, []);
|
||||
|
||||
const blur = useCallback(() => {
|
||||
setFocused(false);
|
||||
}, []);
|
||||
|
||||
const focus = useCallback(() => {
|
||||
setFocused(true);
|
||||
}, []);
|
||||
|
||||
return (<View style = { [ styles.inputContainer, customStyles?.container ] }>
|
||||
{label && <Text style = { styles.label }>{label}</Text>}
|
||||
<View style = { styles.fieldContainer as StyleProp<ViewStyle> }>
|
||||
{icon && <Icon
|
||||
size = { 22 }
|
||||
src = { icon }
|
||||
style = { styles.icon } />}
|
||||
<TextInput
|
||||
editable = { !disabled }
|
||||
onBlur = { blur }
|
||||
onChange = { handleChange }
|
||||
onFocus = { focus }
|
||||
placeholder = { placeholder }
|
||||
placeholderTextColor = { BaseTheme.palette.text02 }
|
||||
style = { [ styles.input,
|
||||
disabled && styles.inputDisabled,
|
||||
clearable && styles.clearableInput,
|
||||
icon && styles.iconInput,
|
||||
focused && styles.inputFocused,
|
||||
error && styles.inputError,
|
||||
customStyles?.input
|
||||
] }
|
||||
value = { `${value}` } />
|
||||
{clearable && !disabled && value !== '' && (
|
||||
<TouchableOpacity
|
||||
onPress = { clearInput }
|
||||
style = { styles.clearButton as StyleProp<ViewStyle> }>
|
||||
<Icon
|
||||
size = { 22 }
|
||||
src = { IconCloseCircle }
|
||||
style = { styles.clearIcon } />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>);
|
||||
};
|
||||
|
||||
export default Input;
|
|
@ -0,0 +1,74 @@
|
|||
// @ts-ignore
|
||||
import BaseTheme from '../../../ui/components/BaseTheme.native';
|
||||
|
||||
export default {
|
||||
inputContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
label: {
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
lineHeight: 0,
|
||||
color: BaseTheme.palette.text01,
|
||||
marginBottom: 8
|
||||
},
|
||||
|
||||
fieldContainer: {
|
||||
position: 'relative'
|
||||
},
|
||||
|
||||
icon: {
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
top: 13,
|
||||
left: 16
|
||||
},
|
||||
|
||||
input: {
|
||||
backgroundColor: BaseTheme.palette.ui03,
|
||||
color: BaseTheme.palette.text01,
|
||||
paddingVertical: 13,
|
||||
paddingHorizontal: BaseTheme.spacing[3],
|
||||
borderRadius: BaseTheme.shape.borderRadius,
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
lineHeight: 0,
|
||||
height: 48,
|
||||
borderWidth: 2,
|
||||
borderColor: BaseTheme.palette.ui03
|
||||
},
|
||||
|
||||
inputDisabled: {
|
||||
color: BaseTheme.palette.text03
|
||||
},
|
||||
|
||||
inputFocused: {
|
||||
borderColor: BaseTheme.palette.focus01
|
||||
},
|
||||
|
||||
inputError: {
|
||||
borderColor: BaseTheme.palette.textError
|
||||
},
|
||||
|
||||
iconInput: {
|
||||
paddingLeft: BaseTheme.spacing[6]
|
||||
},
|
||||
|
||||
clearableInput: {
|
||||
paddingRight: BaseTheme.spacing[6]
|
||||
},
|
||||
|
||||
clearButton: {
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 13,
|
||||
width: 40,
|
||||
height: 48
|
||||
},
|
||||
|
||||
clearIcon: {
|
||||
color: BaseTheme.palette.icon01
|
||||
}
|
||||
};
|
|
@ -8,14 +8,13 @@ import { translate } from '../../../base/i18n';
|
|||
import { Icon, IconInviteMore } from '../../../base/icons';
|
||||
import { getLocalParticipant, getParticipantCountWithFake, getRemoteParticipants } from '../../../base/participants';
|
||||
import Button from '../../../base/react/components/native/Button';
|
||||
import Input from '../../../base/react/components/native/Input';
|
||||
import { BUTTON_TYPES } from '../../../base/react/constants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
import { getBreakoutRooms, getCurrentRoomId } from '../../../breakout-rooms/functions';
|
||||
import { doInvitePeople } from '../../../invite/actions.native';
|
||||
import { participantMatchesSearch, shouldRenderInviteButton } from '../../functions';
|
||||
|
||||
import ClearableInput from './ClearableInput';
|
||||
import CollapsibleList from './CollapsibleList';
|
||||
import MeetingParticipantItem from './MeetingParticipantItem';
|
||||
import styles from './styles';
|
||||
|
@ -235,10 +234,13 @@ class MeetingParticipantList extends PureComponent<Props> {
|
|||
style = { styles.inviteButton }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
}
|
||||
<ClearableInput
|
||||
<Input
|
||||
clearable = { true }
|
||||
customStyles = {{ container: styles.inputContainer,
|
||||
input: styles.centerInput }}
|
||||
onChange = { this._onSearchStringChange }
|
||||
placeholder = { t('participantsPane.search') }
|
||||
selectionColor = { BaseTheme.palette.text01 } />
|
||||
value = { this.props.searchString } />
|
||||
<FlatList
|
||||
bounces = { false }
|
||||
data = { [ _localParticipant?.id, ..._sortedRemoteParticipants ] }
|
||||
|
|
|
@ -310,5 +310,16 @@ export default {
|
|||
paddingLeft: BaseTheme.spacing[3],
|
||||
paddingRight: BaseTheme.spacing[3],
|
||||
fontSize: 16
|
||||
},
|
||||
|
||||
inputContainer: {
|
||||
marginLeft: BaseTheme.spacing[3],
|
||||
marginRight: BaseTheme.spacing[3],
|
||||
marginBottom: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
centerInput: {
|
||||
paddingRight: BaseTheme.spacing[3],
|
||||
textAlign: 'center'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { Icon, IconCloseSolid } from '../../../base/icons';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* String for html autocomplete attribute.
|
||||
*/
|
||||
autoComplete?: string,
|
||||
|
||||
/**
|
||||
* If the input should be focused on display.
|
||||
*/
|
||||
autoFocus?: boolean,
|
||||
|
||||
/**
|
||||
* Class name to be appended to the default class list.
|
||||
*/
|
||||
className?: string,
|
||||
|
||||
/**
|
||||
* Input id.
|
||||
*/
|
||||
id?: string,
|
||||
|
||||
/**
|
||||
* Callback for the onChange event of the field.
|
||||
*/
|
||||
onChange: Function,
|
||||
|
||||
/**
|
||||
* Callback to be used when the user hits Enter in the field.
|
||||
*/
|
||||
onSubmit?: Function,
|
||||
|
||||
/**
|
||||
* Placeholder text for the field.
|
||||
*/
|
||||
placeholder: string,
|
||||
|
||||
/**
|
||||
* The field type (e.g. Text, password...etc).
|
||||
*/
|
||||
type?: string,
|
||||
|
||||
/**
|
||||
* TestId of the button. Can be used to locate element when testing UI.
|
||||
*/
|
||||
testId?: string,
|
||||
|
||||
/**
|
||||
* Externally provided value.
|
||||
*/
|
||||
value?: string
|
||||
};
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
clearableInput: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
height: '20px',
|
||||
border: `1px solid ${theme.palette.ui05}`,
|
||||
backgroundColor: theme.palette.uiBackground,
|
||||
position: 'relative',
|
||||
borderRadius: '6px',
|
||||
padding: '10px 16px',
|
||||
|
||||
'&.focused': {
|
||||
outline: `3px solid ${theme.palette.field01Focus}`
|
||||
}
|
||||
},
|
||||
clearButton: {
|
||||
backgroundColor: 'transparent',
|
||||
border: 0,
|
||||
position: 'absolute',
|
||||
right: '10px',
|
||||
top: '11px',
|
||||
padding: 0,
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.icon02
|
||||
}
|
||||
},
|
||||
input: {
|
||||
backgroundColor: 'transparent',
|
||||
border: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: '6px',
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
textAlign: 'center',
|
||||
caretColor: theme.palette.text01,
|
||||
color: theme.palette.text01,
|
||||
|
||||
'&::placeholder': {
|
||||
color: theme.palette.text03
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Implements a pre-styled clearable input field.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function ClearableInput({
|
||||
autoFocus = false,
|
||||
autoComplete,
|
||||
className = '',
|
||||
id,
|
||||
onChange,
|
||||
onSubmit,
|
||||
placeholder,
|
||||
testId,
|
||||
type = 'text',
|
||||
value
|
||||
}: Props) {
|
||||
const classes = useStyles();
|
||||
const [ val, setVal ] = useState(value || '');
|
||||
const [ focused, setFocused ] = useState(false);
|
||||
const inputRef = React.createRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (value && value !== val) {
|
||||
setVal(value);
|
||||
}
|
||||
}, [ value ]);
|
||||
|
||||
|
||||
/**
|
||||
* Callback for the onBlur event of the field.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const _onBlur = useCallback(() => {
|
||||
setFocused(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Callback for the onChange event of the field.
|
||||
*
|
||||
* @param {Object} evt - The static event.
|
||||
* @returns {void}
|
||||
*/
|
||||
const _onChange = useCallback(evt => {
|
||||
const newValue = evt.target.value;
|
||||
|
||||
setVal(newValue);
|
||||
onChange && onChange(newValue);
|
||||
}, [ onChange ]);
|
||||
|
||||
/**
|
||||
* Callback for the onFocus event of the field.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const _onFocus = useCallback(() => {
|
||||
setFocused(true);
|
||||
});
|
||||
|
||||
/**
|
||||
* Joins the conference on 'Enter'.
|
||||
*
|
||||
* @param {Event} event - Key down event object.
|
||||
* @returns {void}
|
||||
*/
|
||||
const _onKeyDown = useCallback(event => {
|
||||
onSubmit && event.key === 'Enter' && onSubmit();
|
||||
}, [ onSubmit ]);
|
||||
|
||||
/**
|
||||
* Clears the input.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const _clearInput = useCallback(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
setVal('');
|
||||
onChange && onChange('');
|
||||
}, [ onChange ]);
|
||||
|
||||
return (
|
||||
<div className = { `${classes.clearableInput} ${focused ? 'focused' : ''} ${className || ''}` }>
|
||||
<input
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { classes.input }
|
||||
data-testid = { testId ? testId : undefined }
|
||||
id = { id }
|
||||
onBlur = { _onBlur }
|
||||
onChange = { _onChange }
|
||||
onFocus = { _onFocus }
|
||||
onKeyDown = { _onKeyDown }
|
||||
placeholder = { placeholder }
|
||||
ref = { inputRef }
|
||||
type = { type }
|
||||
value = { val } />
|
||||
{val !== '' && (
|
||||
<button
|
||||
className = { classes.clearButton }
|
||||
onClick = { _clearInput }>
|
||||
<Icon
|
||||
size = { 20 }
|
||||
src = { IconCloseSolid } />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ClearableInput;
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { rejectParticipantAudio } from '../../../av-moderation/actions';
|
||||
import Input from '../../../base/components/common/Input';
|
||||
import useContextMenu from '../../../base/components/context-menu/useContextMenu';
|
||||
import participantsPaneTheme from '../../../base/components/themes/participantsPaneTheme.json';
|
||||
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
|
||||
|
@ -22,7 +23,6 @@ import { muteRemote } from '../../../video-menu/actions.any';
|
|||
import { getSortedParticipantIds, shouldRenderInviteButton } from '../../functions';
|
||||
import { useParticipantDrawer } from '../../hooks';
|
||||
|
||||
import ClearableInput from './ClearableInput';
|
||||
import { InviteButton } from './InviteButton';
|
||||
import MeetingParticipantContextMenu from './MeetingParticipantContextMenu';
|
||||
import MeetingParticipantItems from './MeetingParticipantItems';
|
||||
|
@ -39,6 +39,13 @@ const useStyles = makeStyles(theme => {
|
|||
...theme.typography.labelButtonLarge,
|
||||
lineHeight: `${theme.typography.labelButtonLarge.lineHeight}px`
|
||||
}
|
||||
},
|
||||
|
||||
search: {
|
||||
'& input': {
|
||||
textAlign: 'center',
|
||||
paddingRight: '16px'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -107,7 +114,9 @@ function MeetingParticipants({
|
|||
: t('participantsPane.headings.participantsList', { count: participantsCount })}
|
||||
</div>
|
||||
{showInviteButton && <InviteButton />}
|
||||
<ClearableInput
|
||||
<Input
|
||||
className = { styles.search }
|
||||
clearable = { true }
|
||||
onChange = { setSearchString }
|
||||
placeholder = { t('participantsPane.search') }
|
||||
value = { searchString } />
|
||||
|
|
Loading…
Reference in New Issue