Prevent Menu From Closing When User Clicks the Input - css

Is there a way to make it so that the <Input /> doesn't close on click? Currently, if I click inside the input, the menu just closes. I have the same setup as the original poster. I've tried playing around with closeOnSelect="false" and that didn't seem to work. Any advice would be greatly appreciated.
Here is the example in CodeSandbox form:
https://codesandbox.io/s/chakra-menuitem-as-input-forked-9ue4n
import {
Box,
Button,
ChakraProvider,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
useMenuItem,
} from '#chakra-ui/react';
import React from 'react';
const navigationKeys = ['ArrowUp', 'ArrowDown', 'Escape'];
const MenuInput = props => {
const { role, ...rest } = useMenuItem(props);
return (
<Box px="3" role={role}>
<Input
placeholder="Enter value"
size="sm"
{...rest}
onKeyDown={e => {
if (!navigationKeys.includes(e.key)) {
e.stopPropagation();
}
}}
/>
</Box>
);
};
function App() {
return (
<ChakraProvider>
<Menu>
<MenuButton as={Button}>Button</MenuButton>
<MenuList>
<MenuInput />
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>
</ChakraProvider>
);
}
export default App;

Add closeOnSelect={false} to the menu component to stop the menu closing when clicking on the MenuInput.
<Menu closeOnSelect={false}>
<MenuButton as={Button}>Button</MenuButton>
<MenuList>
<MenuInput />
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>

Related

How to have multiple row tabs in Material UI Tabs

I have almost 30 tabs inside Material UI Tabs, the user has to scroll two times to see all the tabs, I would like to show the tabs in two rows with scroll instead of one row with scroll, this will help the user to see most of the tabs in one glance.
How can I do something like this ?, I looked over Material UI document but i couldn't find anything useful, I tried manually giving it CSS style but I wasn't able to achieve my goal (my CSS skills are mediocre).
To show what I mean by multiple row tabs, here is a sample image :
Any help is much appreciated.
I did a little hack:
Use 2 different Tabs components and adjust indexes:
<Box sx={{ display: 'flex',justifyContent: 'center', flexWrap: 'wrap'}}>
<Tabs value={value} onChange={handleChange}>
<Tab label='Precios'/>
<Tab label='Usuarios'/>
<Tab label='Plan'/>
</Tabs>
<Tabs value={value - 3 } onChange={handleChange2}>
<Tab label='Empleados'/>
</Tabs>
</Box>
And manage change for this adjustment:
const handleChange = (event, newValue) => {
setValue(newValue);
};
const handleChange2 = (event, newValue) => {
setValue(newValue + 3);
};
You just change the number 3 for the number of tabs in your first component.
Saludos
I am struggling with similar problem. I have gone throw the documentation and looks like this is not possible using Tabs/Tab features. For now I can see two options:
Use properties variant="scrollable" and scrollButtons="auto" like mentioned above. This will not give you what you expected but at least it is working.
Implement your own tabs. You can use Grid for it. Below example is just to show an approach. It is not redy solution, but it can be easily adjusted.
const useStyles = makeStyles((theme) => ({
navigationLinkContainer: {
// up to you
},
navigationLinkButtonActive: {
color: '#ffffff',
// up to you
},
}));
const NavigationLink = (props) => {
const classes = useStyles();
return (
<Grid item className={classes.navigationLinkContainer}>
<Button
component={Link}
onClick={props.onClick}
>
{props.children}
</Button>
</Grid>
);
};
const NavigationHeaders = (props) => {
const classes = useStyles();
const { headers, className } = props;
const [activeTab, setActiveTab] = React.useState('');
const isActive = (headerId) => headerId === activeTab;
return (
<>
<Grid container >
{headers.map((header) => (
<NavigationLink
className={classnames(isActive(header.id) && classes.navigationLinkButtonActive)}
key={header.id}
onClick={() => setActiveTab(header.id)}
>
{header.title}
</NavigationLink>
))}
</Grid>
{/* some content here shown base on activeTab */}
</>
);
};
I also came to the conclusion that this is not currently possible with Tabs/Tab. I tried using <br />, <hr />, <Divider />, functions to insert breaks, making multiple rows of tabs (which messed up selection), wrapping Tabs in span with max-width (also messed up selection), you name it. I ultimately decided to use scroll on small screens.
I figured out the smallest screen size that would show my tabs properly, then used scroll for any smaller.
const mql = window.matchMedia('(max-width: 2000px)');
const smallScreen = mql.matches;
<Tabs
value={tabValue}
onChange={handleTabChange}
orientation="horizontal"
variant={smallScreen ? 'scrollable' : 'standard'}
centered={!smallScreen}
>
<Tab label="1" />
<Tab label="1" />
<Tab label="3" />
<Tab label="4" />
<Tab label="5" />
</Tabs>
You could add an event handler to change on resize, but was not necessary for my use case
You can set flexWrap: 'wrap' in the tab container component which is a flexbox:
<Tabs
// disable the tab indicator because it doesn't work well with wrapped container
TabIndicatorProps={{ sx: { display: 'none' } }}
sx={{
'& .MuiTabs-flexContainer': {
flexWrap: 'wrap',
},
}}
{...}
>
https://codesandbox.io/s/69733826-material-ui-responsive-tabs-5q57p?file=/demo.js
Try going through the doc of any stuff you use to safe unnecessary problems in future
visit this for more details and full code https://material-ui.com/components/tabs/
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value}
onChange={handleChange}
aria-label="simple tabs example"
indicatorColor="primary"
textColor="primary"
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs example"
>
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</div>
Edit: please notice the variant in Tabs

React how to use icon inside Textfield Material-UI with TypeScript

I have designed a form with validation using TypeScript Material UI and Formik. I want a material UI Icon to appear in my textfield area, here's my code:
import React from 'react'
import { Formik, Form, FieldAttributes,useField} from 'formik'
import { TextField } from '#material-ui/core'
import CalendarTodayIcon from '#material-ui/icons/CalendarToday'
import * as yup from 'yup'
import './MainInfo.css'
const MyTextField: React.FC<FieldAttributes<{}>> = ({
placeholder,type,className,style,
...props
}) => {
const [field, meta] = useField<{}>(props);
const errorText = meta.error && meta.touched ? meta.error : "";
return (
<div className='container'>
<TextField
placeholder={placeholder}
className={className}
style={style}
type={type}
{...field}
helperText={errorText}
error={!!errorText}
id="outlined-basic"
variant="outlined"
/>
</div>
);
};
export function MainInfo() {
return (
<div>
<Formik
validateOnChange={true} validationSchema={validationSchema} initialValues={{ Title: '', ActivationDate: '', ExpirationDate: '', DirectManager: '', HRBP: '' }} onSubmit={(data) => {
console.log(data)
}}
>
{({values, errors}) => (
<Form id='my-form' >
<div>
<label className='label'>عنوان</label>
<div >
<MyTextField style={{width:'60%'}} placeholder='طراح' name='Title' type='input' />
</div>
...
</div>
</Form>
)}
</Formik>
</div>
)
}
but the problem is that I can not add a new Icon property or InputProp since <FieldAttributes<{}>> doesnt accept it. how can I define a new property for the FieldAttributes or fix this issue?
Use the TextField Props InputProps to customize the input field
And use startAdornment, endAdornment to customize the prefix/suffix
Finally use icon inside InputAdornment would be fine
import { TextField, InputAdornment } from "#material-ui/core";
import ExpandLess from "#material-ui/icons/ExpandLess";
import ExpandMore from "#material-ui/icons/ExpandMore";
<TextField
id="standard-basic"
label="Standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<ExpandLess />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
<ExpandMore />
</InputAdornment>
)
}}
/>
Refer:
MUI TextField Props API: InputProps, startAdornment, endAdornment
MUI InputInputAdornment Props API
online demo: https://stackblitz.com/edit/gzlbzm

Trouble calling useStyles in components (Material-UI)

Currently doing a project with Material-UI's library. Im having issues with styling.
I'm pretty new to ReactJS or just JS in general, and doing this project is my first time I am interacting with it.
Any help will be greatly appreciated!
I am unable to style my text box like the way example they gave.
this is my code:
class AppBase extends Component {
constructor(props) {
super(props);
this.state = {
...INITIAL_STATE
};
}
classes = useStyles;
render() {
return (
<Container component="main" maxWidth="md">
<div className={this.classes.root}>
<TextField required id="standard-required" label="Required" defaultValue="Hello World" />
<TextField disabled id="standard-disabled" label="Disabled" defaultValue="Hello World" />
<TextField
id="standard-password-input"
label="Password"
type="password"
autoComplete="current-password"
/>
<TextField
id="standard-read-only-input"
label="Read Only"
defaultValue="Hello World"
InputProps={{
readOnly: true,
}}
/>
<TextField
id="standard-number"
label="Number"
type="number"
InputLabelProps={{
shrink: true,
}}
/>
<TextField id="standard-search" label="Search field" type="search" />
<TextField
id="standard-helperText"
label="Helper text"
defaultValue="Default Value"
helperText="Some important text"
/>
</div>
</Container>
);
}
}
This example I have taken is from: https://codesandbox.io/s/51hrm
I have copied the useStyles exactly at the top, and this is what I get:
How the code looks like
This is not the only instance where I am facing this issue with calling useStyles in my components.
Calling 'classes = useStyles;' works on some useStyles but not the others, I am really at lost for this.
Because of the way the other pages has been done, I will not consider using Functions like the example given.
Thank you in advance!
If you're copying the useStyles function from the example provide, then it needs to be called as a function. Right now, you're just assigning the function to the classes variable, but never executing the function. So just change the one line of code as follows:
const classes = useStyles();
And you will execute the useStyles function and assign the result to the classes variable. At that point, you'll be able to use the classes constant in your jsx to style the components, like the example:
<form className={classes.root} ...></form>
useStyles hook can only be called inside a functional component. You have to change your class component to functional component if you want to use useStyles hook.
I have created a live sandbox for you to play around also: https://codesandbox.io/s/loving-bouman-47bek?fontsize=14&hidenavigation=1&theme=dark
The code below should work fine:
import React from "react";
import ReactDOM from "react-dom";
import { Container, TextField } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
root: {
background: "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)",
border: 0,
borderRadius: 3,
boxShadow: "0 3px 5px 2px rgba(255, 105, 135, .3)",
color: "white",
height: 48,
padding: "0 30px"
}
});
function App() {
const classes = useStyles();
return (
<Container component="main" maxWidth="md">
<div className={classes.root}>
<TextField
required
id="standard-required"
label="Required"
defaultValue="Hello World"
/>
<TextField
disabled
id="standard-disabled"
label="Disabled"
defaultValue="Hello World"
/>
<TextField
id="standard-password-input"
label="Password"
type="password"
autoComplete="current-password"
/>
<TextField
id="standard-read-only-input"
label="Read Only"
defaultValue="Hello World"
InputProps={{
readOnly: true
}}
/>
<TextField
id="standard-number"
label="Number"
type="number"
InputLabelProps={{
shrink: true
}}
/>
<TextField id="standard-search" label="Search field" type="search" />
<TextField
id="standard-helperText"
label="Helper text"
defaultValue="Default Value"
helperText="Some important text"
/>
</div>
</Container>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Also I would hight suggest you to start using functional components instead of class components since the functional components are the future of React.

(login) submit with FormControl in react

iam using the latest version of meteor & react. Yesterday I switched to the newest version of bootstrap. Before that everything worked just fine now I can´t fix my (really noobish) problem.
import React, {Component} from 'react';
import { FormControl, , etc. } from 'react-bootstrap';
export default class Login extends Component {
login(event) {
event.preventDefault();
var username = this.refs.inputName.refs.input.value.trim();
var password = this.refs.inputPw.refs.input.value.trim();
Meteor.loginWithPassword(username, password, function (error) {
if (error) {
...
}
else {
FlowRouter.go('/c/' + Meteor.user().username);
}
});
this.refs.password.refs.input.value = "";
}
render() {
return (
<Row>
<Col xs={3} xsOffset={4}>
<form onSubmit={this.login.bind(this)}>
<FormGroup>
<ControlLabel>Willkommen</ControlLabel>
<FormControl
type="text"
placeholder="Benutzername"
ref="inputName"
name="username"/>
</FormGroup>
<FormGroup>
<FormControl
placeholder="Password"
ref="inputPw"
name="password"
type="password"/>
</FormGroup>
<FormGroup>
<Col smOffset={2} sm={10}>
<Button bsStyle="primary" label="Login" id="loginButton" type="submit" active>Login</Button>
</Col>
</FormGroup>
</form>
</Col>
</Row>
);
}
In my previous solution I used a simple input form. Due to deprecation I switched to this formControl stuff. Now it seems that
<form onSubmit={this.login.bind(this)}>
login never gets called. Console.log(username) returns undefined.
Thanks in advance and many greetings.
//edit 1:
It seems like Iam not the only one with this problem.
React-bootstrap Form getValue not a function
This helped me a lot to find my solution:
import ReactDom from 'react-dom';
var username = ReactDom.findDOMNode(this.refs.inputName).value;
var password = ReactDom.findDOMNode(this.refs.inputPw).value;

Deactivate input in react with a button click

I have this basic component and I want the textfield to be deactivated or activated whenever I click on a button. How can I achieve this?
This is my sample code:
import React from "react";
import Button from 'react-button'
const Typing = (props) => {
var disabled = "disabled";
var enabled = !disabled;
const handleUserInput = (event) => props.onUserInput(event.target.value);
const handleGameClik = (props) => {
disabled = enabled;
}
return(
<div>
<input
className = "typing-container"
value = {props.currentInput}
onChange = {handleUserInput}
placeholder=" ^__^ "
disabled = {disabled}/>
<Button onClick = {handleGameClik}> Start Game </Button>
<Button> Fetch Data </Button>
</div>
);
};
A simplified solution using state could look like this:
class Typing extends React.Component {
constructor(props) {
super(props);
this.state = { disabled: false }
}
handleGameClik() {
this.setState( {disabled: !this.state.disabled} )
}
render() {
return(
<div>
<input
className = "typing-container"
placeholder= " type here "
disabled = {(this.state.disabled)? "disabled" : ""}/>
<button onClick = {this.handleGameClik.bind(this)}> Start Game </button>
<button> Fetch Data </button>
</div>
);
}
};
Working Codepen here.
** 2019 **
Another option is to use, react-hooks' hook useState.
Edit: In a functional component
import React, {useState} from 'react';
function Typing(props) {
const [disabled, setDisabled] = useState(false);
function handleGameClick() {
setDisabled(!disabled);
}
return (
<div>
<input
className='typing-container'
placeholder=' type here '
disabled={disabled}
/>
<button type='submit' onClick={handleGameClick}> Start Game </button>
<button> Fetch Data </button>
</div>
);
}
This might confuse you, but the guys at React.js actually rebuild every form component and made them look almost exactly like the native HTML component. There are some differences however.
In HTML you should disable an input field like this:
<input disabled="disabled" />
But in React.js you'll have to use:
<input disabled={true} />
The accepted example works because anything not 0 is considered true. Therefor "disabled" is also interpreted as true.
const [disabled , setDisabled] = useState(true)
if(condition){
setDisabled(false)
}else{
setDisabled(true)
}
return
<TextField placeholder="Name" disabled={ disabled} />

Resources